Profile · RFC 9943 / RFC 9942
A receipt is a SCITT Signed Statement.
The IETF standardised the shape this product had been building towards: signed statements, registered with a transparency service, answered with receipts. RFC 9943 fixes the envelope and leaves the content of a statement to profiles. This page is that profile. A verifier that speaks SCITT can check a Lyntway receipt and its inclusion in the log without knowing anything else about us.
Reproducible vectors, with both keys derived from stated seeds, live at testdata/scitt/vectors.json in the public tools repository. Every hex value is lowercase; every CBOR value is deterministically encoded per RFC 8949 §4.2.1.
1 · The Signed Statement
One COSE_Sign1, tag 18, over the receipt you already hold.
#6.18([ protected: bstr, unprotected: {}, payload: bstr, signature: bstr ])Payload. The receipt's canonical JSON (RFC 8785), verbatim, including its own signature object. The envelope signs the document the customer holds, not a re-serialisation of it.
Protected header, four entries in encoded-key order:
1 alg -8 EdDSA (RFC 9053)
3 content type 50 uint: CoAP content-format application/json (RFC 9943 §6 allows tstr / uint)
4 kid bstr key_id RFC 9943 §6: kid MUST be present when neither x5t nor x5chain is
15 CWT Claims {1: iss, 2: sub} RFC 9943 §6: MUST include Issuer (1) and Subject (2)iss is the receipt's issuer.key_id — the identifier a Lyntway verifier already resolves to a public key, so a SCITT verifier looks up the same thing. sub is the receipt's id. Both duplicate a payload field, and a verifier MUST reject an envelope whose claims disagree with its payload. Envelopes issued before this profile carry no label 15 and remain valid: absent claims are absent, not wrong.
Unprotected header. Empty as issued (RFC 9943 §6.3). Signature. Ed25519 over RFC 9052 §4.4 ["Signature1", protected, h'', payload].
2 · The entry and the leaf
The log holds the receipt digest, not the receipt.
receipt_digest = SHA-256( SigningInput(receipt) ) as 64 lowercase hex characters entry = those 64 ASCII bytes (not the 32 raw bytes, not the COSE statement) leaf_hash = SHA-256( 0x00 || entry ) RFC 9162 §2.1.1 node_hash = SHA-256( 0x01 || left || right )
SigningInput is the canonical receipt with signature and anchors removed — the same string a successor receipt records as its chain.prev_hash. This has been the log's leaf definition since it was written; it is documented rather than changed, because changing it would invalidate every root already published.
3 · The COSE Receipt
Signed by the log, payload detached, root recomputed by you.
#6.18([ protected: bstr, unprotected: { 396: {...} }, payload: null, signature: bstr ])
protected:
1 alg -8
4 kid bstr log key id
15 CWT Claims {1: log id, 2: entry} RFC 9943 §6 requires claims on Receipts too;
§5.1.2: iss is the transparency service's one identity
395 vds 1 RFC9162_SHA256 (RFC 9942 table 2); protected, because it
tells the verifier how to read the proof
unprotected:
396 vdp { -1: [ bstr .cbor [ tree_size: uint, leaf_index: uint, inclusion_path: [ * bstr ] ] ] }
payload: detached (f6). The signed payload is the 32-byte root MTH(D[tree_size]),
which appears nowhere inside the receipt bytes.Verification (RFC 9942 §5.2.1): hash the entry as a leaf, walk inclusion_path per RFC 9162 §2.1.3.2 with leaf_index and tree_size to a candidate root; then verify the signature over ["Signature1", protected, h'', root] with the key named by kid. A receipt for any other entry fails at the second step, because no root the log ever signed comes out of the first. For a tree of size one the path is empty, which RFC 9162 defines and RFC 9942's CDDL does not admit; this profile encodes the empty array.
A consistency receipt has the same shape with the proof under key -2 as [tree_size_1, tree_size_2, consistency_path] and the newer root as its detached payload. A Transparent Statement is the Signed Statement with receipts placed in its unprotected header under label 394; its protected header, payload and signature are byte-identical to the statement alone.
Where to get each piece
GET /.well-known/lyntway-keys.json issuer and retired keys GET /v1/log/sth signed tree head GET /v1/log/receipt?digest=<hex>&size=<n> RFC 9942 inclusion receipt, application/cose GET /v1/log/receipt/consistency?from=&to= RFC 9942 consistency receipt GET /v1/log/proof/inclusion?digest=&size= the same proof as JSON, for our own verifier
Offline, with the command-line verifier: lyntway-verify -keys keys.json -inclusion receipt.cose -log-keys keys.json receipt.json.
What this profile is not
It is not a claim that Lyntway is a registered or audited SCITT transparency service, and it does not expose a registration policy: the service registers only receipts it issued itself. It does not certify anything about the AI system a receipt describes. It makes one promise — that an implementer holding the RFCs and the vectors can check our evidence without our help — and the vectors are how you hold us to it.