vios_app 0.1.1

Small JSON vaults: Argon2id + AES-GCM, with optional AAD binding.
Documentation
use chrono::Utc;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::{fs, path::Path, process::Command};

// CIA++ signature struct
#[derive(Serialize, Deserialize)]
struct AgentSignature {
    agent_name: String,
    role: String,
    intent: String,
    reem_code: String,
    timestamp: String,
    cia_hash: String,
    exec_status: String,
    notes: String,
}

fn run_agent_script(script_path: &str, fallback_msg: &str) -> (String, String) {
    if Path::new(script_path).exists() {
        match Command::new("bash").arg(script_path).output() {
            Ok(out) => {
                let stdout = String::from_utf8_lossy(&out.stdout).to_string();
                let stderr = String::from_utf8_lossy(&out.stderr).to_string();
                (stdout, stderr)
            }
            Err(e) => (String::new(), format!("exec error: {e}")),
        }
    } else {
        (fallback_msg.to_string(), String::new())
    }
}

fn read_intent_for(agent_name: &str) -> String {
    let p = format!("agents/{}.intent", agent_name.to_lowercase());
    fs::read_to_string(&p)
        .map(|s| s.trim().to_string())
        .unwrap_or_else(|_| "upgrade".to_string())
}

fn main() -> std::io::Result<()> {
    // SINGLE TS FOR THE WHOLE RUN
    let run_ts = Utc::now().timestamp();

    let agents: Vec<(&str, &str, &str, &str)> = vec![
        (
            "Naorix",
            "Core Architect",
            "agents/naorix.sh",
            "Naorix: heartbeat",
        ),
        (
            "EchoLoom",
            "DevOps + Optimizer",
            "agents/echoloom.sh",
            "EchoLoom: heartbeat",
        ),
        (
            "SealMind",
            "Compliance + Hash Audit",
            "agents/sealmind.sh",
            "SealMind: heartbeat",
        ),
        (
            "WhisperGen",
            "Tone Classifier Maintainer",
            "agents/whispergen.sh",
            "WhisperGen: heartbeat",
        ),
        (
            "VaultCrafter",
            "Metadata Automator",
            "agents/vaultcrafter.sh",
            "VaultCrafter: heartbeat",
        ),
        (
            "REEMSmith",
            "Emotional Logic Synthesizer",
            "agents/reemsmith.sh",
            "REEMSmith: heartbeat",
        ),
        (
            "Scribe-001",
            "Canonical Doc Generator",
            "agents/scribe-001.sh",
            "Scribe-001: heartbeat",
        ),
        (
            "Overseer-7",
            "Commit Watchtower",
            "agents/overseer-7.sh",
            "Overseer-7: heartbeat",
        ),
    ];

    fs::create_dir_all("vault_dev_log")?;

    for (name, role, script, fallback) in agents {
        println!("[SPAWN] {name} ({role}) ts={run_ts}");
        let (stdout, stderr) = run_agent_script(script, fallback);

        // CIA hash over combined output
        let mut hasher = Sha256::new();
        hasher.update(stdout.as_bytes());
        hasher.update(stderr.as_bytes());
        let cia_hash = format!("{:x}", hasher.finalize());

        let exec_status = if Path::new(script).exists() {
            if stderr.trim().is_empty() {
                "OK"
            } else {
                "OK_WITH_WARNINGS"
            }
        } else {
            "FALLBACK"
        }
        .to_string();

        let intent = read_intent_for(name);

        let sig = AgentSignature {
            agent_name: name.to_string(),
            role: role.to_string(),
            intent,
            reem_code: "REEM-X99".to_string(),
            timestamp: Utc::now().to_rfc3339(),
            cia_hash,
            exec_status,
            notes: if Path::new(script).exists() {
                format!("stdout_len={}, stderr_len={}", stdout.len(), stderr.len())
            } else {
                "script missing → fallback heartbeat logged".to_string()
            },
        };

        let json_log = format!("vault_dev_log/{name}_{run_ts}.json");
        fs::write(&json_log, serde_json::to_string_pretty(&sig).unwrap())?;
        println!("[LOGGED] {name} -> {json_log}");

        // raw output capture for debugging
        let out_dir = format!("vault_dev_log/{name}/{run_ts}");
        fs::create_dir_all(&out_dir)?;
        fs::write(format!("{}/stdout.log", &out_dir), stdout)?;
        fs::write(format!("{}/stderr.log", &out_dir), stderr)?;
    }

    // Advertise the run timestamp for hooks / UIs
    fs::write("vault_dev_log/RUN_TS", format!("{run_ts}\n"))?;
    Ok(())
}