The case for specifying the receipt format openly
An enterprise adopting a proprietary receipt format acquires a dependency on the vendor's existence for as long as the records must be verifiable. That is a strange property for an audit artefact.
Why does the receipt format have to be open?
Because evidence only one party can verify is not evidence in the sense that matters. If a court, a regulator or a counterparty has to call the vendor to check a receipt, the vendor's continued existence and cooperation are load-bearing parts of the proof.
- A receipt's value depends on third parties being able to verify it, which requires a format they can implement independently.
- The specification is small — a statement structure, a canonicalisation, a signature layer and a verification procedure.
- Publishing it costs the author a differentiator and buys the property that makes the artefact worth anything.
Part of Research, datasets and methods
Why a proprietary format undermines its own purpose
The reason to produce a receipt is that someone other than you can check it: an auditor, a regulator, a counterparty, a court.
| Verifier | Proprietary format | Open specification |
|---|---|---|
| Your own systems | Works | Works |
| Your auditor | Needs the vendor's tool | Can implement or use any tool |
| A regulator | Depends on the vendor | Independent |
| An opposing party in litigation | Must accept the vendor's tool | Can write their own and compare |
| You, in eight years | Depends on the vendor existing | Independent |
The fourth row is the one that decides it. Evidence an opposing party must take on trust from the producing party's vendor is weak evidence, and a competent opponent will say so.
What needs specifying
Four layers, none large.
- Statement structure. Required fields, types, and what each means. Enough that two implementations produce the same object from the same facts.
- Canonicalisation. Reference RFC 8785 rather than inventing one.
- Signature layer. How the authenticator assertion and the issuer countersignature are carried and what each covers.
- Verification procedure. The ordered checks, and what each failure means.
Layer four is the one usually under-specified and the one that matters most in a dispute. Two implementations that check different things will disagree about the same receipt.
The rules that make it usable
A specification of this kind earns its keep by being restrictive.
| Rule | Rationale |
|---|---|
| One signature algorithm | No negotiation, no confusion attacks |
| One canonicalisation | Reproducible digests |
| Required fields, not optional ones | No absent-field logic in verifiers |
| Masking permitted only with the full value signed | Truncation must not be the authoritative form |
| Short expiry and single use by default | Replay and staleness closed at the format level |
| Explicit versioning, no compatibility branches | Verifiers stay small |
Each of these removes a choice, and each removed choice is code that never has to exist in any verifier anyone writes.
What publishing costs the author
Worth being direct about, because the argument is more credible when the cost is named.
- The format stops being a differentiator; competitors can implement it
- Switching away becomes cheap, because receipts remain valid
- Design decisions become subject to public disagreement
- The specification must be maintained even when it is inconvenient
In exchange, the receipts become worth something to the parties who need to rely on them. That trade is favourable only if you believe the format's value comes from adoption rather than from exclusivity.
What it does not need to specify
Restraint matters as much as coverage. A specification that grows to cover everything becomes unimplementable.
- How statements are rendered to humans — implementation-specific and rightly so
- Which actions should be gated — a policy question for each deployment
- How keys are managed operationally — beyond how they are published
- Storage, transport or retention — orthogonal to the format
The boundary is what a verifier needs in order to check a receipt. Everything else belongs in guidance rather than in the specification.
What to publish alongside
A specification without these is an aspiration.
- Test vectors, including negative cases with the expected failure reason
- At least one reference implementation, small enough to read
- A conformance procedure so an implementation can demonstrate correctness
- A stated security model: what verification establishes, and explicitly what it does not
The last is the one that separates a specification from marketing. Saying plainly that a receipt proves a credential signed a statement — and not that the signer understood it, or that the statement was true — is what makes the rest of the claims credible.
The fields, concretely
| Field | Type | Purpose |
|---|---|---|
v | integer | Format version. Refuse unknown versions. |
typ | string | Statement type, so verifiers can route. |
iss | URL | Issuer, and where the key set is published. |
sub | string | Subject identifier, stable across statements. |
iat / exp | integer | Issued-at and expiry, seconds since epoch. |
nonce | base64url | Challenge, so a statement cannot be replayed. |
effect | object | What will happen, in structured form. |
rendered_hash | string | Hash of exactly what the human was shown. |
A test vector you can run
Canonicalisation is where interoperable implementations usually diverge, so the specification should ship a vector rather than a description. Take this statement body:
{
"v": 1,
"typ": "statement",
"iss": "https://example.org",
"sub": "did:key:z6MkExampleSubjectIdentifier",
"iat": 1750000000,
"exp": 1750000300,
"nonce": "Zm9vYmFyYmF6cXV4",
"effect": {
"kind": "payment.release",
"to": "ACME Supplies Ltd",
"amount": "48250.00",
"currency": "GBP"
},
"rendered_hash": "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
}
Serialised with keys sorted lexicographically, no insignificant whitespace, and UTF-8 output, the canonical form is:
{"effect":{"amount":"48250.00","currency":"GBP","kind":"payment.release","to":"ACME Supplies Ltd"},"exp":1750000300,"iat":1750000000,"iss":"https://example.org","nonce":"Zm9vYmFyYmF6cXV4","rendered_hash":"sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae","sub":"did:key:z6MkExampleSubjectIdentifier","typ":"statement","v":1}
Its SHA-256 digest is:
hex: 928d769e0bbf38cbbad68b40fd367b30ada716b771f64656475f7695923b6722
base64url: ko12ngu_OMu61otA_TZ7MK2nFrdx9kZWR192lZI7ZyI
Any implementation that produces a different digest for this input has a canonicalisation bug, and will produce signatures that other implementations reject. That is the entire value of shipping a vector: the disagreement surfaces in a test rather than in a dispute.
What publishing costs, and what it does not
Objections and honest limits
“Publishing the format gives away the product.” The format is not the product. Issuance, key management, revocation, integration and support are. A published format makes the artefact worth relying on, which is what makes the rest saleable.
“An open format invites weak implementations.” It does, which is why the specification should state the rules that make interoperability testable — one canonicalisation, one signature suite, explicit rejection of unknown versions — and ship vectors that catch a wrong implementation immediately.
What the specification should not cover: how a subject identifier is chosen, what an effect object contains for a given domain, or how keys are rotated. Those are deployment decisions, and standardising them prematurely makes the format narrower without making it safer.
What to publish alongside the specification
- A reference verifier, permissively licensed. Reading code settles arguments that prose does not.
- Test vectors, including failure cases. Expired, wrong key, mutated body.
- The canonicalisation rule, with a worked example. This is where implementations diverge.
- A versioning policy. So unknown versions are refused, not guessed at.
- A public key set at a stable URL. Verification with no callback to you.
Terms used here
- Canonicalisation
- Producing one deterministic byte sequence from a data structure, so signatures are reproducible.
- Test vector
- A fixed input with a known correct output, used to catch implementation divergence.
- Non-repudiation
- The property that a signer cannot credibly deny having produced a statement.
Frequently asked questions
Why can't a proprietary format work? The receipt's purpose is verification by third parties. Evidence an opposing party must take on trust from your vendor is weak evidence.
What is most often under-specified? The verification procedure. Two implementations checking different things will disagree about the same receipt, which is exactly what a dispute exposes.
What does publishing cost? The format stops being a differentiator, switching becomes cheap, and design decisions become publicly contestable. That trade is favourable only if value comes from adoption.
What should be left out? Rendering, which actions to gate, operational key management and storage. The boundary is what a verifier needs to check a receipt.
Why ship test vectors rather than prose? Because canonicalisation bugs are invisible in prose and obvious against a vector. The disagreement surfaces in a test instead of a dispute.
What should the specification leave out? Subject identifier schemes, domain-specific effect contents and key rotation policy — deployment decisions that would make the format narrower without making it safer.
Does publishing the format lose the business? The format is not the business. Issuance, key management, revocation and support are, and they are only worth buying if the artefact is worth relying on.
Where this fits in Manav
Manav's statement format and its verifier are published, so anyone can check a receipt with no call to us — including after we are gone.
Sources and further reading
- RFC 8785 — JSON Canonicalization Scheme
- RFC 8037 — CFRG elliptic curve signatures for JOSE
- W3C Web Authentication: An API for accessing Public Key Credentials Level 3
- IETF process documentation on specification development and reference implementations.
- RFC 8032 — Edwards-Curve Digital Signature Algorithm (EdDSA)
- RFC 7517 — JSON Web Key