A receipt says what an agent was recorded as doing at the boundary that governed it. Verifying one is not a signature check. Six things can go wrong, each measured on a running implementation, and each turning the record into something worse than no record.
HASH-INPUT = JCS(receipt without chain.hash and without sig.value) MESSAGE = b"NOA-Receipt-v0.1-sig:" ++ sha256(HASH-INPUT) signature = Ed25519(MESSAGE) chain.hash = "sha256:" ++ hex(sha256(HASH-INPUT))
Canonical form is RFC 8785: keys sorted by UTF-16 code units, integers only, no Unicode normalisation. Two implementations that sort differently produce different bytes for the same object — and then one of them is wrong about who signed what.
| # | What goes wrong | What a buyer sees |
|---|---|---|
| 1 | It re-serialises the parsed object before hashing | A receipt with an extra space verifies. The values checked are not the bytes the signer signed. |
| 2 | An unauthenticated checkpoint is treated as evidence | An honest chain is reported as tampered because of a file anyone can hand over. |
| 3 | A chain that grew past an old checkpoint is called a conflict | Every normal chain that keeps running eventually "fails". |
| 4 | Only the first failure is reported | An operator fixes one thing and the next check finds the other, forever. |
| 5 | It stays silent when no identity manifest was supplied | "Valid" is read as "this agent did it", when all that was established is "some trusted key signed". |
| 6 | It stays silent when no checkpoint was supplied | An unanswered question looks like a clean one. |
The first three are the same defect in different clothes: the verifier accuses an object that did not fail, or names a cause it did not observe. For a tool whose product is credible accusation, that is not a bug in a feature — it is a failure of the thing being sold.
1. open https://attest.rta.lat/verify-offline and save the page 2. paste the receipt and the issuer public key 3. turn off the network — the check still runs
The corpus is public and CC0: 12 vectors, including five receipts that must be rejected and one positive control. A Python and a browser implementation agree on all twelve: receipt-vectors.json.
attest implements the IETF SCITT profile for agent action receipts
(draft-noa-scitt-ai-agent-receipt), verifies offline, and publishes its vectors. It does not claim
that an agent was correct, safe or wise — only that this record was issued by that key and has not changed
since. Free verifier, paid audit. Documentation · Pricing.