vios_app 0.1.1

Small JSON vaults: Argon2id + AES-GCM, with optional AAD binding.
Documentation
use anyhow::Result;
use chrono::{Duration, Utc};
use clap::Parser;
use vios_core::vault::types::{VaultV2, ToneDriftEntry, CiaBlock, LicenseMeta};
use vios_core::vault::vault_crypto::encrypt_bytes;
use vios_core::cia::cia_signature::{cia_sign, CiaInputs};
use vios_core::whisper::whisper_loop::classify;
use vios_core::whisper::whisper_vision::congruence_score;

#[derive(Parser, Debug)]
struct Args {
    #[arg(long)] input: String,
    #[arg(long, env = "VIOS_PASSPHRASE")] passphrase: String,
    #[arg(long)] parent: Option<String>,
    #[arg(long, default_value = "0")] ttl_days: i64,
    #[arg(long, default_value_t = false)] burn_on_view: bool,
    #[arg(long, default_value_t = true)] local_only: bool,
    #[arg(long, default_value_t = true)] reflex: bool,
    #[arg(long)] face_hint: Option<String>,
}

fn main() -> Result<()> {
    let a = Args::parse();
    let plaintext = std::fs::read(a.input.clone())?;

    // Whisper Loop (local-only MVP)
    let hint_text = String::from_utf8_lossy(&plaintext).to_string();
    let w = classify(&hint_text, true, a.reflex);
    let congruence = congruence_score(&w.tone, a.face_hint.as_deref());

    // CIA++
    let cia = cia_sign(CiaInputs { plaintext_bytes: plaintext.clone(), multimodal: vec!["voice".into()] });

    // Encrypt
    let (payload_b64, nonce_b64, salt_b64) = encrypt_bytes(&plaintext, &a.passphrase)?;

    // Build Vault v2.0
    let now = Utc::now();
    let expires_at = if a.ttl_days > 0 { Some(now + Duration::days(a.ttl_days)) } else { None };
    let mut lineage = vec![];
    if let Some(p) = &a.parent { lineage.push(p.clone()); }

    let mut disclaimers = vec![];
    if let Some(d) = &w.disclaimer { disclaimers.push(d.clone()); }
    disclaimers.push("Perception, not absolute truth. User override allowed.".into());

    let vault = VaultV2 {
        version: "2.0".into(),
        created_at: now,
        modified_at: now,
        expires_at,
        self_destruct_on_view: a.burn_on_view,
        parent_hash: a.parent.clone(),
        lineage,
        drift_log: vec![ToneDriftEntry{ at: now, tone: w.tone.clone(), reem: w.reem.clone(), note: Some("initial".into())}],
        payload_b64,
        nonce_b64,
        salt_b64,
        reem_code: Some(w.reem),
        tone_label: Some("calm".into()),
        congruence: Some(congruence),
        cia: CiaBlock {
            content_hash_hex: cia.content_hash_hex,
            device_entropy_hex: cia.device_entropy_hex,
            username: cia.username,
            hostname: cia.hostname,
            multimodal_flags: vec!["voice".into()],
            clone_suspect: cia.clone_suspect,
        },
        license: Some(LicenseMeta{ royalty_bps: 500, ttl_days: 365, memo_hint: "VIOS_VAULT_V2_DEMO".into() }),
        local_only: a.local_only,
        disclaimers,
    };

    std::fs::create_dir_all("vaults")?;
    let out_path = format!("vaults/{}.vault.json", now.to_rfc3339().replace(':', "-"));
    std::fs::write(&out_path, serde_json::to_vec_pretty(&vault)?)?;
    println!("Vault created: {out_path}");
    Ok(())
}