Verify

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-verify built from source. The verifier lives at codeberg.org/restorable/restorable under cmd/restorable-verify; make build at 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:

  1. The receipt's DSSE envelope parses cleanly. Malformed bytes fail here.
  2. The signature algorithm is EdDSA. Any other algorithm is refused to prevent downgrade attacks.
  3. 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.
  4. The payload is schema-valid. Required fields present, types correct, enums valid, timestamps well-formed.
  5. The per-check results roll up correctly to the top-level result. Any disagreement fails verification.
  6. If attestation.type is amd-sev-snp, the attestation chain verifies to AMD's root CA and the launch digest matches the published expected value for the receipt's agent_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.