Add Tier-1 tamper-evident evidence ledger with HMAC chain (#2)#32
Merged
Conversation
Implements docs/evidence_integrity_design.md: - Opt-in keyed HMAC chain (VMGA_EVIDENCE_HMAC_KEY/_KEY_ID) over JSONL evidence; without the key, append-only advisory behavior is unchanged - Per-record integrity metadata (vmga-hmac-chain-v1, sequence, key_id, prev_mac, mac); shared writer/verifier canonicalization; MAC'd bytes == persisted bytes via exact canonical-line append; redaction happens upstream of MAC - Expected-head checkpoint (genesis + head anchors) persisted atomically with fsync in JSON and SQLite state stores, written under _state_lock; checkpoint persist failure for kinetic actions fails closed like a ledger write failure - Three verification states: verified_intact / verified_tampered / cannot_verify; cannot_verify never renders as success; legacy no-metadata ledgers stay cannot_verify; empty ledger with an expected head is verified_tampered (provable truncation) - Cross-file verification over the ordered retained segment set with cross-seam continuity; deleted rotated segments and prefix truncation surface as verified_tampered; pruned-history residual documented - Per-record key_id rotation with historical verify keys; unknown key_id is cannot_verify, never intact - Startup recovery: exactly-one-ahead crash-after-append advances the head after verifying MAC and chain link; larger divergence left for operator inspection - vmga-verify-evidence reports integrity as a separate dimension from sequence checks (--checkpoint/--state-db/--hmac-key) - Tier-1 compromised-broker residual named in docs Implemented with Codex (gpt-5.5); empty-ledger-with-checkpoint classification fixed and full validation run in review. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
b5dcda4 to
e5e3847
Compare
SSBrouhard
added a commit
that referenced
this pull request
Jun 11, 2026
…ase check (#33) The #32 rebase left unresolved conflict markers in CHANGELOG.md, README.md, and docs/deployment_runbook.md that were committed and merged to main. Resolved each as the union of both sides, dropping each branch's stale "remains a design gate" cross-reference now that both #24 and #2 have shipped. vmga_release_check.py now scans all repo text files for conflict markers and fails on any hit, so this class of error cannot pass the release gate again. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements issue #2 per the design of record in
docs/evidence_integrity_design.md. Closes #2. Independent of #31 (based on main; disjoint surface).What
Opt-in Tier-1 keyed HMAC chain over the JSONL evidence ledger (
VMGA_EVIDENCE_HMAC_KEY+VMGA_EVIDENCE_HMAC_KEY_ID), dual-end anchoring via an expected-head checkpoint (genesis + head), three-state verification, cross-file rotation handling, and crash-after-append startup recovery. Without the key configured, existing append-only advisory behavior is unchanged.Acceptance gates exercised (design doc §Acceptance Gates)
The four reviewer-trap cases:
verified_tampered(genesis_mismatch) ✅verified_tampered(genesis/sequence discontinuity) ✅cannot_verify(missing_integrity_metadata), never upgraded ✅cannot_verify, fail closed ✅Full matrix (tests/test_evidence_integrity.py): mutation, insertion, middle deletion, reorder, tail truncation (
head_mismatch), forged genesis, cross-seam continuity across rotation, unknown key, key rotation with historical verify keys (removed old key →cannot_verify), crash-after-append (exactly one-ahead recovers; two-ahead does not), empty-ledger-with-checkpoint →verified_tampered(provable truncation, anchor present).Other gates:
key_id,sequence,prev_mac, canonical record;key_idnewline-rejected (delimiter framing) ✅append_linewrites the exact canonical line); redaction occurs before MAC ✅_state_lock: head read → seq/prev_mac assign → canonicalize → MAC → ledger append+fsync → checkpoint atomic persist+fsync; checkpoint persist failure for kinetic actions fails closed like a ledger write failure ✅cannot_verifynever renders as success:vmga-verify-evidencereportsintegrityas a separate dimension fromevent_sequence, and top-levelvalidrequiresverified_intact✅Residuals (named)
Tier 1 does not detect a compromised broker host or any actor who can both rewrite evidence and read the HMAC key or forge the expected-head checkpoint. That residual belongs to Tier 2/3 (out-of-domain anchor — the #24/#31 operator key is a candidate checkpoint signer, domain-separated). History rotated out of the retained backup set is outside Tier-1 detection scope. Deployments without the key configured remain advisory/unanchored.
Verification (local)
python scripts/vmga_release_check.py→VMGA release check: PASSpython -m pytest -q→118 passedpython -m compileall src tests scripts integrations→ passintegrations/openclaw:npm test→ pass;npm run plugin:validate→Plugin plugin.vmga is valid.git diff --check→ cleanImplemented with Codex (gpt-5.5, low effort); maintainer review added the empty-ledger-with-expected-head tampered classification and its test.
🤖 Generated with Claude Code