> ## Documentation Index
> Fetch the complete documentation index at: https://ctrlrun.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# The receipt chain

> Each receipt carries the hash of the one before it, so an edit, a deletion from the middle or a reordering is detected and named by seq.

The receipt chain is a hash chain over the evidence log: receipt *n* carries `prev_hash`, the
hash of receipt *n − 1*, with `seq` inside the hashed content. Change what one receipt says,
delete one from the middle, or swap two, and the chain no longer verifies at that point.
`ctrlrun receipts --verify-chain` reports the break by `seq` and by name.

## What it detects

```console theme={null}
$ ctrlrun receipts --verify-chain
chain: 41 receipts
seq 17: content altered
```

Six named breaks: `content_altered` where a receipt no longer hashes to its stored hash,
`hash_missing` where the stored hash is gone and nothing can be compared, `link_broken` between
two, `missing` where there is a gap, `head_mismatch` where the head does not match the last
receipt, and `unchained` for a receipt written before the chain existed. Each names the `seq`,
so a reader can go and look at that row rather than being told the log is bad, and
[Receipt integrity](/production/receipt-integrity) says what to do about each.

## What it does not prove

**Alteration is not authorship.** Receipts are not signed. The chain says the log was not
edited after the fact; it says nothing about who wrote any receipt, and nothing here is
evidence of origin. Signing is not in this version.

**It is not tamper-proof.** The head lives in the same database as the receipts, so an
administrator who can rewrite every row can recompute the chain and it will verify. What the
chain raises is the cost of *changing* history while keeping it: rewriting one receipt means
rewriting every receipt after it and the head.

**It does not detect erasing the end.** Deleting the last receipts and rewinding the head is
two statements and leaves a chain that verifies. An anchor outside the database would close
that, and there is none. This is asserted by a test rather than argued, so the sentence cannot
quietly become optimistic.

**It does not prove every action wrote a receipt.** A receipt whose write failed leaves no gap
in `seq` and is invisible to the chain by construction. The events log is where that is
reconciled.

## Where it is checked

* `ctrlrun receipts --verify-chain`, on either store backend, including through `--store-url`.
* `ctrlrun verify`'s G11, *an altered receipt is detected* — against a scratch store verify
  creates on your backend, never against the store you run on.
* On Postgres, `put_receipt` takes the head row's lock first, so concurrent writers cannot
  interleave into a chain that then fails to verify.

## What to do with it

Treat a break as an incident about the store rather than about CTRLRun: the chain is the alarm,
not the lock. If you need evidence of origin rather than of integrity, sign or anchor the
receipts outside the database yourself; they are portable JSON, one object per line, for exactly
that reason.

[Receipt integrity in practice](/production/receipt-integrity) is the runbook: when to run the
check, what each of the six names means for your store, and what an unchained count does to a
scheduled job.

## Next

* [Receipts and evidence](/concepts/receipts-and-evidence): what a receipt holds.
* [Threat model](/THREAT_MODEL): what remains open, in full.
* [Get started](/get-started/quickstart) · [Why](/why).


## Related topics

- [Receipts and evidence](/concepts/receipts-and-evidence.md)
- [Receipt integrity in practice](/production/receipt-integrity.md)
- [Receipt and event schemas](/reference/receipt-and-event-schemas.md)
- [Frequently asked questions](/faq.md)
- [Use the docs from your editor](/mcp/use-the-docs-from-your-editor.md)
