Scaffold dd0c/run: Rust agent (classifier, executor, audit) + TypeScript SaaS
- Rust agent: clap CLI, command classifier (read-only/modifying/destructive), executor with approval gates, audit log entries - Classifier: pattern-based safety classification for shell, AWS, kubectl, terraform/tofu commands - 6 Rust tests: read-only, destructive, modifying, empty, terraform apply, tofu destroy - SaaS backend: Fastify server, runbook CRUD API, approval API, Slack interactive handler - Slack integration: signature verification, block_actions for approve/reject buttons - PostgreSQL schema with RLS: runbooks, executions, audit_entries (append-only), agents - Dual Dockerfiles: Rust multi-stage (agent), Node multi-stage (SaaS) - Gitea Actions CI: Rust test+clippy, Node typecheck+test - Fly.io config for SaaS
This commit is contained in:
58
products/06-runbook-automation/agent/src/audit.rs
Normal file
58
products/06-runbook-automation/agent/src/audit.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use chrono::{DateTime, Utc};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Immutable, append-only audit log entry.
|
||||
/// Every command execution gets logged — no exceptions (BMad must-have).
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AuditEntry {
|
||||
pub id: String,
|
||||
pub tenant_id: String,
|
||||
pub runbook_id: String,
|
||||
pub step_index: usize,
|
||||
pub command: String,
|
||||
pub safety_level: String,
|
||||
pub approved_by: Option<String>,
|
||||
pub approval_method: Option<String>, // "slack_button", "api", "auto" (read-only only)
|
||||
pub exit_code: Option<i32>,
|
||||
pub stdout_hash: Option<String>, // SHA-256 of stdout (don't store raw output)
|
||||
pub stderr_hash: Option<String>,
|
||||
pub started_at: DateTime<Utc>,
|
||||
pub completed_at: Option<DateTime<Utc>>,
|
||||
pub duration_ms: Option<u64>,
|
||||
pub status: AuditStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum AuditStatus {
|
||||
Pending,
|
||||
AwaitingApproval,
|
||||
Approved,
|
||||
Rejected,
|
||||
Executing,
|
||||
Completed,
|
||||
Failed,
|
||||
TimedOut,
|
||||
}
|
||||
|
||||
impl AuditEntry {
|
||||
pub fn new(tenant_id: &str, runbook_id: &str, step_index: usize, command: &str, safety_level: &str) -> Self {
|
||||
Self {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
tenant_id: tenant_id.to_string(),
|
||||
runbook_id: runbook_id.to_string(),
|
||||
step_index,
|
||||
command: command.to_string(),
|
||||
safety_level: safety_level.to_string(),
|
||||
approved_by: None,
|
||||
approval_method: None,
|
||||
exit_code: None,
|
||||
stdout_hash: None,
|
||||
stderr_hash: None,
|
||||
started_at: Utc::now(),
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
status: AuditStatus::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user