Decision receipts

A receipt for each governed decision.

When an agent asks CAIRN to run a tool, the gate decides — allow or deny — and writes one record saying what was asked, which policy decided it, and why. Each record is chained to the one before it. That record is a receipt.

Below are two real ones, exported from a real vault, and a page that checks them on your machine with no CAIRN code. Beside every check is the thing it cannot establish. A receipt that claimed more than a hash chain can prove would be the problem this product exists to stop.

Two receipts · bundle exported 2026-09-06T16:30:42.318Z

One allowed. One refused.

Every value below is copied from evidence/ledger.jsonl — field names included — and a build check fails if any of them drifts from the file. Nothing has been added to make a receipt look complete.

Allowed record 1 of 25
ALLOW execute_commandEXECUTED

reasoning Capability 'execute' held; 5 of 5 policy check(s) passed; static risk MEDIUM.

id
dec-375df0dcf09afa56
timestamp
2026-09-06T16:30:38.376Z
actor
demo-operator
payload.actorSubject.idBasis
asserted
payload.agentChain
null
payload.toolName
execute_command
payload.capability
execute
payload.argKeys
["command"]
payload.risk
MEDIUM
payload.findings
[]
payload.policyContext.engine
COMPASS_V2
payload.policyContext.builtinDigest
90032877e4243979b8f93c760ea24201cbf97578114e7da8c4ebb2b567fff6e5
payload.policyContext.builtinRulesEvaluated
["pol-sec-01","pol-sec-02","pol-sec-03","pol-ops-01","pol-ops-02"]
payload.policyContext.builtinRulesPassed
["pol-sec-01","pol-sec-02","pol-sec-03","pol-ops-01","pol-ops-02"]
evaluated − passed derived
[]
payload.policyContext.consequence.assessed
false
payload.policyContext.autonomy.enforced
false
payload.cairnBuild.version
2.10.1
payload.cairnBuild.gitSha
b39cbb1
prevHash
0000000000000000000000000000000000000000000000000000000000000000
hash
4dde39810befdc876c168bbbc72c807d455ec13c71755fd8052d4dd7d37ee8b4
Not checked yet — the checks run further down this page.
Refused record 13 of 25
DENY execute_commandREJECTED

reasoning Policy Breach [SECURITY | No High-Risk Payload on the Host] | Static risk CRITICAL forbids direct host execution of 'execute_command' (FS_RECURSIVE_DELETE_PS). It must be repaired, or run only in an isolated sandbox.

id
dec-281cb8c57d526148
timestamp
2026-09-06T16:30:42.279Z
actor
demo-operator
payload.actorSubject.idBasis
asserted
payload.agentChain
null
payload.toolName
execute_command
payload.capability
execute
payload.argKeys
["command"]
payload.risk
CRITICAL
payload.findings
["FS_RECURSIVE_DELETE_PS"]
payload.policyContext.engine
COMPASS_V2
payload.policyContext.builtinDigest
90032877e4243979b8f93c760ea24201cbf97578114e7da8c4ebb2b567fff6e5
payload.policyContext.builtinRulesEvaluated
["pol-sec-01","pol-sec-02","pol-sec-03","pol-ops-01","pol-ops-02"]
payload.policyContext.builtinRulesPassed
["pol-sec-02","pol-sec-03","pol-ops-01","pol-ops-02"]
evaluated − passed derived
["pol-sec-01"]
payload.policyContext.consequence.assessed
false
payload.policyContext.autonomy.enforced
false
payload.cairnBuild.version
2.10.1
payload.cairnBuild.gitSha
b39cbb1
prevHash
3e40d08a69268dee8f68ac42b46ca9e7297070aa0d59eb024b28f5edc1fd3a2b
hash
9abef3e08c44a10b65c72dac361d45f32a53d31b8d32bdf15889c51afda7836e
Not checked yet — the checks run further down this page.

Two fields read differently from how they look. outcome: "EXECUTED" is written when the gate permits the call, before the tool runs — it records the decision, not the result. What then happened is an execution span, and this bundle carries none. And idBasis: "asserted" means the actor name is what the operating system session reported, not an identity a provider authenticated.

