{
 "slug": "open-non-repudiation-receipt-specification-rfc-draft",
 "topic_id": "TOPIC-110",
 "cluster": "Content Moats & Datasets",
 "tier": "Tier A",
 "title": "The case for specifying the receipt format openly",
 "summary": "Evidence that only one vendor can produce and verify is not evidence in the sense that matters. A format worth relying on has to be one anyone can implement.",
 "lede": "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.",
 "date": "2026-01-12",
 "category": "Compliance",
 "author_id": "priya-venkatraman",
 "tags": [
  "specification",
  "open standards",
  "interoperability",
  "non-repudiation",
  "RFC",
  "governance"
 ],
 "image_title": "Specifying The Receipt Openly",
 "schema": "Article",
 "key_takeaways": [
  "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."
 ],
 "body": [
  {
   "type": "h2",
   "text": "Why a proprietary format undermines its own purpose"
  },
  {
   "type": "diagram",
   "kind": "compare",
   "alt": "Proprietary versus specified",
   "caption": [],
   "nodes": [],
   "left": {
    "title": "Proprietary format",
    "items": [
     "One implementation",
     "Verification is a service",
     "Vendor outlives the evidence, or does not",
     "Adversary cannot check independently",
     "Lock-in is the product"
    ]
   },
   "right": {
    "title": "Open specification",
    "items": [
     "Any implementation",
     "Verification is arithmetic",
     "Format outlives the vendor",
     "Both sides can check",
     "Interoperability is the product"
    ]
   }
  },
  {
   "type": "p",
   "html": "The reason to produce a receipt is that someone other than you can check it: an auditor, a regulator, a counterparty, a court."
  },
  {
   "type": "table",
   "head": [
    "Verifier",
    "Proprietary format",
    "Open specification"
   ],
   "rows": [
    [
     "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"
    ]
   ]
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "h2",
   "text": "What needs specifying"
  },
  {
   "type": "p",
   "html": "Four layers, none large."
  },
  {
   "type": "ol",
   "items": [
    "<strong style=\"font-weight:600\">Statement structure.</strong> Required fields, types, and what each means. Enough that two implementations produce the same object from the same facts.",
    "<strong style=\"font-weight:600\">Canonicalisation.</strong> Reference RFC 8785 rather than inventing one.",
    "<strong style=\"font-weight:600\">Signature layer.</strong> How the authenticator assertion and the issuer countersignature are carried and what each covers.",
    "<strong style=\"font-weight:600\">Verification procedure.</strong> The ordered checks, and what each failure means."
   ]
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "h2",
   "text": "The rules that make it usable"
  },
  {
   "type": "p",
   "html": "A specification of this kind earns its keep by being restrictive."
  },
  {
   "type": "table",
   "head": [
    "Rule",
    "Rationale"
   ],
   "rows": [
    [
     "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"
    ]
   ]
  },
  {
   "type": "p",
   "html": "Each of these removes a choice, and each removed choice is code that never has to exist in any verifier anyone writes."
  },
  {
   "type": "h2",
   "text": "What publishing costs the author"
  },
  {
   "type": "p",
   "html": "Worth being direct about, because the argument is more credible when the cost is named."
  },
  {
   "type": "ul",
   "items": [
    "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"
   ]
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "h2",
   "text": "What it does not need to specify"
  },
  {
   "type": "p",
   "html": "Restraint matters as much as coverage. A specification that grows to cover everything becomes unimplementable."
  },
  {
   "type": "ol",
   "items": [
    "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"
   ]
  },
  {
   "type": "p",
   "html": "The boundary is what a verifier needs in order to check a receipt. Everything else belongs in guidance rather than in the specification."
  },
  {
   "type": "h2",
   "text": "What to publish alongside"
  },
  {
   "type": "p",
   "html": "A specification without these is an aspiration."
  },
  {
   "type": "ul",
   "items": [
    "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"
   ]
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "h2",
   "text": "The fields, concretely"
  },
  {
   "type": "table",
   "caption": "Minimum statement fields",
   "head": [
    "Field",
    "Type",
    "Purpose"
   ],
   "rows": [
    [
     "<code>v</code>",
     "integer",
     "Format version. Refuse unknown versions."
    ],
    [
     "<code>typ</code>",
     "string",
     "Statement type, so verifiers can route."
    ],
    [
     "<code>iss</code>",
     "URL",
     "Issuer, and where the key set is published."
    ],
    [
     "<code>sub</code>",
     "string",
     "Subject identifier, stable across statements."
    ],
    [
     "<code>iat</code> / <code>exp</code>",
     "integer",
     "Issued-at and expiry, seconds since epoch."
    ],
    [
     "<code>nonce</code>",
     "base64url",
     "Challenge, so a statement cannot be replayed."
    ],
    [
     "<code>effect</code>",
     "object",
     "What will happen, in structured form."
    ],
    [
     "<code>rendered_hash</code>",
     "string",
     "Hash of exactly what the human was shown."
    ]
   ]
  },
  {
   "type": "h2",
   "text": "A test vector you can run"
  },
  {
   "type": "p",
   "html": "Canonicalisation is where interoperable implementations usually diverge, so the specification should ship a vector rather than a description. Take this statement body:"
  },
  {
   "type": "code",
   "text": "{\n  \"v\": 1,\n  \"typ\": \"statement\",\n  \"iss\": \"https://example.org\",\n  \"sub\": \"did:key:z6MkExampleSubjectIdentifier\",\n  \"iat\": 1750000000,\n  \"exp\": 1750000300,\n  \"nonce\": \"Zm9vYmFyYmF6cXV4\",\n  \"effect\": {\n    \"kind\": \"payment.release\",\n    \"to\": \"ACME Supplies Ltd\",\n    \"amount\": \"48250.00\",\n    \"currency\": \"GBP\"\n  },\n  \"rendered_hash\": \"sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae\"\n}"
  },
  {
   "type": "p",
   "html": "Serialised with keys sorted lexicographically, no insignificant whitespace, and UTF-8 output, the canonical form is:"
  },
  {
   "type": "code",
   "text": "{\"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}"
  },
  {
   "type": "p",
   "html": "Its SHA-256 digest is:"
  },
  {
   "type": "code",
   "text": "hex:    928d769e0bbf38cbbad68b40fd367b30ada716b771f64656475f7695923b6722\nbase64url: ko12ngu_OMu61otA_TZ7MK2nFrdx9kZWR192lZI7ZyI"
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "h2",
   "text": "What publishing costs, and what it does not"
  },
  {
   "type": "h2",
   "text": "Objections and honest limits"
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“Publishing the format gives away the product.”</strong> 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."
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“An open format invites weak implementations.”</strong> 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."
  },
  {
   "type": "p",
   "html": "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."
  }
 ],
 "faq": [
  {
   "q": "Why can't a proprietary format work?",
   "a": "The receipt's purpose is verification by third parties. Evidence an opposing party must take on trust from your vendor is weak evidence."
  },
  {
   "q": "What is most often under-specified?",
   "a": "The verification procedure. Two implementations checking different things will disagree about the same receipt, which is exactly what a dispute exposes."
  },
  {
   "q": "What does publishing cost?",
   "a": "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."
  },
  {
   "q": "What should be left out?",
   "a": "Rendering, which actions to gate, operational key management and storage. The boundary is what a verifier needs to check a receipt."
  },
  {
   "q": "Why ship test vectors rather than prose?",
   "a": "Because canonicalisation bugs are invisible in prose and obvious against a vector. The disagreement surfaces in a test instead of a dispute."
  },
  {
   "q": "What should the specification leave out?",
   "a": "Subject identifier schemes, domain-specific effect contents and key rotation policy — deployment decisions that would make the format narrower without making it safer."
  },
  {
   "q": "Does publishing the format lose the business?",
   "a": "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."
  }
 ],
 "sources": [
  {
   "t": "RFC 8785 — JSON Canonicalization Scheme",
   "u": "https://www.rfc-editor.org/rfc/rfc8785"
  },
  {
   "t": "RFC 8037 — CFRG elliptic curve signatures for JOSE",
   "u": "https://www.rfc-editor.org/rfc/rfc8037"
  },
  {
   "t": "W3C Web Authentication: An API for accessing Public Key Credentials Level 3",
   "u": "https://www.w3.org/TR/webauthn-3/"
  },
  {
   "t": "IETF process documentation on specification development and reference implementations."
  },
  {
   "t": "RFC 8032 — Edwards-Curve Digital Signature Algorithm (EdDSA)",
   "u": "https://www.rfc-editor.org/rfc/rfc8032"
  },
  {
   "t": "RFC 7517 — JSON Web Key",
   "u": "https://www.rfc-editor.org/rfc/rfc7517"
  }
 ],
 "related": [
  {
   "slug": "cryptographic-receipt-primitive-rfc8785-ed25519-vs-jwt",
   "title": "A session token is not an audit record",
   "category": "Compliance"
  },
  {
   "slug": "building-manav-verify-write-open-source-offline-verifier",
   "title": "Writing an offline verifier with no dependencies",
   "category": "Compliance"
  },
  {
   "slug": "okta-stytch-wont-build-offline-receipts-saas",
   "title": "Why an identity vendor will not ship offline verification",
   "category": "Comparison"
  },
  {
   "slug": "w3c-verifiable-credentials-vc-trap-enterprise-software",
   "title": "Where decentralised identity's generality becomes a cost",
   "category": "Comparison"
  }
 ],
 "image": "https://cdn.twc.sh/images/igcache/Specifying%20The%20Receipt%20Openly/1200_630/blog.jpg",
 "wordcount": 1132,
 "url": "/blog/open-non-repudiation-receipt-specification-rfc-draft.html",
 "reading_time": "5 min read",
 "hub": {
  "slug": "topics/research-and-datasets",
  "title": "Research, datasets and methods"
 },
 "answer": "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.",
 "answer_q": "Why does the receipt format have to be open?",
 "glossary": [
  {
   "term": "Canonicalisation",
   "def": "Producing one deterministic byte sequence from a data structure, so signatures are reproducible."
  },
  {
   "term": "Test vector",
   "def": "A fixed input with a known correct output, used to catch implementation divergence."
  },
  {
   "term": "Non-repudiation",
   "def": "The property that a signer cannot credibly deny having produced a statement."
  }
 ],
 "checklist": {
  "title": "What to publish alongside the specification",
  "id": "publish",
  "desc": "Five steps.",
  "steps": [
   {
    "name": "A reference verifier, permissively licensed.",
    "text": "Reading code settles arguments that prose does not."
   },
   {
    "name": "Test vectors, including failure cases.",
    "text": "Expired, wrong key, mutated body."
   },
   {
    "name": "The canonicalisation rule, with a worked example.",
    "text": "This is where implementations diverge."
   },
   {
    "name": "A versioning policy.",
    "text": "So unknown versions are refused, not guessed at."
   },
   {
    "name": "A public key set at a stable URL.",
    "text": "Verification with no callback to you."
   }
  ]
 },
 "cta": {
  "title": "Where this fits in Manav",
  "html": "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.",
  "href": "../docs.html",
  "label": "Read the specification"
 }
}