codetether-agent 4.5.7

A2A-native AI coding agent for the CodeTether ecosystem
Documentation
use super::ExecutionProvenance;
use hmac::{Hmac, Mac};
use sha2::Sha256;

type HmacSha256 = Hmac<Sha256>;

pub fn sign_provenance(provenance: &ExecutionProvenance) -> Option<String> {
    let secret = std::env::var("CODETETHER_SIGNING_SECRET").ok()?;
    let mut mac = HmacSha256::new_from_slice(secret.as_bytes()).ok()?;
    mac.update(payload(provenance).as_bytes());
    Some(hex::encode(mac.finalize().into_bytes()))
}

fn payload(provenance: &ExecutionProvenance) -> String {
    format!(
        "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
        provenance.provenance_id,
        part(provenance.session_id.as_deref()),
        part(provenance.task_id.as_deref()),
        part(provenance.run_id.as_deref()),
        part(provenance.attempt_id.as_deref()),
        part(provenance.identity.tenant_id.as_deref()),
        part(provenance.identity.agent_identity_id.as_deref()),
        provenance.identity.agent_name,
        provenance.identity.origin.as_str(),
        part(provenance.identity.worker_id.as_deref()),
        part(provenance.identity.key_id.as_deref()),
        part(provenance.identity.github_installation_id.as_deref()),
        part(provenance.identity.github_app_id.as_deref()),
    )
}

fn part(value: Option<&str>) -> &str {
    value.unwrap_or("")
}

#[cfg(test)]
mod tests {
    use super::sign_provenance;
    use crate::provenance::{ExecutionOrigin, ExecutionProvenance};

    #[test]
    fn signs_provenance_with_secret() {
        unsafe {
            std::env::set_var("CODETETHER_SIGNING_SECRET", "test-secret");
        }
        let provenance = ExecutionProvenance::for_operation("worker", ExecutionOrigin::Worker);
        let signature = sign_provenance(&provenance);
        unsafe {
            std::env::remove_var("CODETETHER_SIGNING_SECRET");
        }
        assert!(signature.is_some());
    }
}