Chain integrity
Every mutation in Sukrit Nidhi writes a row to the audit log. Those rows form a hash chain: each row records the hash of the previous row, so an attacker who modifies one row in the middle must also re-hash every row after it. Changing a row at the database level without re-hashing is detectable by anyone with a read copy of the chain.
How the hash is computed
For each event we compute:
record_hash = SHA-256( prev_hash || canonical_json(event) )
where canonical_json serialises the event fields
deterministically (sorted keys, compact separators, UTF-8). The first event
for a tenant uses a fixed genesis string as its prev_hash.
Verification workflow
Verification is used in three places: on the Centre dashboard (live badge),
in the daily scheduled task (audit.verify_all_chains),
and in the offline verifier CLI shipped in the audit bundle.
What “red” means
If the chain is reported as failed, the verifier tells you exactly which event diverges, what the expected hash is, and what the stored hash is. Two kinds of problems surface:
- Hash mismatch on a row — the row’s fields don’t match its stored record hash. Someone changed the row after it was written.
- Broken
prev_hash— a row’sprev_hashdoesn’t equal the previous row’s record hash. Rows were inserted out of order, or one was deleted.
Chain anchors
The scheduled task
audit.anchor_chain_heads writes a daily
anchor per active tenant:
- Timestamp, event count, chain head at the moment of the snapshot.
- Ed25519 signature over the canonical anchor body.
- Stored in the append-only
chain_anchortable.
Anchors give you a second, independently-signed timeline against which any later replay can be cross-checked. If an attacker later manipulates an event dated before an anchor, the replay won’t reproduce that anchor’s stored head — the tamper window is bounded to the gap between anchors.
ChainAnchor.save() prevents overwrite,
so the model is a single source of truth.
If you get a real red-light event
Investigate, don’t close the case by unlocking things:
- Snapshot everything first. Take a full database dump right now, before any investigation. Chain problems disappear if someone panics and fixes rows.
- Read the problem report. The verifier lists the first five divergent rows with both hashes. Usually the diff points at a single entry; check when it was written and by whom in the audit log.
- Compare with the previous anchor. If the divergent event is dated before the most recent anchor, and the anchor’s head doesn’t replay, you have a tamper event between the anchor and now.
- Escalate. Chain divergence is a security incident, not an operational one. Your audit policy should mandate a security-officer review.