Agent Action Capsule · explain
05 / 11

SCITT: statements, receipts, transparency

An issuer signs a statement, a transparency service records it in an append-only log and hands back a receipt, and anyone holding the two public keys can check both offline.

Sign, register, receive a receipt, verify offline Three columns: an issuer on the left, a transparency service with a Merkle tree log in the middle, a verifier on the right. Step 1: the issuer produces a signed statement, a COSE_Sign1 with iss and sub in its protected header. Step 2: an arrow labelled register carries it to the transparency service, which checks its Registration Policy. Step 3: the service appends the statement's digest as leaf 2 of an eight-leaf tree and computes the root. Step 4: a receipt, signed by the log key, with the root as detached payload and an inclusion proof of tree size 8, leaf index 2 and a three-node path, returns to the issuer. Step 5: the statement and receipt are passed together to the verifier along the bottom, bypassing the service. Step 6: the verifier checks the statement signature with the issuer's public key, recomputes the root from leaf 2 through the highlighted path nodes (leaf 3, the node over leaves 0 and 1, and the node over leaves 4 to 7), and checks the receipt signature with the log's public key, without contacting the issuer or the service. Issuer holds the signing key Signed Statement COSE_Sign1, alg EdDSA CWT_Claims: iss, sub payload: the claim signed by issuer key Receipt COSE_Sign1, log key vds = 1 (label 395) payload: root, detached proof: [8, 2, path×3] Transparency Service append-only log, RFC9162_SHA256 Registration Policy: admit? leaf 2 leaf = SHA-256(statement bytes) tree_size 8 root register receipt statement + receipt (no call back to issuer or service) Verifier has two public keys only ✓ statement signature issuer public key ✓ leaf + path → root 5c834b80… ✓ receipt signature log public key Shows: included at index 2 of size 8. Not: the claim is true.

1What it is

SCITT is an IETF architecture in which an issuer signs a statement and registers it with a transparency service that keeps an append-only log.

On registration the service returns a receipt, a signed proof that the statement sits at a given position in its log at a given size.

A verifier holding the issuer's public key and the log's public key can check the statement and the receipt from the bytes alone, without contacting either party.

2Design choices

Chosen: a receipt is an inclusion proof in the service's log; beyond that it shows only the time of registration, and only when it carries a signed time claim.
Rejected: treating registration as the service agreeing with the statement; a receipt proves inclusion, not agreement. (RFC 9943; restated in draft-mih-scitt-checkpointed-local-log-02 §1.2, §4.5)
Chosen: the statement names its issuer and subject in signed CWT claims (iss, sub).
Rejected: registering any bare COSE_Sign1; a Producer Envelope without protected iss/sub is not a Signed Statement and a registrar wraps the capsule ID in one. (RFC 9943; draft-mih-scitt-agent-action-capsule-05 §3.3)
Chosen: the receipt names its verifiable data structure (vds, label 395) and carries the proof in its header (label 396).
Rejected: baking one log format into the statement; the same statement can carry receipts from logs built differently, and only the receipt changes. (RFC 9942)
Chosen: the receipt's payload is the tree root, detached, and the verifier recomputes it from the leaf and the path.
Rejected: other bytes in the payload slot; a signature over them binds nothing to the leaf whose inclusion the receipt proves, and the reference verifier rejects it. (RFC 9942; scitt-cose vector fail-attached-payload)
Chosen: each service publishes a Registration Policy, and independence comes from registering the same statement with more than one service.
Rejected: one service as the single source of truth; its view cannot be compared with anyone else's. (RFC 9943 §5.1.1 and §6.3, as cited in draft-mih-scitt-checkpointed-local-log-02 §1.2, §4.5)

3What it shows / what it does not

Shows

  • The issuer's key signed exactly these statement bytes (signature check).
  • The statement's leaf is at leaf_index in the log at tree_size, under a root the log key signed (inclusion).
  • The time of registration, but only if the receipt carries a signed time claim.
  • Given two signed tree heads and a consistency proof, that the log only appended between them.

Does not show

  • That the statement's content is true, or that the issuer was entitled to make it.
  • That the log is complete: anything the issuer never registered is simply absent.
  • That other parties see the same log; one receipt cannot rule out a different view shown elsewhere unless tree heads are compared through consistency checks.
  • That the issuer's key belongs to who you think; key authorization is the verifier's own policy.

4Example

The expected values for a valid EdDSA signed statement and its RFC9162_SHA256 receipt, from the cross-implementation vector set. The Python and Go verifiers check both halves; the Rust verifier is receipt-only and checks only the receipt half.

{
  "description": "Happy path: EdDSA Signed Statement + RFC9162_SHA256 receipt; everything verifies.",
  "payload_sha256": "08b6e9ec3bd002da4e21976b6dc1411567d30acb30d671161583b3d8351e7692",
  "protected_header": {
    "statement": {
      "alg": "EdDSA",
      "alg_code": -8,
      "content_type": "application/json",
      "cwt_claims_label": 15,
      "issuer": "https://test-vectors.scitt-cose.invalid",
      "subject": "urn:scitt-cose:test-vectors:v1:valid-eddsa"
    },
    "receipt": {
      "alg": "EdDSA",
      "alg_code": -8,
      "vds_label": 395,
      "vds": 1
    }
  },
  "leaf_entry": "a6dd4523ffdda1754c234acf209bdde758db7d30438468700a7bf6c01e800e3a",
  "leaf_index": 2,
  "tree_size": 8,
  "inclusion_path": [
    "d5790c062bb617c193f8dbedf90469ccf49525739238b6d1102df4d3424fbd44",
    "fb85d1461099612b0a34168f8f207bc9a761a91cd1173f39c5dd7ca4d1d92a6c",
    "b8e2ffb9ad6e45622e44393e6f58a747c4c5b3900d9676e3ecf50aab2ba266e4"
  ],
  "reconstructed_root": "5c834b80c0b40ca993f0b4a3ef9d2e77a8a3e8113358e4f318866c02cd448362",
  "statement_signature_valid": true,
  "receipt_valid": true,
  "result": "VALID"
}
FieldTypeMeaning
payload_sha256hexSHA-256 of the statement's payload bytes (opaque to the log).
protected_header.statementmapStatement alg, content type, and CWT claims at label 15: issuer and subject.
protected_header.receiptmapReceipt alg and vds at label 395; 1 is RFC9162_SHA256.
leaf_entryhexThe log's leaf: in this vector set, SHA-256 of the complete statement bytes.
leaf_index, tree_sizeuintPosition of the leaf and size of the log the receipt speaks for.
inclusion_pathhex[]The audit path: sibling hashes from the leaf up to the root.
reconstructed_roothexThe root a correct verifier recomputes; the receipt's detached payload.
statement_signature_validboolWhether the statement signature must verify under the issuer key.
receipt_validboolWhether the receipt must verify under the log key.
resulttstrOverall verdict, VALID or INVALID.

Source: action-state-group/scitt-cose:test-vectors/v1/valid-eddsa/expected.json @ ac9d3b90d058ff609497573a68402e561fa99404

5Specs