Removes the deferral. Each sampled execution's FULL trace (every graph edge, count, weight) and delta are persisted as evidence; the independent attestor reconstructs them, recomputes the canonical trace hash and delta hash, re-derives the replay-record leaf, and confirms it is among the retained Merkle leaves the root is built from. The behavior-fingerprint hash is re-derived from features (not trusted), and f64 divergence is stored bit-exact for an identical hash. - trace_model: ExecutionTrace::serialize/deserialize (round-trips canonical_hash; total on garbage). Test: full_trace_serialize_roundtrips_canonical_hash. - world_model: WorldDelta::serialize/deserialize (round-trips hash). - ci_reports: retains TRACE_EVIDENCE_SAMPLE full traces; writes evidence/traces.tsv. - attestation: depends on trace_model/world_model; reconstructs each trace, recomputes the leaf, requires it to match the claimed leaf AND be a retained leaf. Negative control: tampered_trace_breaks_attestation (corrupting the full trace, leaving the claimed leaf, fails attestation). - merge-gates: requires evidence/traces.tsv; the separate attest step verifies it. End-to-end (fast profile): 256/256 full traces reconstructed and re-derived to retained leaves; recomputed root matches the claim over 6600 leaves. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
41 lines
1.6 KiB
Rust
41 lines
1.6 KiB
Rust
//! `attest` — independently verify a CI run's evidence directory.
|
|
//!
|
|
//! Usage: `attest <dir>` (default `ci_out`). Exits non-zero if the recomputed
|
|
//! Merkle root does not match the claimed root, the leaf count is inconsistent,
|
|
//! or the leaves are missing. It deliberately ignores `compliance_report.json`.
|
|
|
|
use std::path::PathBuf;
|
|
|
|
fn main() {
|
|
let dir = std::env::args().nth(1).unwrap_or_else(|| "ci_out".to_string());
|
|
let path = PathBuf::from(&dir);
|
|
match attestation::verify_dir(&path) {
|
|
Ok(att) => {
|
|
eprintln!("=== independent attestation of {dir} ===");
|
|
eprintln!(" leaves: {}", att.leaf_count);
|
|
eprintln!(" recomputed root: {:016x}", att.recomputed_root);
|
|
match att.claimed_root {
|
|
Some(r) => eprintln!(" claimed root: {:016x}", r),
|
|
None => eprintln!(" claimed root: <missing>"),
|
|
}
|
|
eprintln!(
|
|
" full traces verified: {}/{} (reconstructed + leaf re-derived)",
|
|
att.traces_verified, att.traces_total
|
|
);
|
|
for (name, ok) in &att.checks {
|
|
eprintln!(" [{}] {}", if *ok { "PASS" } else { "FAIL" }, name);
|
|
}
|
|
if att.ok {
|
|
eprintln!("ATTESTATION: PASS — leaves recompute to the claimed root.");
|
|
} else {
|
|
eprintln!("ATTESTATION: FAIL — evidence is inconsistent.");
|
|
std::process::exit(1);
|
|
}
|
|
}
|
|
Err(e) => {
|
|
eprintln!("ATTESTATION: ERROR — {e}");
|
|
std::process::exit(2);
|
|
}
|
|
}
|
|
}
|