dotlock-bin 0.1.4

Encrypted project-local environment variables manager
pub mod log;
pub mod rotate;
pub mod verify;

use serde_json::json;

use crate::domain::model::DotLockResult;

pub use log::{audit_log_path, show_entries};
pub use rotate::rotate_current_log;
pub use verify::verify_log;

pub fn record_unlock(method: &str, access_mode: &str) -> DotLockResult<()> {
    log::append_entry(
        "unlock",
        json!({
            "method": method,
            "access_mode": access_mode,
        }),
    )
}

pub fn record_run(command: &[String], secrets_consumed: &[String]) -> DotLockResult<()> {
    log::append_entry(
        "run",
        json!({
            "cmd": sanitize_command(command),
            "secrets_consumed": secrets_consumed,
        }),
    )
}

pub fn record_ratchet(
    old_kek_version: u32,
    new_kek_version: u32,
    secrets_rewrapped: usize,
    recipients_rewrapped: usize,
) -> DotLockResult<()> {
    log::append_entry(
        "ratchet",
        json!({
            "old_kek_version": old_kek_version,
            "new_kek_version": new_kek_version,
            "secrets_rewrapped": secrets_rewrapped,
            "recipients_rewrapped": recipients_rewrapped,
        }),
    )
}

pub fn record_dynamic_resolve(
    provider: &str,
    secret_name: &str,
    duration_ms: u128,
    success: bool,
) -> DotLockResult<()> {
    log::append_entry(
        "dynamic_resolve",
        json!({
            "provider": provider,
            "secret_name": secret_name,
            "duration_ms": duration_ms,
            "success": success,
        }),
    )
}

fn sanitize_command(command: &[String]) -> Vec<String> {
    command
        .iter()
        .map(|arg| {
            if let Some((key, _)) = arg.split_once('=') {
                let lowered = key.to_ascii_lowercase();
                if ["token", "secret", "password", "passwd", "key"]
                    .iter()
                    .any(|needle| lowered.contains(needle))
                {
                    return format!("{key}=<redacted>");
                }
            }
            arg.clone()
        })
        .collect()
}