Receipt Chain Verification
Agent Receipts are hash-chained into tamper-evident sequences. This page describes the canonical form, signing process, and verification algorithms.
Canonical form
Section titled “Canonical form”For hashing and signing, receipts must be serialized using the JSON Canonicalization Scheme (RFC 8785) with the proof field removed before hashing. This approach aligns with the W3C Verifiable Credentials Data Integrity specification, though the signing procedure defined here is intentionally simplified.
Signing
Section titled “Signing”The issuer signs the canonical receipt (proof field excluded) with its Ed25519 private key. The signature is encoded as a multibase string (z-prefixed base58btc) and placed in proof.proofValue.
Chain integrity verification
Section titled “Chain integrity verification”To verify a receipt chain:
- Retrieve all receipts for the chain, ordered by
chain.sequence. Let n be the number of receipts. - For each receipt R(i) (0-based index):
- a. Verify the
proofsignature against the issuer’s public key atproof.verificationMethod. - b. Compute the hex-encoded SHA-256 digest of the RFC 8785 canonical form of R(i) with the
prooffield removed. - c. If i < n - 1, confirm R(i+1)‘s
chain.previous_receipt_hashequalssha256:concatenated with that hex digest.
- a. Verify the
- For each receipt R(i) where i > 0, confirm
chain.sequenceequals R(i-1)‘schain.sequence+ 1. - Confirm R(0)‘s
chain.previous_receipt_hashisnull.
If any step fails, the chain is broken at that point. Receipts before the break may still be individually valid; receipts after are suspect.
Reversal receipts
Section titled “Reversal receipts”Receipts are immutable once issued. To record that an action was reversed, issue a new receipt appended to the same chain with:
- The same
action.typeas the original receipt outcome.status:"success"if the reversal succeeded,"failure"if it did not- A
reversal_offield referencing theidof the original receipt
The chain is always append-only; the original receipt is never mutated or removed.
Chain issuer model
Section titled “Chain issuer model”A chain MUST have a single issuer. All receipts within a chain MUST have the same issuer.id. Delegated agents MUST create a new chain and link it to the parent chain via the delegation field.
Delegation verification
Section titled “Delegation verification”When a receipt chain includes a delegation field:
- Resolve
delegation.parent_chain_idand retrieve the parent chain. - Locate the receipt with
idmatchingdelegation.parent_receipt_idin the parent chain. - Confirm
delegation.delegator.idmatches theissuer.idof the parent chain’s receipts. - Confirm the
principalin the delegated chain matches theprincipalin the parent chain — the human on whose behalf actions are taken does not change across delegation.
If any step fails, the delegation link is unverifiable. The delegated chain’s receipts are still individually valid but cannot be traced back to the parent chain.
Trusted timestamp verification
Section titled “Trusted timestamp verification”When action.trusted_timestamp is present and non-null:
- Base64-decode the value to obtain the DER-encoded RFC 3161
TimeStampToken. - Verify the TSA’s signature on the
TimeStampTokenagainst the TSA’s certificate. - Extract the
MessageImprinthash from theTimeStampToken. - Confirm the
MessageImprinthash matches the SHA-256 digest of the receipt’s canonical form (proof field removed). - Confirm the TSA timestamp falls within a reasonable window of
action.timestamp(implementation-defined tolerance).
Trusted timestamps are optional. When present, they provide independent evidence of when the receipt was created, which cannot be backdated by the issuer.