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
2026-03-01 03:03:29 +00:00
|
|
|
use clap::{Parser, Subcommand};
|
|
|
|
|
use tracing::info;
|
|
|
|
|
|
2026-03-01 03:13:26 +00:00
|
|
|
pub mod parser;
|
|
|
|
|
pub mod classifier;
|
|
|
|
|
pub mod executor;
|
|
|
|
|
pub mod audit;
|
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
2026-03-01 03:03:29 +00:00
|
|
|
|
|
|
|
|
#[derive(Parser)]
|
|
|
|
|
#[command(name = "dd0c-run", version, about = "Runbook automation agent")]
|
|
|
|
|
struct Cli {
|
|
|
|
|
#[command(subcommand)]
|
|
|
|
|
command: Commands,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Subcommand)]
|
|
|
|
|
enum Commands {
|
|
|
|
|
/// Execute a runbook
|
|
|
|
|
Run {
|
|
|
|
|
/// Path to runbook file (YAML/Markdown)
|
|
|
|
|
#[arg(short, long)]
|
|
|
|
|
runbook: String,
|
|
|
|
|
|
|
|
|
|
/// dd0c SaaS endpoint
|
|
|
|
|
#[arg(long, default_value = "https://api.dd0c.dev")]
|
|
|
|
|
endpoint: String,
|
|
|
|
|
|
|
|
|
|
/// API key
|
|
|
|
|
#[arg(long, env = "DD0C_API_KEY")]
|
|
|
|
|
api_key: String,
|
|
|
|
|
|
|
|
|
|
/// Dry run (classify only, don't execute)
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
dry_run: bool,
|
|
|
|
|
},
|
|
|
|
|
/// Classify a single command
|
|
|
|
|
Classify {
|
|
|
|
|
/// Command to classify
|
|
|
|
|
command: String,
|
|
|
|
|
},
|
|
|
|
|
/// Verify agent binary signature
|
|
|
|
|
Verify {
|
|
|
|
|
/// Path to signature file
|
|
|
|
|
#[arg(short, long)]
|
|
|
|
|
sig: String,
|
|
|
|
|
/// Path to public key
|
|
|
|
|
#[arg(short, long)]
|
|
|
|
|
pubkey: String,
|
|
|
|
|
},
|
|
|
|
|
/// Print version
|
|
|
|
|
Version,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tokio::main]
|
|
|
|
|
async fn main() -> anyhow::Result<()> {
|
|
|
|
|
tracing_subscriber::fmt()
|
|
|
|
|
.with_env_filter(
|
|
|
|
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
|
|
|
|
.unwrap_or_else(|_| "dd0c_run=info".into()),
|
|
|
|
|
)
|
|
|
|
|
.json()
|
|
|
|
|
.init();
|
|
|
|
|
|
|
|
|
|
let cli = Cli::parse();
|
|
|
|
|
|
|
|
|
|
match cli.command {
|
|
|
|
|
Commands::Run { runbook, endpoint, api_key, dry_run } => {
|
|
|
|
|
info!(runbook = %runbook, dry_run, "Starting runbook execution");
|
|
|
|
|
// TODO: Parse runbook → classify steps → execute with approval gates
|
|
|
|
|
}
|
|
|
|
|
Commands::Classify { command } => {
|
|
|
|
|
let result = classifier::classify(&command);
|
|
|
|
|
println!("{}", serde_json::to_string_pretty(&result)?);
|
|
|
|
|
}
|
|
|
|
|
Commands::Verify { sig, pubkey } => {
|
|
|
|
|
// TODO: Ed25519 signature verification
|
|
|
|
|
println!("Signature verification not yet implemented");
|
|
|
|
|
}
|
|
|
|
|
Commands::Version => {
|
|
|
|
|
println!("dd0c/run agent v{}", env!("CARGO_PKG_VERSION"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|