For: Engineers evaluating cryptographic audit guarantees

How the audit chain prevents tampering

~4 min read

SHA-256 + JCS canonical JSON + append-only — why a single byte-flip breaks the whole chain.

The SBO3L audit chain is a hash-chained log: every decision the daemon makes appends one row, and each row's hash includes the previous row's hash. A single byte-flip anywhere in history breaks the chain, and the strict-mode verifier rejects the capsule.

The chain rule

event_n.hash = sha256( event_{n-1}.hash || jcs(event_n.content) )

jcs is RFC 8785 canonical JSON — same input bytes on every implementation regardless of map ordering or whitespace. || is byte concatenation. The first event uses a fixed genesis hash (0x000...) so the chain has no bootstrap hole.

What a tamper looks like

Suppose an attacker edits event N's amount from $1000 to $10000 in the daemon's SQLite file. The hash of event N changes. Event N+1's hash was computed using the OLD event N hash, so the chain link breaks at N+1. The verifier walks the chain from the latest event backward, recomputing hashes; the first mismatch raises strict_mode_violation. Returns rc=1 (chain broken), not rc=0 (clean) or rc=2 (other capsule check failed).

Why not Merkle trees?

Append-only chains are simpler than Merkle trees for the SBO3L use case: we always read the chain forward (from a known anchor) and we never need to prove non-membership. Merkle would add log(N) proof size for a property we don't need. The on-chain anchor (separate article) is what lets us truncate the chain locally without losing audit-grade trust.

Performance

Append: one SHA-256 + one INSERT, ~3µs on commodity hardware. Verify: SHA-256 the entire chain top-down, ~1ms per 1000 events. At 1 million events the verify pass takes ~1 second; for chains larger than that, switch to incremental verification using the on-chain anchor as the trusted starting point.