| Internet-Draft | SCITT Measurement Capsules | September 2026 |
| Templeman | Expires 31 March 2027 | [Page] |
This document defines the measurement capsule: a small, deterministic JSON record in which a party that is neither the subject of a claim nor a participant in the action it concerns records what the subject declared, what the measuring party observed, and the difference between the two. A capsule carries digests of its evidence, a measurement state in which "could not be checked" is a first-class outcome, and no decision, approval or authorisation. Capsules are identified by the SHA-256 of their JCS serialisation and batched under an RFC 9162 Merkle Tree Hash. The document describes their registration as SCITT Signed Statements (RFC 9943), how they refer to rather than restate receipts issued under other profiles, and one implementation, including where it does not yet match this profile.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 31 March 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Several individual drafts define receipts for automated actions. [I-D.schrock-ep-authorization-receipts] binds an enrolled approver key to one canonical action before execution; [I-D.marques-asqav-compliance-receipts] profiles signed action receipts, including decision receipts carrying a policy digest; [I-D.sahu-agent-action-receipts] defines signed, hash-chained action receipts and [I-D.noa-scitt-ai-agent-receipt] carries such receipts as SCITT Signed Statements; [I-D.hopley-x402-compliance-receipt] records categorical screening outcomes in agentic-payment flows. These readings are the author's; corrections from their authors will be adopted.¶
Those receipts are issued by a party in the path of the action and answer "was this permitted, and by whom?". They are not designed to answer a different question: does what a subject publicly declares about itself (a protocol version in a registry, a signing key in an agent card, a token's deployments, an evaluation result) match what an unaffiliated reader can observe?¶
A measurement capsule records one such comparison. It differs from an action receipt in three ways. First, its Issuer is neither the subject nor a participant in the action, and it grants no authority; where the Issuer measures itself, the capsule should say so. Second, it carries the declaration, the observation and an explicit differential, and its state may be UNCHECKABLE, which is never recorded as a pass, a failure, a zero or an absence. Third, it may reference a receipt issued under another profile by digest (Section 7.2) but never copies its outcome, so an approver's receipt and an independent measurement of the same effect can sit in one Transparency Service without either restating the other.¶
The closest work, [I-D.dogru-cedulon-decision-profile], reconciles a
decider's signed Decision Records against an authenticated extract of
effects; a capsule records one comparison and draws no conclusion about
any decision. [I-D.mih-sokolov-scitt-payload-binding] specifies declared
canonicalisation and typed digest references for Signed Statements;
capsule_id (Section 4) is its derived identifier under algorithm jcs
with the exclusion set {capsule_id}.¶
This document profiles content, not the SCITT architecture. The terms Issuer, Signed Statement, Transparency Service and Receipt are used as defined in [RFC9943], and registration follows [I-D.ietf-scitt-scrapi] unchanged. The author's organisation does not operate a Transparency Service. The document is Experimental because its requirements on decision fields and UNCHECKABLE (Section 7) have not been shown useful by more than one implementation (Section 13).¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
What the subject, or a registry or manifest speaking for it, states about a claim, as read from bytes the measurer retrieved.¶
What the measurer read from a source other than the declaring one: a live protocol exchange, a ledger's state, a signature verification or a rerun on different hardware.¶
The structured difference between the two. It is data, not a judgement: "these two statements disagree" does not say which is true.¶
The Issuer of a capsule; neither the subject nor a participant in the action the claim concerns, except in a self-measurement, which the capsule SHOULD mark as such.¶
An ordered vocabulary recording how strong an observation is, as distinct from what it found: STATE_PROOF_VERIFIED (a proof checked against a block header's state commitment, the header not checked against consensus), STATE_PROOF_RECORDED (kept, did not verify), OPERATOR_API (an operator's answer, no proof), UNCHECKABLE and REJECTED (an identity check failed). A label MUST NOT be upgraded; an Issuer MUST refuse to build a capsule from a source record whose label claims more than its evidence shows.¶
A capsule is a JSON object [RFC8259] with the structure below, in CDDL
[RFC8610] using the .cat control of [RFC9165]. The inner structure
of the open maps depends on kind.¶
capsule = {
"schema": "csoai.measurement-capsule/0.2",
"kind": tstr .regexp "measurement\\.[a-z0-9_]+",
"subject_id": tstr,
"claim": { "statement": tstr, * tstr => any },
"declared": open-map,
"observed": open-map,
"differential": open-map,
"sources": sources,
"measurement_state": tstr .size (1..64),
"authority_state": authority-none,
"effect_reference": effect-reference / null,
"observed_at": tdate,
"correction_pointer": correction-pointer / null,
"limitations": [* tstr],
? "runtime_declared": open-map,
? "runtime_observed": open-map,
"capsule_id": sha256-hex
}
open-map = { * tstr => any }
sources = { * tstr => source-value }
source-value = digest / null / [* source-value] / sources
digest = tstr .regexp "(sha256:)?[0-9a-f]{40,128}"
sha256-hex = tstr .regexp "[0-9a-f]{64}"
authority-none = "NONE: measurement only; this capsule grants"
.cat " and records no execution authority"
effect-reference = { "profile": tstr, "statement_digest": digest,
? "transparency_service": tstr, ? "entry_id": tstr, * tstr => any }
correction-pointer = { "record_schema": tstr, "record_version": tstr,
"record_sha256": digest, "supersedes_record_sha256": digest / null,
"from": any, "to": any, ? ("why" / "cause"): tstr, * tstr => any }
kind fixes the inner structure of the open maps and the vocabulary
of measurement_state. subject_id is an identifier (an endpoint URL,
asset:product:ledger:address), not a resolvable reference.
claim.statement can be true or false of the subject. differential
SHOULD name the items that differ, not only a count. sources holds
digests of the artefacts the capsule was derived from (signed source
record, fetched documents, responses, proofs, commits) and MUST NOT
carry evidence bytes, URLs or free text; any other value makes the
capsule invalid. authority_state is the only member whose name
concerns authority, with one permitted value. observed_at is the time
of the observation, not of the build. limitations states what the
measurement does not show; a capsule SHOULD carry at least one, and it
is covered by capsule_id. In runtime_declared and
runtime_observed (reproducibility claims), values not recorded at the
time are UNRECORDED, never omitted or inferred.¶
The implementation's seven kinds, not registered here, compare: an MCP
endpoint's registry, mcp.json, server-card and payment-manifest
declarations with its live initialize and tools/list answer
(contract_parity); an agent card's signatures with their verification
under its declared protocol version (a2a_card_signature); an issuer's
list of a tokenised asset's deployments with each ledger's state
(cross_ledger_supply); external indexes' listings of the measurer's
own offerings with its live bytes (self_parity); a signed evaluation
result with a re-run on a second runtime
(cross_runtime_reproduction); and an endpoint's advertised tools at
two times (tool_drift). The seventh, public_signal, records one
reading of a public counter and is observation-only (Section 14).¶
capsule_id is the SHA-256 digest, as 64 lowercase hexits, of the JSON
Canonicalization Scheme (JCS) [RFC8785] serialisation of the capsule
with capsule_id removed. Issuers MUST NOT place non-finite numbers or
integers whose magnitude exceeds 2^53 in a capsule; such quantities
(token supplies in base units are the common case) MUST be carried as
strings. Issuers SHOULD avoid floating-point values.¶
A capsule file stores each capsule as exactly its JCS bytes, so that a
verifier can recompute capsule_id from the stored line.¶
A batch is a capsule file and a batch record that commits to it. The
capsule file holds one capsule per line, each followed by a single LF,
sorted by capsule_id, with no duplicates.¶
The batch Merkle root is the Merkle Tree Hash of [RFC9162], Section
2.1.1, with SHA-256, over the capsule_id values as 32-byte strings
sorted in ascending byte order. Sorting makes the root a commitment to a
set: reordering the file does not change it, so a changed root always
means changed membership or content. Inclusion of one capsule is shown
by an RFC 9162 audit path (Section 2.1.3), which existing tooling can
verify.¶
The batch record is a JSON object carrying at least schema, as_of,
kind, n_capsules, per-state counts, merkle_root, the construction
in words, the capsule file's SHA-256 and authority_state. It SHOULD
state its inclusion rule and count, by reason, the source rows the rule
excluded (for example, a dimension on which only one public surface
spoke, where nothing was compared), so a reader sees what was not
measured.¶
An index lists each batch's record digest, root, count, signature state
and timestamp state, and carries two roots under the same rule: one over
the union of all capsule_id values (a duplicate across batches is an
error) and one over the batch roots. The index is signed and
timestamped. The implementation also chains daily indexes: each names
the previous day's index by SHA-256 and root and states the number of
days without an index.¶
A capsule, batch record or index is registered as the payload of a COSE_Sign1 [RFC9052] Signed Statement with these protected header parameters:¶
alg (1): Ed25519 (-19) [RFC9864], an Ed25519 [RFC8032] key, or another algorithm the Transparency Service's policy permits (the polymorphic EdDSA (-8) is deprecated by [RFC9864]);¶
content type (3):
application/vnd.csoai.measurement-capsule+json, or the same with
-batch or -index before +json (Section 11);¶
kid (4): the UTF-8 bytes of the signing key's verification method,
for the implementation did:web:csoai.org#board-attestation-1
[DID-WEB];¶
CWT Claims (15) [RFC9597]: iss the Issuer, sub the batch's
kind, the capsule's subject_id or a fixed index name, and iat
the signing time.¶
The payload is the JCS serialisation of the object, or its SHA-256 under the COSE Hash Envelope [RFC9995]. With one registration per batch, inclusion is shown in two steps: capsule to batch root (the Issuer's tree), then batch record to the log (the Receipt).¶
The implementation does not yet encode COSE, and nothing has been registered with any SCITT Transparency Service. It signs, with pure Ed25519, a canonical JSON payload pinning a record's SHA-256, root(s) and counts, and stops if the signature also verifies over an altered preimage. It submits record digests to OpenTimestamps [OTS] and has recorded the index's and chain head's signed-payload digests in Rekor [REKOR]; neither is a SCITT Transparency Service or issues Receipts. Moving to COSE means re-signing every record, since a COSE signature also covers the protected header.¶
measurement_state is one string from a vocabulary defined per kind.
The states in use are, by family: agreement (CONSISTENT, INCONSISTENT,
UNCHANGED_AT_NAME_GRANULARITY); verification (VERIFIED, FAILED);
reproduction (REPRODUCED_ITEMWISE, REPRODUCED_AGGREGATE_ONLY,
NOT_REPRODUCED); reading (the evidence-ladder labels, MEASURED, PARTIAL,
TOTAL_COMPLETE, TOTAL_PARTIAL); and absence and inability (UNCHECKABLE,
NOT_LISTED, NOT_DECLARED, LISTED_NOT_READ, PERMISSIONED_NOT_READABLE,
ISSUER_LIST_UNAVAILABLE). For every kind:¶
UNCHECKABLE and each absence-and-inability state MUST be recorded as such and MUST NOT be mapped to a pass, failure, zero or omission; a capsule MUST be issued for it when the batch's inclusion rule covers the subject.¶
A state asserting absence (such as NOT_LISTED) MUST be used only where the read that established it was complete; a partial read yields UNCHECKABLE, and a total MUST NOT be recorded over a partial read.¶
INCONSISTENT says two statements disagree, not which is true, and a state MUST NOT be stronger than its evidence.¶
effect_reference, when not null, names a Signed Statement issued under
another profile about the same effect: the profile and revision (for
example draft-schrock-ep-authorization-receipts-13), the digest of the
statement, and optionally where it can be retrieved. The digest MUST be
computed over the octets as registered, since a re-encoded statement has
a different digest under the same signature
[I-D.templeman-scitt-framing-space]. The referenced statement's fields,
in particular its outcome, are not copied. The implementation has not
yet populated this member, so this composition is a proposal, not a
report.¶
A capsule is never edited; any change produces a different capsule. A
correction is a new capsule, in a new batch, whose correction_pointer
records the schema, version and SHA-256 of the source record in which
the change was made, the SHA-256 of the record it supersedes, the state
before (from) and after (to), and the reason, with the fixing commit
where there was one. The superseded capsule remains valid and
retrievable. A change of canonicalisation or Merkle construction changes
identifiers and roots without changing any measurement, and MUST be
published as a new batch, leaving earlier batches in place.¶
sources carries only digests; the evidence stays with whoever holds
it. A digest of a public document is meant to be re-identifiable. A
digest of a non-public, low-entropy artefact can be recovered by
guessing, so Issuers SHOULD NOT place one in sources without blinding,
which the implementation does not provide; its sources are all public
or its own records.¶
subject_id can identify a service run by one person. Issuers SHOULD
measure only surfaces published for machine consumption, SHOULD NOT send
credentials or go beyond discovery (the MCP measurements stop at
initialize and tools/list), and SHOULD offer subjects a route to
request correction or exclusion. Registration is append-only, so a
capsule registered in error can be superseded but not withdrawn;
Issuers SHOULD review batches before registering them.¶
A valid signature on a batch record shows only that the holder of the
key signed those bytes, not that any value is correct, that the
instrument is sound or that the subject endorses the measurement. A
verifier SHOULD recompute every capsule_id, the batch root, the
record and file digests and the signature, and SHOULD reject a batch in
which any capsule carries a forbidden member or value.¶
The implementation signs with one Ed25519 key, held by a signing service
reached by an authenticated request; it is not a threshold key and no
claim is made about hardware protection. Compromise of the key or of the
request credential allows valid signatures over false records,
including a re-signed chain of daily indexes. Such a rewrite is
detectable only against an anchor dated before it (a Bitcoin-attested
timestamp, a Rekor entry or a Receipt), so verifiers SHOULD check
anchors as well as signatures. The key is published through did:web,
so its authenticity rests on DNS and the Web PKI; retired verification
methods SHOULD remain resolvable.¶
An OpenTimestamps calendar commitment shows only that calendars
received a digest; only a proof upgraded to a Bitcoin attestation dates
the bytes. SHA-1 Git commit identifiers in sources are locators on
which no integrity claim rests.¶
The measurer reads from one location at one time, and surfaces that
should agree may be read hours apart (recorded in limitations). A
VERIFIED card signature does not show who controls the key. A measurer
chooses what to measure, and a batch of subjects likely to disagree
would be accurate capsule by capsule and misleading in aggregate; hence
the inclusion rule in batch records, and consumers SHOULD NOT aggregate
state counts across batches with different inclusion rules.¶
This document has no IANA actions. The media types in Section 6 are in the vendor tree and can be registered without IETF action, which the author intends to do before any Signed Statement is registered under them.¶
This section records the status of known implementations of the protocol defined by this specification at the time of posting of this Internet-Draft, and is based on a proposal described in [RFC7942]. The description of implementations in this section is intended to assist the IETF in its decision processes in progressing drafts to RFCs. Please note that the listing of any individual implementation here does not imply endorsement by the IETF. Furthermore, no effort has been spent to verify the information presented here that was supplied by IETF contributors. This is not intended as, and must not be construed to be, a catalog of available implementations or their features. Readers are advised to note that other implementations may exist. RFC Editor: please remove this section before publication.¶
CSOAI measurement capsule builder (Council of AI, contact
nicholas@csoai.org): a prototype Python builder and verifier. Seven
adapters each check the signature and pinned digests of an existing
signed measurement record; one constructor enforces Section 7.1
and the digest-only rule and refuses any capsule whose bytes differ from
an independent RFC 8785 library's output. Licence Apache-2.0; source
published at https://huggingface.co/datasets/csoai/evidence-index/tree/main/source
(commit 10094094). Not implemented: COSE_Sign1, registration,
effect_reference, blinded digests. As of 26 September 2026, each item
checked against the live artefact that day:¶
The index (schema csoai.measurement-capsule-index/0.2) binds 13,184
capsules of schema csoai.measurement-capsule/0.2 in 8 batches of 7
kinds, with index root 85533b833d36f8a4... (in full in the index).
Capsule files, leaf lists and batch records are published under
https://councilof.ai/measurement-capsules/v0.2/; the index, its
signature and timestamp proof are also in the dataset
huggingface.co/datasets/csoai/evidence-index (commit 8be939c5),
as the genesis of a daily chain whose length that day was one.¶
The index's OpenTimestamps proof carries a Bitcoin attestation at block 968674; the batch records' proofs were still calendar commitments. The signed payloads of the index and the chain head are in Rekor at log indexes 2968539665 and 2968539944.¶
A browser verifier at https://councilof.ai/verify-server/ returns
every capsule held about one endpoint and re-derives inclusion; the
MCP tools measurement_index, verify_capsule (which returns a
capsule's audit path) and server_evidence are served at
https://councilof.ai/mcp/.¶
A checker sharing no code with the builder, written by the same organisation, recomputed from the published files all 13,184 identifiers under its own JCS serialiser (every stored line matched byte for byte), all batch roots and the index root, and verified the index and chain-head signatures. It is not the independent implementation the experiment asks for.¶
Capsules by kind, with the largest states: contract_parity 9,148 (CONSISTENT 5,570, INCONSISTENT 2,905, UNCHECKABLE 673; 5,814 distinct endpoints); tool_drift 3,240 (UNCHECKABLE 1,755, UNCHANGED_AT_NAME_GRANULARITY 1,485); cross_ledger_supply 353 (CONSISTENT 101, OPERATOR_API 77, UNCHECKABLE 52, STATE_PROOF_VERIFIED 49, INCONSISTENT 4, 70 in six other states); cross_runtime_reproduction 154 (NOT_REPRODUCED 89, REPRODUCED_ITEMWISE 54, REPRODUCED_AGGREGATE_ONLY 11); self_parity 138 (NOT_LISTED 55, CONSISTENT 37, UNCHECKABLE 37, INCONSISTENT 8, NOT_DECLARED 1); public_signal 118 (MEASURED 103, UNCHECKABLE 12, PARTIAL 3); a2a_card_signature 33 (VERIFIED 13, UNCHECKABLE 12, FAILED 8).¶
296 capsules are self-measurements: the 138 self-parity capsules (self
by kind), 4 contract-parity capsules on the Issuer's own endpoint
(flagged in claim) and the 154 cross-runtime capsules, which re-run
the Issuer's own evaluation cards but carry no explicit flag. Of the 118 public-signal capsules, 50 concern the
Issuer's own packages and 43 cannot separate its activity from others'.
The other 12,770 concern subjects with which the Issuer has no stated
relationship. 12 capsules carry a correction pointer; effect_reference
is null in all. Capsules are 1,175 to 28,005 bytes (median 2,163). An
earlier, unpublished 0.1 grammar used a
non-RFC 9162 tree; its batches were rebuilt under this profile.¶
The experiment asks whether an unaffiliated, authority-free
declared-versus-observed record is useful alongside actor-issued
receipts in a SCITT deployment. It is judged by four outcomes: (1) an
implementation written by another party from this text recomputes the
identifiers and roots of a published batch; (2) a batch is registered
with a Transparency Service operated by another party and its Receipt
is verified by a third; (3) a capsule carries an effect_reference to a
Signed Statement from a different Issuer; (4) the open issues are
resolved and published as corrections without loss of existing batches.
If none occurs within two revisions, the author will withdraw the draft.¶
(1) COSE_Sign1 encoding over a Sig_structure; (2) the superseded
capsule_id in correction_pointer; (3) a per-kind allowlist of member
names in place of the denylist; (4) neutral names for the
csoai.-prefixed identifiers, and per-kind state vocabularies in a
registry or appendix; (5) blinded digests for non-public sources; (6)
whether observation-only kinds such as public_signal belong here; (7)
an explicit self-measurement member, since kind and claim do not mark
every case.¶
The drafts cited in the introduction sharpened the distinction this document draws. Any mischaracterisation of them is the author's.¶