Skip to content

Add Tier-1 tamper-evident evidence ledger with HMAC chain (#2)#32

Merged
SSBrouhard merged 1 commit into
mainfrom
feat/2-evidence-integrity
Jun 11, 2026
Merged

Add Tier-1 tamper-evident evidence ledger with HMAC chain (#2)#32
SSBrouhard merged 1 commit into
mainfrom
feat/2-evidence-integrity

Conversation

@SSBrouhard

Copy link
Copy Markdown
Owner

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:

  • Prefix-truncated ledger → verified_tampered (genesis_mismatch) ✅
  • Deleted rotated segment → verified_tampered (genesis/sequence discontinuity) ✅
  • Legacy no-metadata ledger → cannot_verify (missing_integrity_metadata), never upgraded ✅
  • Missing key / missing expected head → 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:

  • One shared canonicalization for writer and verifier; verifier recomputes from parsed JSON; MAC commits to key_id, sequence, prev_mac, canonical record; key_id newline-rejected (delimiter framing) ✅
  • MAC'd bytes == persisted bytes (append_line writes the exact canonical line); redaction occurs before MAC ✅
  • Write order under _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_verify never renders as success: vmga-verify-evidence reports integrity as a separate dimension from event_sequence, and top-level valid requires verified_intact
  • Cross-file verification (option 1 from the design) documented; pruned-history residual stated ✅

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.pyVMGA release check: PASS
  • python -m pytest -q118 passed
  • python -m compileall src tests scripts integrations → pass
  • integrations/openclaw: npm test → pass; npm run plugin:validatePlugin plugin.vmga is valid.
  • git diff --check → clean

Implemented 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

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>
@SSBrouhard SSBrouhard force-pushed the feat/2-evidence-integrity branch from b5dcda4 to e5e3847 Compare June 11, 2026 01:02
@SSBrouhard SSBrouhard merged commit 4a6c42d into main Jun 11, 2026
5 checks passed
@SSBrouhard SSBrouhard deleted the feat/2-evidence-integrity branch June 11, 2026 01:04
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add tamper-evident evidence ledger and verification command

1 participant