← Blog

What a hash-chained audit trail actually proves

Runback Team··compliance, engineering

An auditor doesn't ask if your agent made the right call. They ask if you can prove what it actually did — and “trust the vendor” is not evidence. Most agent logs are a database table someone with write access could edit an hour before the review. Ours is built so that isn't true, and so you don't have to take our word for it either.

1 byte
changed in any past entry — every hash chained after it breaks, provably
Each entry's hash is computed over the previous entry's hash plus its own dataentry 1hash = h₁entry 2hash = h₂entry 3hash = h₃h₂ = SHA256(h₁ + entry2)h₃ = SHA256(h₂ + entry3)signed checkpointchange entry 1 → h₁ changes → h₂, h₃ all break
Each entry's hash folds in the previous entry's hash. Alter anything upstream and every hash after it stops matching — there's no way to edit one entry in isolation.

What this actually proves

Nothing deleted. The ledger is append-only, enforced by an advisory transaction lock per organization. Remove any entry and the chain breaks at exactly that point — a gap in the sequence is detectable, not concealable.

Nothing altered. Change any field in any past entry — a decision, a tool output, a timestamp — and its hash no longer matches what the next entry was sealed against. The failure cascades forward: every downstream hash is invalidated, not just the one you touched.

Dual-chain seal. The checkpoint signature covers both the event-chain digest and the cassette digest together — possession of a valid, signed checkpoint means the full runtime, including every nondeterministic input the agent touched, is accounted for, not just the decision log.

Fail-closed, not fail-open. When a signing key is configured, a missing or invalid checkpoint signature fails verification — database write access alone can't forge a valid checkpoint.

The algorithm, in full

No part of this is proprietary magic. This is the exact algorithm — the same one implemented in the open-source @runback/verify CLI, so anyone can check our work without trusting our servers:

runback.cassette/v1 — algorithm
CASSETTE CHAIN
  entry_hash[0] = SHA256("" + canonical({kind, key, output}))
  entry_hash[n] = SHA256(entry_hash[n-1] + canonical({kind, key, output}))
  algorithm: oracle-chain/sha256

LEDGER CHAIN
  leaf[run]     = SHA256("leaf:" + canonical({run_id, name, status, digest, ended_at, steps}))
  entry[n]      = SHA256(entry[n-1] + leaf[n])
  node(a, b)    = SHA256("node:" + a + b)
  merkle_root   = balanced binary tree over leaves

CHECKPOINT SIGNATURE
  payload       = "ledger:{orgId}:{seq}:{head_hash}:{merkle_root}"
  signature     = HMAC-SHA256(AUDIT_SIGNING_KEY, payload)
  fail-closed   = key configured → signature required to pass verification

Domain-separated Merkle tree. Leaf nodes are hashed with a "leaf:" prefix, internal nodes with "node:" — so a leaf value can never be confused with a node value at any depth, closing off a class of second-preimage attack.

So what?

This is what EU AI Act Article 12 logging and APRA CPS 230 incident management look like in practice — not a screenshot of a dashboard, but a record whose integrity a third party can verify independently, without asking your engineering team to vouch for it. When the question is “how do we know this wasn't edited,” the answer is a chain anyone can re-derive, not a promise.

Paste a real export and watch the chain verify yourself, no account needed, at runback.dev/verify.