Verifying a receipt
The practical walkthrough. Given a .intoto.json
receipt and the public signing key of the agent that produced it,
run one command and read its output.
This page is written for an auditor receiving receipts from their customer. The customer forwards the weekly evidence email, the auditor runs through the steps below. If you are the customer and you want to spot-check a receipt yourself, the same commands work.
What you need
-
restorable-verifybuilt from source. The verifier lives at codeberg.org/restorable/restorable undercmd/restorable-verify;make buildat the repo root produces both binaries. -
The receipt file. In the weekly digest, receipts are
attached as
<source>-<timestamp>.intoto.json. - The agent's public signing key. Your customer exports it from their dashboard (Agents → <agent> → Public key) and sends it to you once. Save it; it is stable across receipts.
The simplest verification
restorable-verify --pubkey agent.pub receipt.intoto.json
On a valid receipt, the command prints the parsed payload and
exits 0:
receipt_id: rcpt_01hz...
org_id: org_abc
agent_id: agent_xyz
source_id: prod-db
issued_at: 2026-04-14T02:07:00Z
backup:
backup_id: bkp_01hz...
created_at: 2026-04-13T03:00:00Z
ciphertext_sha: sha256:...
size: 1.2 MB
restore:
started: 2026-04-14T02:00:00Z
duration: 6m52s
agent_version: 0.1.4
target_engine: postgres 16.2
result: PASS
checks:
recent-orders PASS { "n": 1247 }
schema-sanity PASS { "tables": 42 }
attestation: none
signature: valid
prev_receipt: rcpt_01hy... On an invalid receipt (broken signature, schema violation, field out of range), non-zero exit with a one-line diagnostic on stderr:
error: signature verification FAILED What the command checks
In order, short-circuiting on the first failure:
- The receipt's DSSE envelope parses cleanly. Malformed bytes fail here.
-
The signature algorithm is
EdDSA. Any other algorithm is refused to prevent downgrade attacks. - The Ed25519 signature verifies against the supplied pubkey. This is the hard cryptographic check. A valid signature means: the holder of the private key produced this payload, unmodified.
- The payload is schema-valid. Required fields present, types correct, enums valid, timestamps well-formed.
-
The per-check results roll up correctly to the top-level
result. Any disagreement fails verification. -
If
attestation.typeisamd-sev-snp, the attestation chain verifies to AMD's root CA and the launch digest matches the published expected value for the receipt'sagent_version.
Batch verification
The weekly evidence email attaches every receipt as a separate file. Verify all of them at once:
restorable-verify --pubkey agent.pub ./receipts/*.intoto.json
Output is one block per receipt. Exit is 0 only
if every receipt verifies; the first failure sets a non-zero
exit code that persists through the rest of the batch.
For scripted use:
restorable-verify --pubkey agent.pub --format json ./receipts/*.intoto.json \
| jq '. | select(.result != "pass")' Chain verification
Each receipt's prev_receipt_id points at the
previous receipt for the same source. Walking the chain
catches removed-from-the-middle rewrites.
restorable-verify --pubkey agent.pub --follow-chain ./receipts/*.intoto.json
With --follow-chain, the verifier additionally
checks that every receipt's prev_receipt_id
references a receipt that is either in the batch or marked
known-missing (the first receipt per source has
prev_receipt_id: null; earlier-than-batch
receipts show as gaps). A broken chain fails verification.
What a passing verification tells you
"The holder of this agent's private signing key produced this payload at the recorded time, affirming that a backup with this hash was decrypted and restored, and these checks ran against that restore with these results."
What it does not tell you is covered in What a receipt proves.
Common failure patterns
signature verification FAILED
Either the pubkey is wrong for this agent, or the receipt was tampered with after signing. Check with your customer that the agent id in the receipt matches the agent whose pubkey you have.
algorithm mismatch
The DSSE envelope declares an algorithm other than
EdDSA. A valid Restorable receipt is always
EdDSA; anything else is either a forgery or a schema bug.
Report to the customer.
issued_at in future by more than 5 minutes
Clock skew on the agent host. The orchestrator rejects submissions this skewed, so seeing one means either your own clock is off or the receipt was hand-crafted. Check your host time first.
checks roll-up disagrees with stored result
The per-check array and the top-level result
disagree. A well-formed receipt cannot produce this; a
malformed one suggests the bytes in your
.intoto.json were edited by hand. The file is not
trustworthy.
Offline verification
Everything above works offline. The verifier binary, the pubkey, the receipt. No internet required. The only network-touching operation is the optional transparency-log inclusion proof fetch.