Check it yourself

Four checks, run in your browser.

The code is js/receipt.js — about a hundred lines, no dependencies, readable in a sitting. It restates the hash definition from CAIRN's ledgerStore.js rather than importing it, so you are not trusting CAIRN to check CAIRN.

1 · Contents
Each receipt's hash is SHA-256 of its predecessor's hash followed by its own contents, keys sorted. Recomputed here.
Waiting…
Proves the record has not changed since its hash was computed. Not who computed it.
2 · Position
Every record from the first to the receipt re-derives, and each links to the one before.
Waiting…
Detects an altered earlier record — unless every hash after it was recomputed too. Try it below.
3 · File
The SHA-256 of ledger.jsonl equals the figure in manifest.json.
Waiting…
Detects corruption. The manifest is unsigned in this bundle, so anyone who rewrote the ledger could rewrite this figure.
4 · Coverage
Every gate decision in audit.jsonl has a receipt in the ledger.
Waiting…
If the ledger write fails, CAIRN still lets an allowed call proceed, so a broken database cannot stop the product. This is how you would find one. It cannot see a decision that reached neither file, and a gate that errors fails closed with an audit event rather than a receipt.
The limit, demonstrated · the refused receipt
Loading the bundle…
Alter an earlier record and the refused receipt's position check fails. Then do what an attacker with write access would: recompute every hash after the change.

That is the whole of what tamper-evident means, and why no stronger word is used. What defeats the rewrite is something the rewriter does not hold: an RFC 3161 timestamp token from an authority you trust, or an earlier copy of the bundle kept somewhere they cannot reach — which is what CAIRN's scheduled export to a declared directory is for. CAIRN also signs a bundle when a signing key is present on the node. That key sits on the same host as the ledger, so a signature protects a bundle after it has left the node, not against someone already on it. This bundle has none of the three — signature.txt says UNSIGNED and anchors.json holds no tokens — and a receipt page that did not say so would be claiming the protection without the key.

What a receipt is evidence of

Proves, and does not.

A receipt establishes
It does not establish
What the gate was asked: tool, capability, and the names of the arguments
The argument values. Only their names are recorded
The decision, the stated reason, and the rules evaluated, with a digest of the built-in rule set
That the rules were the right rules, or that the decision was correct. A receipt records a decision; it does not grade one
The CAIRN build that decided, by version and commit
That the build was unmodified on that machine
That the record is unchanged, and so is everything before it — unless the whole chain after an edit was rewritten
Who wrote it. That takes a signature with a key you trust, and this example is unsigned
The time on the deciding node's clock
The time. A node sets its own clock; an RFC 3161 anchor is what attests one, and there is none here
The actor name, and what it rested on (idBasis)
Who the person was, when the basis is asserted
That a decision in the bundle has a receipt
That every decision is in the bundle. Hashing cannot detect a record that was never written, and a call that never entered the gate leaves nothing to hash
Asked for, and not here

Two fields a certificate is expected to carry are not on this record.

An external assessment in September 2026 listed what a per-action certificate should hold: agent identity, action, tool, policy version, sandbox state, assurance tier, evidence hash and timestamp. Six are on the receipt above. Two are not, and have not been added to this page to make it look complete.

Sandbox state
The gate decides whether a call may run and whether it must be isolated; it writes the receipt at that moment, before anything runs. Where it ran is recorded afterwards as an execution span, in a separate file. This bundle's spans.jsonl is empty.
Assurance tier
CAIRN grades what generated code has been shown to do — from static_only up to idempotent — but that grade belongs to a task's acceptance, not to a gate decision, and it is not written into the receipt.
Agent identity
Present, and weaker than the word suggests. agentChain carries the delegation chain a caller states when a call arrives through the MCP gateway. These receipts came from a local operator, so it is null, and the actor is asserted.

Receipts from your own vault.

CAIRN exports a bundle like this one from any node, by hand or on a schedule, with the verification procedure inside it, signed when a signing key is present on the node. The checks on this page work on your bundle unchanged.

Download CAIRN