ADR 0005: Hash-chained custody ledger, persisted append-only in SQLite¶
- Status: accepted (v0.2)
- Date: 2026-09-26
Context¶
The spec asks for an append-only custody store (PostgreSQL) and for hashes to be verified on load. REVENANT runs on a single examiner workstation, often offline, and a database server is a heavy dependency for that setting.
Decision¶
- Integrity lives in the data itself. Each
CustodyRecordcommits to the previous record's hash (a hash chain), and every event carries a SHA-256 over its canonical form.CustodyLedger.verify()recomputes the chain. custody_store.pypersists the ledger to SQLite withBEFORE UPDATEandBEFORE DELETEtriggers that abort. A second analysis may only append to a store whose history is a prefix of its own. A diverging history is refused.- Raw artefacts are hashed before parsing (
acquirerecords). Files the OS refuses to open are recorded asacquire_failed, never silently skipped.
Consequences¶
- An edit made through SQL is blocked. An edit made around SQL (dropping the
trigger, hex-editing the file) is detected by
verify_storebecause the hash chain breaks. This is tested. - The schema ports to PostgreSQL unchanged apart from trigger syntax. Doing so would add multi-user access, not stronger integrity.
- The ledger shows tampering but cannot prevent someone replacing the whole file. Anchoring the head hash externally (printed in every report as "Ledger head") is the mitigation.