<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.43 (Ruby 3.3.6) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-mih-zhang-agent-disclosure-bundle-02" category="std" consensus="true" submissionType="IETF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.34.1 -->
  <front>
    <title abbrev="AAC Evidence Bundle">AAC Evidence Bundle</title>
    <seriesInfo name="Internet-Draft" value="draft-mih-zhang-agent-disclosure-bundle-02"/>
    <author initials="S." surname="Mih" fullname="Steven Mih">
      <organization>Action State Group, Inc.</organization>
      <address>
        <email>spec@actionstate.ai</email>
      </address>
    </author>
    <author initials="Y." surname="Zhang" fullname="Yiqun Zhang">
      <organization>Independent</organization>
      <address>
        <email>zhangyiqun9164@gmail.com</email>
      </address>
    </author>
    <date year="2026" month="October" day="31"/>
    <area>Security</area>
    <workgroup>SCITT</workgroup>
    <keyword>SCITT</keyword>
    <keyword>evidence</keyword>
    <keyword>AI agent</keyword>
    <keyword>transparency</keyword>
    <keyword>audit</keyword>
    <abstract>
      <?line 92?>

<t>This document defines the AAC Evidence Bundle, a portable presentation and
verification container for an Agent Action Capsule and the records that make
its evidentiary claim intelligible. A permalink carries a bundle in its URL
fragment, an offline HTML report carries a bundle in its shell, and a hosted
report serves a bundle at its URL. The bundle does not alter any enclosed
Capsule. It declares its citation closure and any missing cited records,
carries verified disclosure preimages as a bundle-level overlay, separates
three different completeness claims, can carry the producer's signed
declaration of what it offers in each category of evidence, and permits
independently specified
extension blocks and countersignatures: signatures by a party other than the
producer over the bundle digest, each accompanied by a statement of which
claims that party recomputed and with what result.</t>
    </abstract>
  </front>
  <middle>
    <?line 108?>

<section anchor="introduction">
      <name>Introduction</name>
      <t>A Capsule proves the identity and claimed content of one action. An audit
report normally needs more: the report Capsule, its chain of judgments or
adjudications, the turns cited by those records, selected disclosed preimages,
and the log evidence that supports a claim about coverage. Passing these
items as unrelated files produces a presentation that a verifier cannot
describe precisely.</t>
      <t>The AAC Evidence Bundle is that unit of evidence. It is deliberately a
presentation-layer object: the Capsules in <tt>records</tt> retain their original
bytes, identities, signatures, and registrations. The same bundle can be
encoded in a URL fragment, embedded in an offline HTML shell, or obtained
from a hosted report URL. These are transports for one object, not distinct
evidence formats.</t>
    </section>
    <section anchor="conventions">
      <name>Conventions and Definitions</name>
      <t>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
<xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they appear in all capitals,
as shown here.</t>
      <dl>
        <dt>Bundle:</dt>
        <dd>
          <t>An AAC Evidence Bundle as defined in <xref target="bundle-object"/>.</t>
        </dd>
        <dt>Root Capsule:</dt>
        <dd>
          <t>The Capsule identified by the Bundle's <tt>root</tt> member. The Bundle's evidence
claim is made from this Capsule outward through its declared closure.</t>
        </dd>
        <dt>JSON-DIGEST:</dt>
        <dd>
          <t>The lowercase-hex SHA-256 digest of <tt>UTF8(JCS(value))</tt>, using <xref target="RFC8785"/>
over the whole recursively canonicalized JSON value with no member filtering,
as defined by <xref target="I-D.mih-scitt-agent-action-capsule"/>.</t>
        </dd>
        <dt>Countersignature:</dt>
        <dd>
          <t>A countersignature is a signature by a party other than the producer over a
bundle digest, accompanied by a statement of which claims that party
recomputed and with what result. A report is a bundle.</t>
        </dd>
        <dt>Countersigner:</dt>
        <dd>
          <t>The party that makes a countersignature. A countersigner works in the
manner of an Auditor <xref target="RFC9943"/>: it independently recomputes checks over a
Bundle it was given and signs a statement of the results -- a list of what
it recomputed, never an opinion. A countersigner MAY register that
statement as a Signed Statement with a Transparency Service, in which case
the entry carries the Receipt; the Receipt proves that the statement is in
that log and says nothing about the Bundle. Which producers a countersigner
accepts Bundles from is its own admission rule, outside this document.</t>
        </dd>
        <dt>Self-countersignature:</dt>
        <dd>
          <t>An entry in <tt>countersignatures</tt> whose signer key is the producer's key
(<xref target="self-countersignature"/>). It is well-formed and is rendered as not
independent.</t>
        </dd>
      </dl>
      <t><tt>countersignatures</tt> is a member name. An entry is a statement by a party
other than the producer that it recomputed named checks over the bundle
digest, in the manner of an Auditor <xref target="RFC9943"/>. It is not a COSE
countersignature <xref target="RFC9338"/>, not a Receipt <xref target="RFC9942"/>, and not a
Transparency Service function.</t>
      <dl>
        <dt>WITHHELD:</dt>
        <dd>
          <t>The disclosure state of an eligible member absent from the Bundle-level
disclosure overlay. It is neither an empty value nor a verification failure.</t>
        </dd>
      </dl>
      <t>The terms "Capsule", "capsule_id", <tt>chain.parent_capsule_id</tt>, <tt>references</tt>,
<tt>seq</tt>, <tt>leaf_index</tt>, and <tt>log_coordinates</tt> are as defined in
<xref target="I-D.mih-scitt-agent-action-capsule"/>.</t>
    </section>
    <section anchor="bundle-object">
      <name>Evidence Bundle Object</name>
      <t>A Bundle is a JSON <xref target="RFC8259"/> object with this shape. Ellipses denote values
defined by this document or a registered extension; they do not authorize
untyped replacement formats.</t>
      <artwork><![CDATA[
{
  "bundle_version": "2",
  "bundle_kind": "evidence-bundle/v2",
  "root": "<capsule_id>",
  "records": [ ... ],
  "completeness": {
    "closure_depth": 2,
    "records_mode": "complete|declared_incomplete",
    "payloads_mode": "all|selected|none",
    "suppressed_fields": [ ... ],
    "missing": [ "<cited capsule digest>", ... ],
    "selection": "producer-selected"
  },
  "disclosures": {
    "<capsule_id>": { "<member>": "<revealed preimage>" }
  },
  "disclosure_record": "<capsule_id>",
  "completeness_certificate": { ... },
  "checkpoint": { ... },
  "category_coverage": { ... },
  "verification": { ... },
  "extensions": { "<registered kind>": { ... } },
  "countersignatures": [ ... ]
}
]]></artwork>
      <t><tt>bundle_version</tt> and <tt>bundle_kind</tt> are REQUIRED and MUST be respectively
<tt>"2"</tt> and <tt>"evidence-bundle/v2"</tt>. <tt>root</tt>, <tt>records</tt>, and <tt>completeness</tt> are
REQUIRED, except that an empty-history Bundle (<xref target="empty-history"/>) has no
<tt>root</tt>. <tt>root</tt> is the lowercase-hex <tt>capsule_id</tt> of one record in <tt>records</tt>.
Every <tt>records</tt> element MUST be an unmodified Capsule object conforming to
<xref target="I-D.mih-scitt-agent-action-capsule"/>; duplicate <tt>capsule_id</tt> values are
not permitted. All other members shown are OPTIONAL unless a claim in this
document requires them. A receiver MUST reject an object with an unknown
<tt>bundle_version</tt> or <tt>bundle_kind</tt> as an AAC Evidence Bundle, rather than
silently applying version-2 verification rules.</t>
      <t><tt>payloads_mode</tt> states whether the producer included all payload material it
chose to carry (<tt>all</tt>), only a selected subset (<tt>selected</tt>), or none
(<tt>none</tt>). A Bundle whose <tt>payloads_mode</tt> is <tt>none</tt> carries no <tt>disclosures</tt>
member. <tt>selection</tt>, when present, names the certificate mode of
<tt>completeness_certificate</tt> (<xref target="certificate-modes"/>).
<tt>suppressed_fields</tt> names fields intentionally not carried by the Bundle; it
does not turn an absent disclosure into an empty value or a failure.</t>
      <section anchor="empty-history">
        <name>Empty-History Bundles</name>
        <t>An <em>empty-history Bundle</em> shows that a log stream has no history to disclose.
A stream that was never started and a history that was cleared read the same.
Clearing a history starts a new stream, with a new <tt>log_id</tt>, under the same
key. It never rewrites or truncates the old stream's log, and a checkpoint
that does so is inconsistent with that log's earlier checkpoints.</t>
        <t>An empty-history Bundle has an empty <tt>records</tt> array and no <tt>root</tt> member.
<tt>root</tt> MUST be absent exactly when <tt>records</tt> is empty. In an empty-history
Bundle:</t>
        <ul spacing="normal">
          <li>
            <t><tt>completeness.records_mode</tt> MUST be <tt>complete</tt>, <tt>completeness.missing</tt> MUST
be absent or empty, and <tt>completeness.payloads_mode</tt> MUST be <tt>none</tt>;</t>
          </li>
          <li>
            <t><tt>completeness_certificate</tt>, <tt>disclosures</tt> and <tt>disclosure_record</tt> MUST be
absent;</t>
          </li>
          <li>
            <t><tt>checkpoint</tt>, with <tt>checkpoint.cose</tt>, SHOULD be present when the stream has
a checkpoint: its first checkpoint, or its latest one while it still holds
no record. A stream that was never started has none.</t>
          </li>
        </ul>
        <t>A Bundle with no <tt>root</tt> that is not a well-formed empty-history Bundle is
malformed.</t>
        <t>A verifier reports a well-formed empty-history Bundle as <em>no history</em>. It is
well formed: a verifier MUST NOT report it as failed, and MUST NOT report it
as an absence (<xref target="composed-members"/>). Of its claims (<xref target="completeness"/>), graph
closure passes, since there is nothing to close, and interval coverage and
per-record membership are not established, with the finding <tt>no_records</tt>.
When <tt>checkpoint.cose</tt> verifies, the verifier reports the checkpoint's signed
<tt>timestamp</tt> with the result, as no history since that time. Otherwise it
reports no history without a date.</t>
      </section>
    </section>
    <section anchor="closure">
      <name>Citation Closure</name>
      <t>Citation closure is declared, not assumed. Starting at <tt>root</tt>, a producer
MUST traverse <tt>chain.parent_capsule_id</tt> and every <tt>references[].digest</tt>
transitively through <tt>completeness.closure_depth</tt> edges. The default depth is
2 when the member is absent: report to judgments or adjudications, then to
turns. A producer using a different depth MUST state that non-negative JSON
integer explicitly.</t>
      <t>For each target reached within that depth, the Bundle MUST either supply the
cited Capsule in <tt>records</tt> with a matching identity or list its digest once in
<tt>completeness.missing</tt>. A target MUST NOT be silently dropped. <tt>records_mode</tt>
MUST be <tt>complete</tt> exactly when <tt>missing</tt> is absent or empty, and MUST be
<tt>declared_incomplete</tt> exactly when it is non-empty. A verifier MUST report
the latter as declared incomplete, not as complete and not as an unexplained
failure. A supplied record whose computed <tt>capsule_id</tt> does not match the
chain target or reference digest is not supplied evidence for that target.</t>
      <t>The closure declaration says nothing about records beyond its stated depth.
A verifier MUST report the depth it applied and MUST NOT infer unbounded
ancestry or citation completeness from the presence of a short chain.</t>
    </section>
    <section anchor="disclosures">
      <name>Bundle-Level Disclosures</name>
      <t><tt>disclosures</tt> is a Bundle-level overlay keyed first by the enclosed Capsule's
<tt>capsule_id</tt>, then by the disclosure member name:</t>
      <artwork><![CDATA[
"disclosures": {
  "<capsule_id>": {
    "agent_input": { ... },
    "agent_output": { ... }
  }
}
]]></artwork>
      <t>It has the same eligibility and DE-3 digest rule as the per-Capsule
Disclosure Envelope in
<xref target="I-D.mih-agent-disclosure-envelope"/>. The member name
selects that draft's registered committed-digest path in the specified
Capsule. For every disclosed member, a verifier MUST compute
<tt>JSON-DIGEST(revealed preimage)</tt> and compare it to the committed digest at
that registered path in that <tt>records</tt> element. A match is REVEALED; a
mismatch is a failed verification of that disclosed content. A member absent
from this overlay is WITHHELD, never blank and never failed.</t>
      <t>The overlay is carried once per Bundle rather than by wrapping each Capsule
in <tt>{capsule, disclosures}</tt>. It MUST NOT alter an enclosed Capsule or its
<tt>capsule_id</tt>. An overlay key not naming a supplied record, or a member not
eligible under the Disclosure Envelope registry, is a disclosure finding and
MUST NOT be treated as a successful revelation. <tt>disclosure_record</tt>, when
present, identifies the Capsule that records the act of sealing or issuing
these disclosures; it is evidence about that act and does not replace the
DE-3 check.</t>
    </section>
    <section anchor="completeness">
      <name>Completeness Claims and Log Membership</name>
      <t><tt>completeness_certificate</tt> and <tt>checkpoint</tt>, when supplied, support three
separate claims. A verifier MUST report each claim independently. The
<tt>composed/v1</tt> extension adds a fourth claim, composition closure
(<xref target="composed-closure"/>), which is reported separately from these three.</t>
      <ol spacing="normal" type="1"><li>
          <t><strong>Graph closure</strong>: every citation reached under <xref target="closure"/> is supplied
with matching identity or explicitly listed in <tt>completeness.missing</tt>.</t>
        </li>
        <li>
          <t><strong>Interval coverage</strong>: the claimed sequence interval is bound to a
checkpointed range root. Without a verified Receipt <xref target="RFC9942"/> over the
checkpoint from a Transparency Service, or a verified checkpoint
signature, this is only relative to a producer-asserted checkpoint.</t>
        </li>
        <li>
          <t><strong>Per-record membership</strong>: every supplied record is bound to its claimed
log position under that range root, and, for a range, every other leaf in
the interval is accounted for (<xref target="non-record-leaves"/>).</t>
        </li>
      </ol>
      <t>Per-record membership is the claim a range certificate makes about the
records in its interval: interval coverage binds the leaf values, and per-record
membership says what each leaf is. A verifier MUST ignore members of
<tt>completeness_certificate</tt> that this document does not define; they MUST NOT
change any result it reports.</t>
      <t>The certificate MUST state its <tt>log_id</tt>, range root, first and last <tt>seq</tt>,
and the proof material that binds that range root to <tt>checkpoint</tt>. A signed
range proof that binds only the first and last record proves endpoint
inclusion only: an interior record can be deleted or replaced while such a
proof still passes. A verifier MUST NOT report whole-range completeness from
endpoint inclusion alone. A verifier MUST verify an included Receipt
<xref target="RFC9942"/> over the checkpoint, or the checkpoint signature, before reporting
interval coverage or per-record membership as independently verified.
Otherwise it MUST explicitly label those claims <tt>checkpoint_unverified</tt> and
describe them as relative to a producer-asserted checkpoint.
The portable checkpoint signature member is <tt>checkpoint.cose</tt>, an unpadded
base64url CLL COSE checkpoint. A verifier that implements this member MUST
verify it and require its log identifier, MMR size, and root to equal the
certificate and checkpoint values before removing the qualifier.</t>
      <t>For the third claim, every supplied record MUST either carry a membership entry
within the declared interval or be explicitly listed in <tt>completeness.missing</tt>.
A membership entry or record whose <tt>seq</tt> is outside the interval MUST be
rejected. Each membership entry MUST carry <tt>log_coordinates</tt> stating its
claimed position and an inclusion proof to the certificate's range root. The
base profile's terms apply exactly:
<tt>seq</tt> is the 1-based log position and <tt>leaf_index = seq - 1</tt> is the zero-based
MMR index. <tt>log_coordinates</tt> states which index is used. The verifier MUST
verify every inclusion proof at its stated <tt>leaf_index</tt>, require the proof's
<tt>leaf_index</tt> to equal that coordinate and its <tt>size</tt> to equal
<tt>checkpoint.mmr_size</tt>, and bind the record to
the range root; it MUST NOT substitute an endpoint proof for a missing
per-record proof. A record without a valid proof may remain usable for graph
closure but does not satisfy per-record membership.</t>
      <section anchor="certificate-modes">
        <name>Certificate Modes</name>
        <t><tt>completeness.selection</tt> names the mode in which <tt>completeness_certificate</tt>
is read:</t>
        <ul spacing="normal">
          <li>
            <t><strong>Absent</strong> (the Bundle has no <tt>selection</tt> member): a range certificate, as
described above. Its interval ends at the checkpoint's last leaf.</t>
          </li>
          <li>
            <t><strong><tt>producer-selected</tt></strong>: a producer-selected certificate
(<xref target="producer-selected"/>).</t>
          </li>
        </ul>
        <t>This document defines no other value. A verifier that meets a <tt>selection</tt>
value it does not implement, including a value that is not a string (<tt>null</tt>
among them), MUST
report interval coverage and per-record membership as not established
(finding <tt>certificate_mode_unsupported</tt>). It MUST NOT report either claim as
failed because of the mode, MUST NOT read the certificate under another mode,
and MUST NOT report the Bundle as invalid on that ground. Graph closure,
disclosures, extensions and countersignatures are unaffected.</t>
        <t>The same rule applies to a verifier that predates a mode: a verifier that does
not implement producer-selected certificates reports such a Bundle's interval
coverage and per-record membership as not established, never as failed. A
Bundle in a mode the verifier lacks is unverified in that respect; it is not
evidence of tampering, and reporting it as invalid would make an honest Bundle
indistinguishable from an altered one. The <tt>selection</tt> member is the
producer's to write, and so is any edit to a Bundle after it is made: setting
an unknown mode can turn a failing claim into one that is not established,
but never into one that is established (<xref target="security"/>).</t>
      </section>
      <section anchor="producer-selected">
        <name>Producer-Selected Certificates</name>
        <t>A producer-selected Bundle carries records that its producer chose from a log,
each proven on its own, and makes no claim about the records between them. Its
<tt>completeness.selection</tt> is <tt>producer-selected</tt>. Its certificate carries
<tt>log_id</tt>, <tt>range_root</tt> and <tt>memberships</tt>. It carries no range: <tt>first_seq</tt>,
<tt>last_seq</tt>, <tt>body_digests</tt> and <tt>range_proof</tt> are not part of it, and a
verifier does not read them. It MUST NOT carry <tt>non_record_leaves</tt>
(<xref target="non-record-leaves"/>).</t>
        <artwork><![CDATA[
"completeness": {
  "closure_depth": 0,
  "records_mode": "complete",
  "payloads_mode": "none",
  "suppressed_fields": [],
  "missing": [],
  "selection": "producer-selected"
},
"completeness_certificate": {
  "log_id": "<log identifier>",
  "range_root": "<checkpoint root>",
  "memberships": { "<capsule_id>": { "log_coordinates": { ... },
                                      "inclusion_proof": { ... } } }
}
]]></artwork>
        <t>A verifier that implements this mode:</t>
        <ul spacing="normal">
          <li>
            <t>reports interval coverage as not established (finding
<tt>selection_not_checkable</tt>): there is no interval;</t>
          </li>
          <li>
            <t>verifies per-record membership record by record. <tt>range_root</tt> MUST equal the
checkpoint's root, and the certificate's <tt>log_id</tt> the checkpoint's
<tt>log_id</tt>. Each record's membership entry MUST carry
<tt>log_coordinates</tt> naming the checkpoint's <tt>log_id</tt>, a <tt>seq</tt> of at least 1
that no other entry claims, and <tt>leaf_index = seq - 1</tt>, and an inclusion
proof at that index whose <tt>size</tt> equals <tt>checkpoint.mmr_size</tt> and that binds
the record's <tt>capsule_id</tt> to the root. A membership entry for a record the
Bundle does not carry is a failure. Otherwise the rules of
<xref target="completeness"/> apply, including the <tt>checkpoint_unverified</tt> label;</t>
          </li>
          <li>
            <t>reports a certificate that carries <tt>non_record_leaves</tt> as invalid.</t>
          </li>
        </ul>
        <t>When <tt>checkpoint.cose</tt> does not verify, or a stated value (including the
certificate's <tt>log_id</tt>) contradicts it, the checkpoint anchors no record: the
verifier reports per-record membership as not established, labelled
<tt>checkpoint_unverified</tt>, except that a certificate that carries
<tt>non_record_leaves</tt> still fails it.</t>
        <t>A producer-selected Bundle is therefore never complete as to its log: a
verifier MUST NOT report it as such, and its best result is that every carried
record is a member of the checkpointed log. A changed record or proof fails
per-record membership exactly as in a range Bundle. Graph closure, which is
about citations, not log positions, is unaffected by the mode.</t>
      </section>
      <section anchor="non-record-leaves">
        <name>Non-Record Leaves</name>
        <t>A log can hold leaves that are not records. The Evidence Layer
<xref target="I-D.mih-agent-evidence-layer"/> reserves the <tt>record_type</tt> value <tt>padding</tt>
for records a store appends so that a checkpoint's size falls on a bucket
boundary (its Section 12.1). A store that pads before every checkpoint usually
has a padding leaf at the checkpoint's last leaf, so a range ending there
always covers one. A range certificate covers such leaves in its member
<tt>non_record_leaves</tt>, which carries each one in full:</t>
        <artwork><![CDATA[
"non_record_leaves": [
  { "seq": 6,
    "content": { "capsule_id": "<64 hex>", "record_type": "padding",
                 "epistemic_type": "producer_claim",
                 "store_nonce": "<64 hex>" } }
]
]]></artwork>
        <t><tt>non_record_leaves</tt> is an array. Each element is an object with <tt>seq</tt>, the
leaf's 1-based log position within <tt>[first_seq, last_seq]</tt>, and <tt>content</tt>, the
leaf's content. A digest alone is not accepted in place of <tt>content</tt>: a bare
leaf value could be the identifier of a Capsule that the Bundle thereby hides.
Members of an element other than <tt>seq</tt> and <tt>content</tt> are ignored.</t>
        <t><strong>The padding leaf.</strong> This document defines one non-record leaf type,
padding. The members of a padding record are those of
<xref target="I-D.mih-agent-evidence-layer"/> Section 12.1: <tt>record_type</tt> <tt>padding</tt>,
<tt>epistemic_type</tt> <tt>producer_claim</tt>, and <tt>store_nonce</tt>, 32 bytes as 64 lowercase
hexadecimal characters. That document fixes the members but not their
serialization or the record's identifier, so this document states both for a
padding leaf in a Bundle. Its <tt>content</tt> is a JSON object with exactly four
members: those three and <tt>capsule_id</tt>. <tt>capsule_id</tt> is 64 lowercase
hexadecimal characters, and is the JSON-DIGEST (<xref target="conventions"/>) of the object
formed by the other three members. The leaf value is <tt>capsule_id</tt>. A store's
local-only producer envelope (<tt>signature</tt>, <tt>key_id</tt>) is not part of <tt>content</tt>.
(Moving this rule into the Evidence Layer awaits a later revision of that
document.)</t>
        <t><strong>Checking an element.</strong> A verifier reads the elements in order. For each one:</t>
        <ol spacing="normal" type="1"><li>
            <t>An element that is not an object is malformed (finding
<tt>non_record_leaf_element_invalid</tt>).</t>
          </li>
          <li>
            <t>A <tt>seq</tt> that is not an integer within <tt>[first_seq, last_seq]</tt>, that any
membership entry states in its <tt>log_coordinates</tt> (whether or not that entry
is otherwise valid), or that is the <tt>seq</tt> of an earlier element, is invalid
(finding <tt>non_record_leaf_seq_invalid</tt>); the element accounts for no
position.</t>
          </li>
          <li>
            <t>Otherwise the element accounts for its <tt>seq</tt>. Then:
            </t>
            <ul spacing="normal">
              <li>
                <t>when <tt>content</tt> is not an object, or its <tt>record_type</tt> is not a string,
the element is malformed (finding <tt>non_record_leaf_malformed</tt>);</t>
              </li>
              <li>
                <t>when <tt>record_type</tt> is not <tt>padding</tt>, the leaf is of a type this document
does not define (finding <tt>non_record_leaf_type_unsupported</tt>), and the
verifier checks nothing further about it;</t>
              </li>
              <li>
                <t>when the padding <tt>content</tt> does not have exactly the four members above,
each in the form above, with <tt>epistemic_type</tt> equal to <tt>producer_claim</tt>,
it is malformed (finding <tt>non_record_leaf_malformed</tt>);</t>
              </li>
              <li>
                <t>the verifier recomputes the leaf value from the other three members and
requires it to equal both <tt>content.capsule_id</tt> and
<tt>body_digests[seq - first_seq]</tt>; otherwise the leaf does not match
(finding <tt>non_record_leaf_mismatch</tt>).</t>
              </li>
            </ul>
          </li>
        </ol>
        <t>A <tt>non_record_leaves</tt> value that is not an array is malformed (finding
<tt>non_record_leaves_malformed</tt>) and accounts for no position.</t>
        <t><strong>Per-position accounting.</strong> For per-record membership over a range, every
<tt>seq</tt> in <tt>[first_seq, last_seq]</tt> MUST be accounted for by exactly one of a
record's membership entry and a <tt>non_record_leaves</tt> element. Each position is
then:</t>
        <ul spacing="normal">
          <li>
            <t><strong>verified</strong>: a record's verified membership, or a padding leaf that
recomputes to its body digest;</t>
          </li>
          <li>
            <t><strong>unsupported</strong>: a leaf of a type the verifier does not implement; or</t>
          </li>
          <li>
            <t><strong>unaccounted</strong>: neither, or an element that failed a check above
(finding <tt>membership_record_missing</tt> for a position with neither).</t>
          </li>
        </ul>
        <t>Any failure fails per-record membership, whatever else is unsupported. When
nothing fails and some position is unsupported, per-record membership is not
established: the verifier MUST NOT report it as established, and MUST NOT
report it as failed. Otherwise the rules of <xref target="completeness"/> apply. A
verifier that implements this member therefore no longer rejects a range
because it contains padding records, as <xref target="I-D.mih-agent-evidence-layer"/>
Section 12.1 requires. A verifier that does not implement the member ignores
it, as it ignores any member it does not know, and then reports every padding
position as missing, which fails per-record membership. A producer therefore
SHOULD NOT emit <tt>non_record_leaves</tt> to readers that do not implement this
revision; until it can, a producer-selected certificate states what it can
prove without it.</t>
        <t>A non-record leaf is not a record. It never appears in <tt>records</tt>, never
counts as a record for graph closure, disclosures or any count a verifier
reports, and is never the target of a citation. Leaves other than padding,
such as a store's checkpoint stamps, are not covered by this document. A range
over a log that contains one is reported, at best, as not established through
<tt>non_record_leaf_type_unsupported</tt>; a producer that needs a Bundle over such a
log without that result uses a producer-selected certificate.</t>
      </section>
    </section>
    <section anchor="category-coverage">
      <name>Category Coverage</name>
      <t><tt>category_coverage</tt>, when present, is the producer's signed declaration of
what it offers in each category of a named category set: for each category,
exactly one status. It answers the <tt>categories</tt> field of an Evidence Request
(<xref target="I-D.mih-agent-evidence-request"/>), and a producer MAY include it without
one. <strong>It declares; it does not prove.</strong> A verifier checks that it is well
formed, who signed it, and whether it is consistent with what the Bundle
carries. It never establishes that the producer offers everything it
declares, or that nothing exists in a category declared absent.</t>
      <artwork><![CDATA[
"category_coverage": {
  "set": "<category set identifier>",
  "categories": {
    "<category name>": { "status": "AVAILABLE" },
    "<category name>": { "status": "UNDISCLOSED",
                         "policy_uri": "<absolute URI>" }
  },
  "request_digest": "<64 hex>",
  "signer": { "key_id": "<64 hex>" },
  "signature": "<128 hex>"
}
]]></artwork>
      <t><tt>set</tt> is REQUIRED, a non-empty string naming a category set
(<xref target="iana"/>). A set whose identifier begins <tt>x-</tt> is private: it is not
registered, and a verifier never treats it as known. <tt>categories</tt> is
REQUIRED, a non-empty object keyed by category name; each value is an object
with <tt>status</tt> and, where required, <tt>policy_uri</tt>. <tt>request_digest</tt>,
OPTIONAL, is the digest of the Evidence Request this declaration answers,
computed as <xref target="I-D.mih-agent-evidence-request"/> computes a request digest, in
lowercase hexadecimal. <tt>signer</tt> and <tt>signature</tt> are OPTIONAL, and present
together or not at all. Members not defined here are ignored: they never make
the member malformed, and a signature covers them.</t>
      <t>The member is part of the bundle digest (<xref target="bundle-digest"/>), so a
countersignature binds it.</t>
      <section anchor="category-statuses">
        <name>Statuses</name>
        <t>Each category has exactly one <tt>status</tt>, written in upper case:</t>
        <table>
          <thead>
            <tr>
              <th align="left">status</th>
              <th align="left">Meaning</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td align="left">
                <tt>AVAILABLE</tt></td>
              <td align="left">evidence in this category is offered</td>
            </tr>
            <tr>
              <td align="left">
                <tt>PARTIAL</tt></td>
              <td align="left">only some evidence in this category is offered</td>
            </tr>
            <tr>
              <td align="left">
                <tt>WITHHELD</tt></td>
              <td align="left">evidence in this category may exist and is not released to this requester</td>
            </tr>
            <tr>
              <td align="left">
                <tt>DELETED</tt></td>
              <td align="left">evidence in this category was destroyed or has expired</td>
            </tr>
            <tr>
              <td align="left">
                <tt>ABSENT</tt></td>
              <td align="left">the producer states that it holds nothing in this category</td>
            </tr>
            <tr>
              <td align="left">
                <tt>UNDISCLOSED</tt></td>
              <td align="left">the producer does not state which of the other statuses applies, under the policy <tt>policy_uri</tt> names</td>
            </tr>
          </tbody>
        </table>
        <t><tt>AVAILABLE</tt>, <tt>PARTIAL</tt>, <tt>WITHHELD</tt> and <tt>DELETED</tt> are retention states of the
Evidence Layer <xref target="I-D.mih-agent-evidence-layer"/>, with the same meanings,
applied to a category rather than to one record's payload. This document
defines <tt>UNDISCLOSED</tt> and <tt>ABSENT</tt>. <tt>ABSENT</tt> is the producer's asserted
absence in the Evidence Layer's sense ("Three Kinds of No"): its own unproven
claim about its own store, never a non-membership proof and never the
requester's recorded absence of <xref target="I-D.mih-agent-evidence-request"/>. The
retention state <tt>LEGAL_HOLD</tt> is not a status: it changes what may happen to
evidence, not what is offered.</t>
        <t><tt>UNDISCLOSED</tt> MUST carry <tt>policy_uri</tt>, an absolute URI (<xref target="RFC3986"/>,
Section 4.3) naming the policy under which the status is not disclosed;
<tt>policy_uri</tt> MAY accompany any other status. <tt>WITHHELD</tt> and <tt>UNDISCLOSED</tt> are
different statements and MUST NOT be conflated: <tt>WITHHELD</tt> says evidence may
exist and is not released; <tt>UNDISCLOSED</tt> says nothing about whether any
exists.</t>
      </section>
      <section anchor="category-signature">
        <name>Signature</name>
        <t><tt>signer.key_id</tt> is a 32-byte Ed25519 <xref target="RFC8032"/> public key as 64 lowercase
hexadecimal characters. <tt>signature</tt> is the Ed25519 signature, as 128
lowercase hexadecimal characters, under that key over the signing input</t>
        <artwork><![CDATA[
UTF8(JCS({"category_coverage": <the member without signature>,
          "root": <the Bundle's root>}))
]]></artwork>
        <t>Including <tt>root</tt> binds the declaration to the root record of the Bundle it
was made for: a signed declaration copied into a Bundle with another root
does not verify. It does not bind the rest of the Bundle; a Bundle with the
same root and other records carries it unchanged, and only the bundle digest
(<xref target="bundle-digest"/>) tells the two Bundles apart. The signature
attributes the declaration to the holder of the key. It does not establish
who that holder is; a verifier MAY compare <tt>signer.key_id</tt> with the
producer's keys (<xref target="self-countersignature"/>) and report the result.</t>
      </section>
      <section anchor="category-properties">
        <name>Verifier Properties</name>
        <t>A verifier that implements this member reports the following properties,
each as exactly one of <tt>PASS</tt>, <tt>FAIL</tt>, <tt>NOT_PRESENT</tt>, <tt>NOT_CHECKED</tt> and
<tt>INCONCLUSIVE</tt>, as the definition of each property below assigns them. A
Bundle without the member has no category properties.</t>
        <dl>
          <dt><tt>category_coverage_form</tt>:</dt>
          <dd>
            <t><tt>PASS</tt> when the member is as described above, including a <tt>policy_uri</tt> for
every <tt>UNDISCLOSED</tt> status; otherwise <tt>FAIL</tt>. When it is <tt>FAIL</tt>, every other
property is <tt>NOT_CHECKED</tt>.</t>
          </dd>
          <dt><tt>category_coverage_signature</tt>:</dt>
          <dd>
            <t><tt>NOT_PRESENT</tt> when the member carries no signature; <tt>PASS</tt> when the
signature verifies; <tt>FAIL</tt> otherwise, including when the signing input has
no JCS serialization (for example, a number JCS cannot represent).</t>
          </dd>
          <dt><tt>category_coverage_set_registered</tt>:</dt>
          <dd>
            <t><tt>NOT_PRESENT</tt> for a private (<tt>x-</tt>) set; <tt>INCONCLUSIVE</tt> when the verifier
does not know the set; <tt>FAIL</tt> when it knows the set and a declared category
is not one of its names; <tt>PASS</tt> otherwise.</t>
          </dd>
          <dt><tt>category_coverage_request</tt>:</dt>
          <dd>
            <t><tt>NOT_CHECKED</tt> when the verifier does not have the Evidence Request;
otherwise <tt>FAIL</tt> when <tt>request_digest</tt> is present and is not that
request's digest, computed over its octets before they are parsed;
otherwise <tt>NOT_CHECKED</tt> when the request cannot be read, or its
<tt>categories</tt> field is malformed (<xref target="I-D.mih-agent-evidence-request"/>);
<tt>NOT_PRESENT</tt> when the request has no <tt>categories</tt> field; <tt>FAIL</tt> when the
request names another set, or a requested category has no entry; <tt>PASS</tt>
otherwise.</t>
          </dd>
          <dt><tt>category_coverage_consistency</tt>:</dt>
          <dd>
            <t><tt>NOT_CHECKED</tt> when the verifier cannot assign every record the Bundle
carries to a category, or to none (the set's definition says how): a record
it cannot assign could be in any category; <tt>FAIL</tt> when a record the Bundle
carries belongs to a category declared <tt>ABSENT</tt> or <tt>WITHHELD</tt>; <tt>PASS</tt>
otherwise. A record in a <tt>DELETED</tt> or <tt>UNDISCLOSED</tt> category is not a
contradiction, and an <tt>AVAILABLE</tt> category with no record in the Bundle is
not one either: the Bundle may cover less than the producer offers.</t>
          </dd>
        </dl>
        <t>Each property stands alone. A verifier MUST report them separately and MUST
NOT combine them, or the statuses, into a count, a percentage, a score or a
claim of completeness. It MUST NOT report a <tt>PASS</tt> of every property as proof
that the producer offered everything it holds. A <tt>FAIL</tt> of
<tt>category_coverage_form</tt>, <tt>category_coverage_signature</tt> or
<tt>category_coverage_consistency</tt> is a failed check of the Bundle; the other
properties are reported and change no other result. Category coverage is
reported apart from the completeness claims of <xref target="completeness"/> and changes
none of them.</t>
        <t>A verifier that does not implement this member follows the rule for any
member it does not know: it reports nothing about it, and the member stays
covered by the bundle digest.</t>
      </section>
    </section>
    <section anchor="extensions">
      <name>Typed Extensions</name>
      <t><tt>extensions</tt>, when present, is an object keyed by a registered Evidence Bundle
extension kind. The member name declares the kind and its value is that kind's
block. The extension kind registry is Specification Required. A registered
specification defines its block shape and any semantic checks. For example, a
company-side row model such as <tt>report/v1</tt> is permitted only as such an
extension; this neutral core does not define, parse, or own its rows.</t>
      <t>Extensions are included in the bundle digest (<xref target="bundle-digest"/>). A verifier
that does not implement a registered extension kind MUST preserve its
integrity status as digest-covered, report the block as uninterpreted, and
MUST NOT apply that block's semantics. An unknown kind MUST NOT alter the
core Capsule, closure, disclosure, or membership checks.</t>
      <section anchor="producer-key">
        <name>The producer-key/v1 Extension</name>
        <t>The <tt>producer-key/v1</tt> extension kind declares the key of the producer that
assembled the Bundle:</t>
        <artwork><![CDATA[
"extensions": {
  "producer-key/v1": { "public_key": "<64 lowercase hex>" }
}
]]></artwork>
        <t><tt>public_key</tt> is the producer's 32-byte Ed25519 <xref target="RFC8032"/> public key as 64
lowercase hexadecimal characters. The block carries one key. A producer MUST
NOT add other members to the block, and a verifier MUST ignore any it finds.
Like every extension, the block is covered by the bundle digest, so a
countersignature's <tt>over</tt> binds the declaration, and adding, removing, or
changing the declaration afterwards makes that entry <tt>invalid</tt>.</t>
        <t>A verifier that implements this kind uses <tt>public_key</tt> for one purpose only:
it adds the key to the producer's keys when determining independence
(<xref target="self-countersignature"/>), so that a <tt>countersign/v1</tt> entry whose
<tt>signer.key_id</tt> equals it is reported as <tt>not independent</tt>. The declaration
can only cause an entry to be reported as <tt>not independent</tt>. It MUST NOT
cause any entry to be reported as independent, <tt>resolved</tt>, or valid, and it
MUST NOT change any other result. It is not a claim of identity or of
authority, and a verifier MUST NOT present it as one. The absence of this
extension says nothing about whether any entry is independent.</t>
        <t>The block is malformed when it is not an object, or when <tt>public_key</tt> is
absent, is not a string, or is not exactly 64 lowercase hexadecimal
characters. A verifier MUST ignore a malformed block when determining
independence, MUST NOT fail the Bundle or any claim because of it, and still
treats the block as digest-covered.</t>
      </section>
      <section anchor="composed">
        <name>The composed/v1 Extension</name>
        <t>The <tt>composed/v1</tt> extension kind carries an evidence set built from the
answers of several responders as one portable object. Each responder answered
one Evidence Request (<xref target="I-D.mih-agent-evidence-request"/>) with exactly one of
three outcomes:</t>
        <ul spacing="normal">
          <li>
            <t>an artifact, here an Evidence Bundle;</t>
          </li>
          <li>
            <t>a signed refusal;</t>
          </li>
          <li>
            <t>a recorded absence.</t>
          </li>
        </ul>
        <t>Each answer is a <em>member</em> of the composition. The block declares:</t>
        <ul spacing="normal">
          <li>
            <t>each member's digest and outcome;</t>
          </li>
          <li>
            <t>the observer that produced each member;</t>
          </li>
          <li>
            <t>the joins between members;</t>
          </li>
          <li>
            <t>a <em>composed digest</em> that a judgment or other record can cite as the exact
evidence set it was made from.</t>
          </li>
        </ul>
        <t>The containing Bundle is an ordinary <tt>evidence-bundle/v2</tt>. Its <tt>root</tt> is the
composing party's own record, and its core checks apply unchanged. The
extension adds a set of member bundles alongside that record; it does not
change what <tt>records</tt>, <tt>root</tt> or <tt>completeness</tt> mean.</t>
        <artwork><![CDATA[
"extensions": {
  "composed/v1": {
    "members": [
      { "id": "<member id>", "observer": "<observer id>",
        "request_digest": "<64 hex>", "outcome": "artifact",
        "digest": "<64 hex>", "bundle": { ... } },
      { "id": "<member id>", "observer": "<observer id>",
        "request_digest": "<64 hex>", "outcome": "refusal",
        "digest": "<64 hex>", "refusal": { ... } }
    ],
    "observers": [
      { "id": "<observer id>", "role": "<role>",
        "custody_domain": "<label>" }
    ],
    "joins": [
      { "members": ["<member id>", "<member id>"],
        "basis": "pre_agreed_identifier",
        "pointer": "<JSON Pointer>",
        "identifier_digest": "<64 hex>",
        "compare": ["<JSON Pointer>"],
        "state": "agree" }
    ],
    "not_requested": [ "<participant id>" ],
    "missing": [ "<member id>" ],
    "composed_digest": "<64 hex>"
  }
}
]]></artwork>
        <section anchor="composed-block">
          <name>Block</name>
          <t>The block is a JSON object conforming to this CDDL <xref target="RFC8610"/>:</t>
          <sourcecode type="cddl"><![CDATA[
composed-v1 = {
  members: [+ composed-member],
  observers: [+ composed-observer],
  ? joins: [* composed-join],
  ? not_requested: [* label-id],
  ? missing: [* label-id],
  composed_digest: digest-hex,
}

composed-member = artifact-member / refusal-member / absence-member

member-common = (
  id: label-id,
  observer: label-id,             ; an observers[].id
  request_digest: digest-hex,     ; the Evidence Request this answers
  digest: digest-hex,             ; see "Members and Outcomes"
)

artifact-member = { member-common, outcome: "artifact",
                    ? bundle: { * tstr => any },   ; a Bundle
                    ? response: artifact-response }
refusal-member  = { member-common, outcome: "refusal",
                    ? refusal: signed-refusal }
absence-member  = { member-common, outcome: "absence",
                    ? absence: recorded-absence }

signed-refusal = {                ; as received, unmodified
  request_digest: digest-hex,
  reason: tstr,                   ; Evidence Request refusal reason
  issued_at: tstr,                ; RFC 3339
  * tstr => any,                  ; deployment signature members
}

artifact-response = {             ; as received, minus artifact bytes
  request_digest: digest-hex,
  artifact_digest: digest-hex,
  * tstr => any,                  ; deployment members and signature
}

recorded-absence = {              ; the requester's own record
  request_digest: digest-hex,
  window: { from: tstr, to: tstr },   ; RFC 3339
  ? route: tstr,
  ? commitment: digest-hex,       ; a retention commitment it held
}

composed-observer = {
  id: label-id,
  role: tstr,                     ; see "Observers and Redundancy"
  custody_domain: tstr,           ; opaque, compared octet-for-octet
}

composed-join = {
  members: [label-id, label-id],  ; two distinct ids, ascending
  basis: join-basis,
  ? pointer: tstr,                ; RFC 6901 JSON Pointer
  ? identifier_digest: digest-hex,
  ? compare: [+ tstr],            ; RFC 6901 JSON Pointers
  state: join-state,
}

join-basis = "pre_agreed_identifier" / "shared_artifact_digest" /
             "issued_receipt" / "same_interval" /
             "controller_binding"
join-state = "agree" / "mismatch" / "unjoined" / "one_sided"

label-id   = text .regexp "[A-Za-z0-9._:-]{1,128}"
digest-hex = text .regexp "[0-9a-f]{64}"
]]></sourcecode>
          <t>A join's <tt>pointer</tt> and each <tt>compare</tt> element are JSON Pointers <xref target="RFC6901"/>.
Member <tt>id</tt> values are unique, and so are observer <tt>id</tt> values. Every member's
<tt>observer</tt> names a declared observer. Every join names two distinct declared
members, in ascending order. No two joins share the same <tt>members</tt>, <tt>basis</tt>
and <tt>pointer</tt>. <tt>not_requested</tt> names participants that the composing party
knew of but did not ask. A participant listed there is not a member, and its
identifier MUST NOT also appear as a member <tt>id</tt>. A block that violates any of
these rules is malformed. A verifier reports a malformed block as a failed
<tt>composed/v1</tt> check and MUST NOT report any composition result from it.</t>
        </section>
        <section anchor="composed-members">
          <name>Members and Outcomes</name>
          <t>A member records the outcome of one request to one responder, and nothing
else. The three outcomes are those of <xref target="I-D.mih-agent-evidence-request"/>. They
are never converted into one another:</t>
          <ul spacing="normal">
            <li>
              <t>A refusal member exists only when the responder signed a refusal.</t>
            </li>
            <li>
              <t>A timeout is an <tt>absence</tt>, never a <tt>refusal</tt>.</t>
            </li>
            <li>
              <t>A received refusal is never recorded as an <tt>absence</tt>.</t>
            </li>
            <li>
              <t>A request still inside its waiting window is pending. Pending is not an
outcome, so a pending request MUST NOT be composed as a member.</t>
            </li>
          </ul>
          <t><tt>digest</tt> depends on the outcome:</t>
          <ul spacing="normal">
            <li>
              <t>For <tt>artifact</tt>, it is the member Evidence Bundle's bundle digest
(<xref target="bundle-digest"/>).</t>
            </li>
            <li>
              <t>For <tt>refusal</tt> and <tt>absence</tt>, it is the lowercase hexadecimal SHA-256 of the
UTF-8 JCS <xref target="RFC8785"/> serialization of the refusal or absence object, as
carried.</t>
            </li>
          </ul>
          <t>When the body (<tt>bundle</tt>, <tt>refusal</tt> or <tt>absence</tt>) is carried, a verifier MUST
recompute <tt>digest</tt> from it. For a refusal or absence, it MUST also require the
body's <tt>request_digest</tt> to equal the member's. A refusal's signature is
verified under the deployment's signature profile. A verifier that does not
implement that profile reports the refusal as signature-unverified. It does
not report it as failed, and it does not report it as absent.</t>
          <t>An artifact member MAY carry <tt>response</tt>: the responder's signed response
to the request, as received, with the artifact bytes removed. A response
signature that names the artifact by digest therefore still verifies without
them. When the bundle and <tt>response</tt> are both carried, a verifier MUST require
the response's <tt>request_digest</tt> to equal the member's, and its
<tt>artifact_digest</tt> to equal the member's <tt>digest</tt>. Either mismatch is a failed
<tt>composed/v1</tt> check. This binds the artifact to the request it answered, as a
refusal's or absence's own <tt>request_digest</tt> does. The response's signature is
verified under the deployment's signature profile, exactly as a refusal's is.
A verifier that does not implement that profile reports the response as
signature-unverified, which is neither failed nor absent. <tt>response</tt> is not
part of the composed digest (<xref target="composed-digest"/>).</t>
          <t>An artifact that fails verification is still an <tt>artifact</tt> member. The
verifier reports the failure on that member. It is not a fourth outcome, and
the verifier MUST NOT report it as a successful grant.</t>
          <t>An empty-history Bundle (<xref target="empty-history"/>) is an <tt>artifact</tt> member that
shows no history. It is neither a failure nor an <tt>absence</tt>. A responder whose
history was cleared, or never started, can answer with one.</t>
          <t><strong>Each member keeps its own claims.</strong> A verifier MUST verify every carried
member bundle as an Evidence Bundle under this document. It MUST report that
bundle's graph closure, interval coverage and per-record membership
(<xref target="completeness"/>), and its disclosures and countersignatures, <em>per member</em>.</t>
          <t>The verifier MUST NOT merge one member's claims with another member's, or with
the containing Bundle's. The containing Bundle's claims are about its own
<tt>records</tt> only. A refusal or absence member is not a failed claim: a verifier
reports it as what it is.</t>
        </section>
        <section anchor="composed-closure">
          <name>Composition Closure</name>
          <t>A <tt>composed/v1</tt> block makes a fourth completeness claim, <em>composition
closure</em>. It holds when every member is either:</t>
          <ul spacing="normal">
            <li>
              <t>present, meaning its body is carried and reproduces its <tt>digest</tt>; or</t>
            </li>
            <li>
              <t>declared missing, meaning its <tt>id</tt> is listed in <tt>missing</tt> and it carries no
body.</t>
            </li>
          </ul>
          <t>The claim has three results:</t>
          <ul spacing="normal">
            <li>
              <t><strong><tt>pass</tt></strong>: every member is present and <tt>missing</tt> is absent or empty.</t>
            </li>
            <li>
              <t><strong><tt>withheld</tt></strong> (finding <tt>declared_incomplete</tt>): every member is present or
declared missing, and at least one is declared missing.</t>
            </li>
            <li>
              <t><strong><tt>fail</tt></strong>: in any of these cases:
              </t>
              <ul spacing="normal">
                <li>
                  <t>a member neither carries a body nor is listed in <tt>missing</tt>;</t>
                </li>
                <li>
                  <t>a listed member carries a body;</t>
                </li>
                <li>
                  <t>a carried body does not reproduce its <tt>digest</tt>;</t>
                </li>
                <li>
                  <t><tt>missing</tt> names an undeclared member.</t>
                </li>
              </ul>
            </li>
          </ul>
          <t>A verifier MUST report composition closure separately from the three
per-member claims and from the containing Bundle's claims. It MUST NOT report
it as one of them.</t>
          <t>Declaring a member missing withholds its body, not its existence. Its <tt>id</tt>,
<tt>observer</tt>, <tt>request_digest</tt>, <tt>outcome</tt> and <tt>digest</tt> remain in the composed
digest.</t>
          <t>Composition closure covers declared members only. A participant that is in
neither <tt>members</tt> nor <tt>not_requested</tt> is not visible to a verifier from this
block (<xref target="composed-security"/>).</t>
        </section>
        <section anchor="composed-binding">
          <name>Controller Binding Records</name>
          <t>A <em>controller binding record</em> is a JSON object in which the holders of two
signing keys both sign one statement about how the two keys are related. It is the linkage evidence of the <tt>controller_binding</tt> join
basis (<xref target="composed-joins"/>). Its signatures show only that whoever held each
private key signed the record. They do not show who holds either key, and
this document never states who a key holder is.</t>
          <t>A binding record is not a member of the <tt>composed/v1</tt> block. It is delivered
beside the containing Bundle, with any revocation statements that withdraw it;
how they are delivered is outside this document. The block binds a record
only through a join's <tt>identifier_digest</tt>.</t>
          <t>In this section a <em>key identifier</em> is 64 lowercase hexadecimal characters;
for an Ed25519 <xref target="RFC8032"/> key, the one key type this document defines, it is
the raw 32-byte public key. A <em>time</em> is an <xref target="RFC3339"/> date-time in
UTC with a "Z" suffix and seconds precision. A root record's <tt>timestamp</tt>,
against which a binding is checked, can also carry fractional seconds. Times
compare as instants.</t>
          <sourcecode type="cddl"><![CDATA[
controller-binding = {
  binding_version: "controller-binding/0",
  claim: binding-claim,
  method: tstr,                  ; a verification method token
  keys: [key-id, key-id],        ; two distinct keys, ascending
  issued_at: binding-time,       ; the ceremony, as stated
  not_before: binding-time,      ; not later than issued_at
  not_after: binding-time,       ; later than issued_at
  revocation: "revocation_statement/0",
  nonce: tstr,                   ; non-empty, chosen per ceremony
  sigs: [* binding-sig],
  ? key_types: [tstr, tstr],     ; one per entry of keys, in order
  ? delegator: key-id,           ; delegated_by only: one of keys
  ? credential: binding-credential,
                                 ; same_organization_credentialed
}

binding-claim = "same_controller_asserted" / "delegated_by" /
                "same_organization_credentialed"
binding-sig = { key_id: key-id, sig: any }   ; sig-hex for ed25519
binding-credential = {
  issuer: key-id,
  method: tstr,
  digest: digest-hex,
}

binding-revocation = {
  revocation_version: "controller-binding-revocation/0",
  binding: digest-hex,           ; the binding digest
  key_id: key-id,                ; one of the binding's keys
  revoked_at: binding-time,
  sig: sig-hex,
}

key-id       = text .regexp "[0-9a-f]{64}"
sig-hex      = text .regexp "[0-9a-f]{128}"
binding-time = text .regexp
  "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z"
]]></sourcecode>
          <t>The claim types, the member each adds, the method this document defines for
each, and what the two key holders state:</t>
          <ul spacing="normal">
            <li>
              <t><tt>same_controller_asserted</tt>: no added member; <tt>dual_signature/0</tt>. One
controller holds both keys.</t>
            </li>
            <li>
              <t><tt>delegated_by</tt>: adds <tt>delegator</tt>; <tt>dual_signature/0</tt>. The holder of
<tt>delegator</tt> delegated to the holder of the other key, who accepted.</t>
            </li>
            <li>
              <t><tt>same_organization_credentialed</tt>: adds <tt>credential</tt>;
<tt>dual_signature_credential/0</tt>. Both keys are held within one organization,
as a credential from a third key states.</t>
            </li>
          </ul>
          <t>A binding record is malformed when it lacks a member, carries a member the
CDDL does not list, or has a value outside its form; when it carries
<tt>delegator</tt> and its <tt>claim</tt> is not <tt>delegated_by</tt>, or <tt>credential</tt> and its
<tt>claim</tt> is not <tt>same_organization_credentialed</tt>, or lacks the member its
<tt>claim</tt> adds; when its two <tt>keys</tt> are equal or not in ascending order; when
<tt>not_before</tt> is later than <tt>issued_at</tt> or <tt>not_after</tt> is not later than it;
when <tt>nonce</tt> is empty; when <tt>delegator</tt> is not one of <tt>keys</tt>; when both keys
are <tt>ed25519</tt> keys and a <tt>sig</tt> is not a <tt>sig-hex</tt>; or when its <tt>method</tt> is one
this document defines for another claim type. A <tt>method</tt> token this document does not define is well formed. A
binding record or revocation statement that is not a JSON object is
malformed. <tt>delegated_by</tt> states a relation, not a scope: a verifier reads no
authority from it.</t>
          <t><strong>Key types.</strong> <tt>key_types</tt>, when present, names the type of each key in
<tt>keys</tt>, in its order. When it is absent, both keys are <tt>ed25519</tt> keys. This
document defines one key type, <tt>ed25519</tt>: the key identifier is the raw
Ed25519 public key, and the key signs with Ed25519. A record whose
<tt>key_types</tt> names any other token is well formed. Its <tt>sig</tt> values are not
held to <tt>sig-hex</tt>, and a verifier that does not implement that key type
treats the record as under a method it does not implement.</t>
          <t><strong>Signing input and binding digest.</strong> The signing input is the UTF-8 octets of
the JCS <xref target="RFC8785"/> serialization of the record without <tt>sigs</tt>. Every other
member is covered. The <em>binding digest</em> is the lowercase hexadecimal SHA-256
of the signing input. It does not cover <tt>sigs</tt>, so a binding keeps its digest
if a key signs it again. Two ceremonies over the same keys differ in <tt>nonce</tt>
and so have two digests.</t>
          <t><strong>Methods.</strong> Under <tt>dual_signature/0</tt>, <tt>sigs</tt> has exactly two entries:
<tt>sigs[0].key_id</tt> is <tt>keys[0]</tt>, <tt>sigs[1].key_id</tt> is <tt>keys[1]</tt>, and each <tt>sig</tt>
is that key's signature over the signing input; for an <tt>ed25519</tt> key, its
Ed25519 signature in lowercase hexadecimal. A record with one signature, a third entry, or a signature that
does not verify is not signed by both keys. One key cannot bind another key
to itself, because each key signs the other key's identifier.
<tt>dual_signature_credential/0</tt> requires the same two signatures. Its
<tt>credential</tt> names a signed object from the key <tt>credential.issuer</tt>, delivered
beside the record, whose SHA-256 is <tt>credential.digest</tt> and which is verified
by its native method <tt>credential.method</tt>. This document registers no
credential format. A credential never decides whether a binding applies, and
a verifier MUST NOT present the organization it names as an identity.</t>
          <t><strong>Revocation.</strong> Either key holder can withdraw a binding with a revocation
statement (<tt>binding-revocation</tt>). A statement <em>applies</em> to a binding when it
is well formed, <tt>binding</tt> is the binding digest, <tt>key_id</tt> is one of the
binding's <tt>keys</tt>, and <tt>sig</tt> is that key's Ed25519 signature over the UTF-8
JCS serialization of the statement without <tt>sig</tt>. A statement names a
binding by digest, so it reaches every copy of the record. From <tt>revoked_at</tt>
on, the binding is not in effect. <tt>revoked_at</tt> is the revoking key holder's
statement; a backdated revocation returns the joins it covered to unjoined,
never further. A verifier knows only the statements it received.</t>
          <t><strong>When a binding applies.</strong> A binding record <em>applies</em> to two key identifiers
at a set of times T when all of these hold:</t>
          <ol spacing="normal" type="1"><li>
              <t>it is well formed;</t>
            </li>
            <li>
              <t>the verifier implements its <tt>method</tt> and the type of both keys, and both
bound keys signed it under that method;</t>
            </li>
            <li>
              <t>its <tt>keys</tt> are exactly the two key identifiers, in either order;</t>
            </li>
            <li>
              <t>for every T, <tt>not_before</tt> &lt;= T &lt; <tt>not_after</tt>;</t>
            </li>
            <li>
              <t>no revocation statement the verifier received that applies to it has a
<tt>revoked_at</tt> at or before any T.</t>
            </li>
          </ol>
          <t><strong>What a binding never states.</strong> A binding is a statement by both key holders
that their two keys are not independent issuers. A verifier MUST NOT present
a binding record as establishing who controls either key; a person, name,
account, organization identity or legal relationship; that the keys were
under one control at any time, beyond the window and claim both key holders
stated; or that two keys with no binding between them have distinct
controllers.</t>
        </section>
        <section anchor="composed-joins">
          <name>Joins</name>
          <t>A join declares that two members' records are about the same thing, and how
that is established. The producer DECLARES a state; a verifier DERIVES it
again from the carried members. A declaration that does not match the
derivation is a failed <tt>composed/v1</tt> check (finding <tt>join_state_mismatch</tt>). It
is never a pass.</t>
          <t>The states derive as follows:</t>
          <ul spacing="normal">
            <li>
              <t><strong><tt>one_sided</tt></strong>: exactly one of the two members has outcome <tt>artifact</tt>
with records, and the other has none: it is not an artifact, or it is an
empty-history Bundle (<xref target="empty-history"/>). Under <tt>controller_binding</tt>, a
join with an empty-history member is <tt>one_sided</tt> only when the two are
linked (below).</t>
            </li>
            <li>
              <t><strong><tt>unjoined</tt></strong>: neither member has outcome <tt>artifact</tt> with records; or one
has records, the other is an empty-history Bundle, and the
<tt>controller_binding</tt> linkage does not link them; or both have records, but
the linkage basis does not establish that their records are about the same
thing.</t>
            </li>
            <li>
              <t><strong><tt>agree</tt></strong>: both members are artifacts with records and are linked, and
every <tt>compare</tt> pointer resolves in both root records to values with
identical JCS serializations. A join with no <tt>compare</tt> and a link is
<tt>agree</tt>.</t>
            </li>
            <li>
              <t><strong><tt>mismatch</tt></strong>: both members are artifacts with records and are linked, and
at least one <tt>compare</tt> pointer resolves to different values, or does not
resolve on one side.</t>
            </li>
          </ul>
          <t>Linkage is evaluated on each artifact member's root record (its bundle's
<tt>root</tt>). This document defines the derivation for three bases:</t>
          <ul spacing="normal">
            <li>
              <t><strong><tt>pre_agreed_identifier</tt></strong>: <tt>pointer</tt> selects a string in each root record.
The members are linked when SHA-256 of that string's UTF-8 octets equals
<tt>identifier_digest</tt> on both sides. The identifier itself never enters the
block.</t>
            </li>
            <li>
              <t><strong><tt>shared_artifact_digest</tt></strong>: <tt>pointer</tt> selects a digest string in each root
record. The members are linked when the two values are identical.</t>
            </li>
            <li>
              <t><strong><tt>controller_binding</tt></strong>: <tt>pointer</tt> selects a key identifier string in each
root record, for example <tt>/key_id</tt>, and <tt>identifier_digest</tt> is a binding
digest (<xref target="composed-binding"/>). The members are linked when a delivered
binding record whose binding digest equals <tt>identifier_digest</tt> applies to
the two selected values, with T the <tt>timestamp</tt> of each root record
(<xref target="I-D.mih-scitt-agent-action-capsule"/>). A verifier that received no
record with that digest, or only well-formed records with that digest whose
<tt>method</tt> or key types it does not implement, reports the join as
<tt>not_derivable</tt>; so does a join without <tt>pointer</tt> or <tt>identifier_digest</tt>. Any other record with
that digest that does not apply (malformed, not signed by both keys, other
keys, outside its window, or revoked) leaves the members unjoined. A join on this basis states that the
two root records are under keys whose holders bound them, as both of them
stated it; it says nothing about who those holders are.  </t>
              <t>
When one member is an empty-history Bundle (<xref target="empty-history"/>), the
derivation still runs. That member's key identifier is the key that signed
its <tt>checkpoint.cose</tt>, as that signature authenticates it, and its T is
that checkpoint's <tt>timestamp</tt>; <tt>pointer</tt> selects the key identifier in the
other member's root record only. Linked gives <tt>one_sided</tt>, never <tt>agree</tt>,
because nothing was compared; not linked gives <tt>unjoined</tt>. An empty-history
member without a <tt>checkpoint.cose</tt> that verifies has no key, and the join is
<tt>not_derivable</tt>.</t>
            </li>
          </ul>
          <t>The bases <tt>issued_receipt</tt> (the two records cite one Receipt) and
<tt>same_interval</tt> (the two records fall in one declared interval) are reserved
for a later specification that defines their derivation. A block MAY declare
joins on them; such joins are digest-covered. A verifier that does not
implement a derivation for the basis reports such a join as <tt>not_derivable</tt>.
It MUST NOT report the join as a derived agreement, and MUST NOT count it as
corroboration.</t>
          <t>A join one of whose artifact members is declared missing is also
<tt>not_derivable</tt>.</t>
          <t>On a derived <tt>mismatch</tt>, a verifier reports each member's value at each
differing pointer, and names no winner.</t>
        </section>
        <section anchor="composed-observers">
          <name>Observers and Redundancy</name>
          <t><tt>observers[]</tt> states, for each member, which observer produced it, in what
<tt>role</tt>, and in which <tt>custody_domain</tt>. <tt>role</tt> is a short token naming where
the observer sits, for example <tt>responder</tt>, <tt>runtime</tt> or <tt>network_boundary</tt>; this document does
not define a role vocabulary, and a verifier reports the role without
interpreting it. A custody domain is an opaque label that the composing party
assigns. Two observers are in the same custody domain exactly when their
labels are identical octet sequences.</t>
          <t><strong>Same custody is redundant, not corroborating.</strong> For every join that derives
<tt>agree</tt>, a verifier MUST report the pair as <tt>redundant</tt> in any of these cases:</t>
          <ul spacing="normal">
            <li>
              <t>both members name the same observer;</t>
            </li>
            <li>
              <t>the two observers have the same <tt>custody_domain</tt>;</t>
            </li>
            <li>
              <t>the two members' root records carry the same <tt>key_id</tt>;</t>
            </li>
            <li>
              <t>the join's basis is <tt>controller_binding</tt>: the holders of the two keys have
stated that the keys are not independent issuers (<xref target="composed-binding"/>).</t>
            </li>
          </ul>
          <t>It MUST present the result as "redundant, not corroborating", with the reason.
A <tt>controller_binding</tt> join therefore never yields corroboration.
Otherwise it MAY report the pair as corroborating, and it MUST qualify that as
resting on <em>declared</em> custody.</t>
          <t>Custody labels are the composing party's declaration. Like <tt>producer-key/v1</tt>
(<xref target="producer-key"/>), they can only downgrade a result. Identical labels make a
pair redundant. Distinct labels never establish that two observers are
independent, and a verifier MUST NOT present them as doing so.</t>
          <t>Corroboration is derived; it is never declared, and it is not part of the
block. A verifier lists results per pair. It MUST NOT combine them into a
count or score.</t>
        </section>
        <section anchor="composed-digest">
          <name>Composed Digest</name>
          <t>The composed digest is the digest a judgment cites as the evidence set it was
made from. It is computed only from the declarations in the block, so it
recomputes from the containing Bundle alone. It stays the same whether or not
the member bodies are carried.</t>
          <ol spacing="normal" type="1"><li>
              <t>Build the <em>digest input</em> <tt>D</tt>, a JSON object with exactly these five
members:
              </t>
              <ul spacing="normal">
                <li>
                  <t><tt>kind</tt>: the string <tt>"composed/v1"</tt>.</t>
                </li>
                <li>
                  <t><tt>members</tt>: for each member, an object with exactly its <tt>id</tt>, <tt>observer</tt>,
<tt>request_digest</tt>, <tt>outcome</tt> and <tt>digest</tt>. Sort the objects in ascending
order of <tt>id</tt>.</t>
                </li>
                <li>
                  <t><tt>observers</tt>: for each observer, an object with exactly its <tt>id</tt>, <tt>role</tt>
and <tt>custody_domain</tt>. Sort in ascending order of <tt>id</tt>.</t>
                </li>
                <li>
                  <t><tt>joins</tt>: for each join, an object with its <tt>members</tt>, <tt>basis</tt> and
<tt>state</tt>, plus whichever of <tt>pointer</tt>, <tt>identifier_digest</tt> and <tt>compare</tt>
the join carries (absent members stay absent, never <tt>null</tt>). Sort in
ascending order of <tt>members[0]</tt>, then <tt>members[1]</tt>, then <tt>basis</tt>, then
<tt>pointer</tt>, where a join without <tt>pointer</tt> sorts before every join with
one, including a <tt>pointer</tt> that is the empty string. <tt>compare</tt> keeps
its carried order. Use <tt>[]</tt> when the block has no <tt>joins</tt>.</t>
                </li>
                <li>
                  <t><tt>not_requested</tt>: the block's <tt>not_requested</tt> sorted ascending, or <tt>[]</tt>
when it has none.</t>
                </li>
              </ul>
              <t>
Identifiers are ASCII (<tt>label-id</tt>), so ascending order is octet order.
<tt>pointer</tt> values, which may hold any character, compare by the octets of
their UTF-8 encoding. The order in which arrays are carried does not affect
the digest.</t>
            </li>
            <li>
              <t><tt>composed_digest</tt> is the lowercase hexadecimal SHA-256 of the UTF-8 octets
of the JCS <xref target="RFC8785"/> serialization of <tt>D</tt>. This is the canonicalization
algorithm <tt>jcs</tt> of <xref target="I-D.mih-sokolov-scitt-payload-binding"/>: RFC 8785 with
no normalization pass, SHA-256, bare hex. <tt>D</tt> contains no numbers.</t>
            </li>
            <li>
              <t>Member bodies, each member's <tt>response</tt>, <tt>missing</tt> and <tt>composed_digest</tt>
itself are excluded.</t>
            </li>
          </ol>
          <t>A verifier MUST recompute <tt>composed_digest</tt> and report a mismatch as a failed
<tt>composed/v1</tt> check. It MUST NOT report the recomputed value as the digest of
any member, nor as the containing Bundle's digest.</t>
          <t>The containing Bundle's own digest (<xref target="bundle-digest"/>) still covers the whole
block, bodies included. A countersignature over the containing Bundle
therefore binds the composition and everything carried with it. The composed
digest binds only the evidence set.</t>
          <t>Because the composed digest covers each member's <tt>outcome</tt>, <tt>observer</tt> and
<tt>digest</tt>, all observers' roles and custody labels, and every join
declaration, changing any of them changes the composed digest. A judgment
that cites a composed digest therefore also binds the declared custody, and
with it the redundancy result a verifier derives from that custody.</t>
        </section>
        <section anchor="composed-verify">
          <name>Verifying composed/v1</name>
          <t>A verifier that implements <tt>composed/v1</tt> MUST report each of the following
separately:</t>
          <ol spacing="normal" type="1"><li>
              <t>the recomputed composed digest, and whether it matches;</t>
            </li>
            <li>
              <t>per member: outcome, body carried or declared missing, digest reproduced,
and, for a carried member bundle, its own three completeness claims, no
history for an empty-history Bundle (<xref target="empty-history"/>), and, when it
carries <tt>response</tt>, whether the response's digests match and its
signature state;</t>
            </li>
            <li>
              <t>composition closure;</t>
            </li>
            <li>
              <t>per join: the declared state, the derived state (or <tt>not_derivable</tt>), and
whether they match;</t>
            </li>
            <li>
              <t>per derived <tt>agree</tt>: <tt>redundant</tt> (with reason) or corroborating on
declared custody.</t>
            </li>
          </ol>
          <t>A verifier that does not implement <tt>composed/v1</tt> follows <xref target="extensions"/>: it
reports the block as uninterpreted and integrity-covered. It MUST NOT present
any member, join or composed digest as checked.</t>
        </section>
      </section>
      <section anchor="provisional-kinds">
        <name>Provisional Extension Kinds</name>
        <t>The interim registry (<xref target="iana"/>) also records two extension kinds held for
ratification and not registered: <tt>sd-jwt-issuers/v1</tt>, the issuer keys under
which SD-JWT presentations <xref target="RFC9901"/> in a revealed <tt>agent_input</tt> are
verified offline, and <tt>disclosure-policy-decisions/v1</tt>, the per-Capsule
disclosure-policy decision that a constraint record binds by digest. This
document does not define them. A verifier that does not implement them treats
them as it treats any extension kind it does not implement (<xref target="extensions"/>):
digest-covered and uninterpreted.</t>
      </section>
    </section>
    <section anchor="bundle-digest">
      <name>Bundle Digest and Countersignatures</name>
      <t>The bundle digest is <tt>SHA-256(UTF8(JCS(canonical bundle form)))</tt>, rendered as
64 lowercase hexadecimal characters. The canonical bundle form is the Bundle
with <tt>countersignatures</tt> omitted. <tt>countersignatures</tt> is excluded so that
independent parties can add signatures without changing the digest they sign;
every other present Bundle member, including <tt>extensions</tt>, participates.</t>
      <t><tt>countersignatures</tt>, when present, is an array of objects. Each object MUST
carry <tt>type</tt> and the type's signature encoding, and signs the bundle digest.
An object whose signer is a party other than the producer is a
countersignature (<xref target="conventions"/>). This document does not define who
populates the slot or whether a relying party should rely on a given signer.</t>
      <t>The registered types are <tt>cose-sign1</tt> and <tt>countersign/v1</tt>. A <tt>cose-sign1</tt>
object is <tt>{type: "cose-sign1", signature: "&lt;base64url tagged COSE_Sign1&gt;"}</tt>.
It is a reserved neutral slot whose population and verification are future
scope; a current verifier MUST surface each such entry as <tt>unverified</tt> and
MUST NOT present pass-through data as a verified countersignature. The
<tt>countersign/v1</tt> object is defined in <xref target="countersign-entry"/>. A verifier MUST
report an entry of a type it does not implement as <tt>unverified</tt> and MUST NOT
let it change any other result.
An entry with no <tt>type</tt> member is the legacy form of <tt>countersign/v1</tt>.
A verifier MUST process it as <tt>countersign/v1</tt>, and its signing input then
uses the value <tt>countersign/v1</tt> for <tt>type</tt>. A producer MUST NOT emit the
legacy form.</t>
      <section anchor="countersign-entry">
        <name>The countersign/v1 Entry</name>
        <artwork><![CDATA[
{
  "type": "countersign/v1",
  "signer": { "id": "<signer identifier>", "key_id": "<64 hex>" },
  "over": "<bundle digest>",
  "statement": {
    "checks": [ {"name": "<check>", "result": "<result>"}, ... ],
    "recomputed_at": "<RFC 3339 UTC timestamp>",
    "scope": {
      "ledger_id": "<ledger identifier>",
      "period": { "from": "<RFC 3339>", "to": "<RFC 3339>" },
      "closure_depth": 2
    }
  },
  "signature": "<128 hex>",
  "receipt": { ... }
}
]]></artwork>
        <t><tt>type</tt>, <tt>signer</tt>, <tt>over</tt>, <tt>statement</tt>, and <tt>signature</tt> are REQUIRED;
<tt>receipt</tt> is OPTIONAL.</t>
        <t><tt>signer.id</tt> identifies the countersigner, for example a <tt>did:web</tt>
identifier. <tt>signer.key_id</tt> is the countersigner's
32-byte Ed25519 <xref target="RFC8032"/> public key as 64 lowercase hexadecimal
characters: the key itself, not a digest of it, so that a verifier holding
only the entry can check the signature offline.</t>
        <t><tt>over</tt> is the bundle digest (<xref target="bundle-digest"/>) of the Bundle the
countersigner recomputed.</t>
        <t><tt>statement</tt> says what the countersigner recomputed and with what result.</t>
        <ul spacing="normal">
          <li>
            <t><tt>checks</tt> is a non-empty array. Each element names one check in <tt>name</tt>, a
short phrase for what was recomputed, and gives its <tt>result</tt>, which is
exactly one of the five values below. A check name is unique within a
statement.</t>
          </li>
          <li>
            <t><tt>recomputed_at</tt> is the <xref target="RFC3339"/> UTC time, with a "Z" suffix, at which
the countersigner finished recomputing.</t>
          </li>
          <li>
            <t><tt>scope.ledger_id</tt> identifies the ledger whose records the Bundle carries, as
the issuer identified it to the countersigner. <tt>scope.closure_depth</tt> is the
closure depth the countersigner applied (<xref target="closure"/>). <tt>scope.period</tt>, when
present, is the <tt>{from, to}</tt> interval of <xref target="RFC3339"/> timestamps the checks
covered; it is absent when the Bundle declares no such interval.</t>
          </li>
        </ul>
        <table>
          <thead>
            <tr>
              <th align="left">result</th>
              <th align="left">Meaning</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td align="left">established</td>
              <td align="left">The check ran and its condition held.</td>
            </tr>
            <tr>
              <td align="left">failed</td>
              <td align="left">The check ran and its condition did not hold.</td>
            </tr>
            <tr>
              <td align="left">not present</td>
              <td align="left">The Bundle carries nothing this check operates on.</td>
            </tr>
            <tr>
              <td align="left">not checked</td>
              <td align="left">The check could not run, for example because there was nothing to compare against.</td>
            </tr>
            <tr>
              <td align="left">inconclusive</td>
              <td align="left">The check ran but resolved only in part.</td>
            </tr>
          </tbody>
        </table>
        <t>Each result stands alone. A statement MUST NOT carry an aggregate result, a
score, a rating, or a claim of compliance, and a verifier MUST NOT derive one
from the checks. Capture coverage, meaning whether every action that occurred
was recorded, cannot be recomputed from the records a Bundle carries; a
statement MUST NOT report it as a check result. Profile identifiers naming a
recomputation policy, when a later revision adds them, use the
<tt>countersign.</tt> prefix; this document defines none.</t>
        <t><tt>signature</tt> is the Ed25519 signature, as 128 lowercase hexadecimal
characters, over the signing input
<tt>UTF8(JCS({"over": over, "signer": signer, "statement": statement, "type": type}))</tt>. The
signature binds the Bundle, the signer's identity and what the countersigner
says about it; <tt>signer</tt> is the entry's <tt>signer</tt> object exactly as it appears
on the wire.</t>
        <t><tt>receipt</tt>, when present, is a Receipt <xref target="RFC9942"/> showing the statement was
registered with a Transparency Service, in that service's receipt encoding. A verifier that cannot verify it MUST report it as unverified; its
absence does not invalidate the entry.</t>
        <t>A verifier MUST ignore entry members not defined here, and they MUST NOT
change any result it reports.</t>
      </section>
      <section anchor="countersign-verify">
        <name>Verifying a countersign/v1 Entry</name>
        <t>A verifier MUST, for each <tt>countersign/v1</tt> entry:</t>
        <ol spacing="normal" type="1"><li>
            <t>recompute the bundle digest and compare it to <tt>over</tt>. An entry whose <tt>over</tt>
differs is not a countersignature of this Bundle and MUST be reported as
<tt>invalid</tt>;</t>
          </li>
          <li>
            <t>verify <tt>signature</tt> over the signing input under <tt>signer.key_id</tt>. An entry
whose signature fails is invalid, and its checks MUST NOT be displayed as
results;</t>
          </li>
          <li>
            <t>determine independence as in <xref target="self-countersignature"/>.</t>
          </li>
        </ol>
        <t>A verifier MAY resolve <tt>signer.key_id</tt> against a directory of countersigners
that it or the relying party chooses, to put a name to the key. No directory
is privileged, and this document defines none. A verifier reports each
<tt>countersign/v1</tt> entry as exactly one of <tt>invalid</tt>, <tt>not independent</tt>,
<tt>unresolved signer</tt> (independent, and the key is in no directory consulted),
or <tt>resolved</tt> (independent, and the key is found). It MUST attribute each
check and result to the signer, as the signer's statement, and MUST NOT
present them as its own findings.</t>
      </section>
      <section anchor="self-countersignature">
        <name>Self-Countersignature</name>
        <t>A countersignature is a self-countersignature when <tt>signer.key_id</tt> equals the
producer's key: the <tt>kid</tt> of a Producer Envelope over any record in <tt>records</tt>
(<xref target="I-D.mih-scitt-agent-action-capsule"/>), the <tt>public_key</tt> of a well-formed
<tt>producer-key/v1</tt> extension (<xref target="producer-key"/>), or any other key the verifier
holds for that producer. A self-countersignature is a well-formed entry that does
not meet the definition of a countersignature, because its signer is the
producer. A verifier MUST NOT reject it for being one, and MUST render it as
not independent, never as a countersignature by a party other than the
producer. The verifier computes
independence itself for each entry; no entry member and no directory entry
can change that result, and a producer key declaration can only change it
to not independent.</t>
      </section>
    </section>
    <section anchor="fragment-codec">
      <name>Fragment Codec</name>
      <t>The common permalink codec is exact. To encode a Bundle in a URL fragment, a
producer MUST serialize the whole Bundle with JCS, including recursive
<tt>sort_keys</tt> object-member ordering, UTF-8 encode the resulting JSON text, and
encode those bytes with RFC 4648 base64url <xref target="RFC4648"/> Section 5 without
padding. The resulting unescaped ASCII string is the fragment value after
<tt>#</tt>. A decoder MUST base64url-decode the unpadded fragment, UTF-8 decode it,
parse it as JSON, and then apply the Bundle checks in this document.</t>
      <t>The <tt>/v/&lt;id&gt;</tt> route carries a Bundle of one using this same codec; <tt>/bundle</tt>
uses no different decoder. The path is a locator only. URL fragments are not
sent in HTTP requests, so a hosted service receives a Bundle only when it is
otherwise uploaded or embedded in the returned report.</t>
    </section>
    <section anchor="verification">
      <name>Verification</name>
      <t><tt>verification</tt>, when present, is producer self-report. It MAY record tool
versions, performed checks, or findings, but it is not proof and MUST NOT
replace independent verification of Capsules, disclosures, closure,
certificates, checkpoints, or countersignatures. A verifier MUST label it as
producer self-report.</t>
      <t>A conforming verifier MUST at minimum compute the Bundle digest, verify each
enclosed Capsule under the base profile, apply <xref target="closure"/> and <xref target="disclosures"/>,
and report graph closure, interval coverage, and per-record membership as
separate results. It MUST distinguish WITHHELD, REVEALED-match,
REVEALED-mismatch, declared incomplete, and absent or invalid membership
proofs. It MUST apply <xref target="certificate-modes"/> to every Bundle, so that a
certificate mode it does not implement is reported as not established, never
as failed.</t>
    </section>
    <section anchor="security">
      <name>Security Considerations</name>
      <t>The Bundle does not make omitted evidence disappear. Its explicit missing
declaration is necessary to prevent a short report from being represented as a
complete closure. Range endpoints are not a substitute for per-record
membership: endpoint-only proofs leave interior substitution and deletion
undetected. Disclosures are not signatures and are untrusted until their DE-3
digest comparison succeeds. Fragments can be copied or changed by a party
hosting a link, so every received Bundle requires independent verification.
Unknown extension kinds are integrity-covered but semantically untrusted.</t>
      <t>A producer-selected certificate proves that each carried record is in the
log; it says nothing about the records the producer left out, so a verifier
never presents one as complete (<xref target="producer-selected"/>). Reporting an
unimplemented certificate mode as not established rather than invalid keeps a
failure meaningful: a verifier that reported every unfamiliar mode as invalid
would report an honest Bundle and an altered one identically. The cost is a
downgrade: whoever alters a Bundle can name an unknown mode, or put a
non-record leaf of an undefined type where a record or a padding leaf was, and
so turn a failing per-record membership into one that is not established
(<xref target="certificate-modes"/>, <xref target="non-record-leaves"/>). Neither change can make a
claim established. A relying party therefore treats "not established" as
unverified, never as a pass. A padding leaf is accepted only with its full
content, never as a bare digest, because a bare leaf value could be the
identifier of a Capsule the producer chose not to show; recomputing the value
from padding content, whose only free member is a random nonce, rules that
out (<xref target="non-record-leaves"/>).</t>
      <t>A countersignature's <tt>statement</tt> and <tt>signer</tt> are what a relying party reads,
so both are inside the signing input (<xref target="countersign-entry"/>); a signature over
the bundle digest alone would let anyone holding the Bundle rewrite a <tt>failed</tt>
result as <tt>established</tt>, or attribute the entry to a different signer
identifier, without detection. A verified signature establishes who made
the statement, not that the relying party should rely on that party: signer
trust, and the choice of any directory used to put a name to a key, are
outside this document. A self-countersignature is not a second party's
check, and rendering it as one would present the producer's own claim as
independent corroboration; <xref target="self-countersignature"/> forbids that. A
<tt>producer-key/v1</tt> declaration can only cause an entry to be reported as not
independent: a producer that declares a key it does not hold weakens only
its own Bundle's countersignatures, and a producer that countersigns with a
key it does not declare is still reported at most as an <tt>unresolved signer</tt>
unless a directory the verifier chose lists that key. Results
are listed per check and never combined, because an aggregate would hide a
<tt>failed</tt> or <tt>not checked</tt> result behind a total.</t>
      <section anchor="composed-security">
        <name>composed/v1</name>
        <ul spacing="normal">
          <li>
            <t><strong>Omitted participants.</strong> The composing party chooses whom to list.
Composition closure proves that every <em>declared</em> member is present or
declared missing. It cannot reveal a participant that the composing party
never listed. <tt>not_requested</tt> makes a known omission explicit. Completeness
against the real set of participants needs an independent statement of
that set, such as a coordinator's sealed topology. A verifier MUST NOT
describe composition closure as completeness of participation.</t>
          </li>
          <li>
            <t><strong>Declarations versus derivations.</strong> Declared join states are re-derived;
a mismatch fails. Declared custody can only downgrade
(<xref target="composed-observers"/>). Nothing a producer declares can make two
observers independent.</t>
          </li>
          <li>
            <t><strong>Controller bindings.</strong> A binding can only make an agreement redundant
(<xref target="composed-observers"/>); no binding, revocation or failed signature can
make two members corroborating. Two keys bound by their holders can sign
agreeing records, and without the redundancy rule they would read as two
issuers. A verifier knows only the revocation statements it received: the
party presenting a composition can leave out a revocation, and a
<tt>controller_binding</tt> agreement is then reported redundant all the same,
never more. A revocation the producer did not account for derives
<tt>unjoined</tt> and fails a declared <tt>agree</tt> (<tt>join_state_mismatch</tt>).</t>
          </li>
          <li>
            <t><strong>No history.</strong> An empty-history Bundle (<xref target="empty-history"/>) is its
producer's statement that its stream holds no record. Its signed checkpoint
shows that the stream exists, under which key, and the size of its log at
that time. It carries no certificate, so nothing shows that no leaf of that
log is a record. A verifier MUST NOT present no history as
evidence that no action took place. Because it is an <tt>artifact</tt>, never an
<tt>absence</tt>, a cleared or unstarted history cannot be read as a responder
that did not answer.</t>
          </li>
          <li>
            <t><strong>Outcomes.</strong> Keeping refusal and absence distinct is evidentiary:
synthesizing either one from the other forges the stronger record from the
weaker.</t>
          </li>
        </ul>
      </section>
      <section anchor="category-security">
        <name>Category Coverage</name>
        <t>A category declaration is the producer's statement about itself
(<xref target="category-coverage"/>). A producer that chooses its own categories can
declare whatever is favorable to it; this is why a private set is never
reported as registered, and why a verifier reports properties rather than a
verdict of completeness. A signed <tt>ABSENT</tt> is attributable and can be
contradicted by records a counterparty holds, but it is not evidence that
nothing exists. Including the Bundle's <tt>root</tt> in the signing input stops a
declaration made for one root record from being presented with another; a
countersignature over the bundle digest is what binds it to the whole
Bundle.</t>
      </section>
    </section>
    <section anchor="privacy">
      <name>Privacy Considerations</name>
      <t>A Bundle reveals a preimage only through its disclosure overlay
(<xref target="disclosures"/>); a member absent from the overlay stays WITHHELD.</t>
      <t>A range that carries padding leaves (<xref target="non-record-leaves"/>) shows where padding
runs begin and end, and padding is appended before a checkpoint is cut. Such a
range therefore reveals some of the log's earlier checkpoint sizes, which
padding exists to blur (Section 12.1 of <xref target="I-D.mih-agent-evidence-layer"/>).
A producer that does not want to show them uses a producer-selected
certificate (<xref target="producer-selected"/>), which carries no positions other than
those of the records it carries.</t>
      <t>A cleared history's empty-history Bundle (<xref target="empty-history"/>) carries a
checkpoint signed by the same key as the cleared stream. Clearing a history
therefore does not unlink that key from it.</t>
      <t>For the <tt>composed/v1</tt> extension (<xref target="composed"/>):</t>
      <ul spacing="normal">
        <li>
          <t>Join identifiers enter only as digests (<tt>identifier_digest</tt>).</t>
        </li>
        <li>
          <t>A delivered controller binding record (<xref target="composed-binding"/>) reveals that
the holders of its two keys bound them, its claim type and its window. It
carries no name, and it is delivered only to whom the presenting party
chooses.</t>
        </li>
        <li>
          <t><tt>custody_domain</tt> is an opaque label and SHOULD NOT be a network address, an
account identifier or a personal name.</t>
        </li>
        <li>
          <t>Withholding a member's body (<tt>missing</tt>) keeps the composed digest stable. A
judgment can therefore be checked against a composition presented to an
audience that may not see every member.</t>
        </li>
      </ul>
    </section>
    <section anchor="iana">
      <name>IANA Considerations</name>
      <t>IANA is requested to create the "AAC Evidence Bundle Parameters" registry
group with these Specification Required registries, using <xref target="RFC8174"/> and
<xref target="RFC2119"/> terminology and the designated-expert criteria of <xref target="RFC8126"/>:</t>
      <ol spacing="normal" type="1"><li>
          <t>"Evidence Bundle kind", initial value <tt>evidence-bundle/v2</tt>.</t>
        </li>
        <li>
          <t>"Evidence Bundle extension kind", initial values <tt>producer-key/v1</tt>
(<xref target="producer-key"/>) and <tt>composed/v1</tt> (<xref target="composed"/>). A registered
specification defines each block; private <tt>x-</tt> prefixed kinds are not
registered. The kinds <tt>sd-jwt-issuers/v1</tt> and
<tt>disclosure-policy-decisions/v1</tt> (<xref target="provisional-kinds"/>) are not initial
values.</t>
        </li>
        <li>
          <t>"Evidence Bundle countersignature type", initial values <tt>cose-sign1</tt>
(<xref target="bundle-digest"/>) and <tt>countersign/v1</tt> (<xref target="countersign-entry"/>).</t>
        </li>
        <li>
          <t>"Evidence Bundle category set", with no initial values. Each entry
registers a category set identifier for <tt>category_coverage</tt>
(<xref target="category-coverage"/>), its category names, and how a record is assigned
to a category. The registering specification names any external source of
the categories by version. Identifiers beginning <tt>x-</tt> are private and are
not registered.</t>
        </li>
      </ol>
      <t>Until IANA registry creation, the interim registry of record is <tt>REGISTRY.md</tt>
in the source repository of <xref target="I-D.mih-scitt-agent-action-capsule"/>. It records
the same values and policy.</t>
    </section>
  </middle>
  <back>
    <references anchor="sec-combined-references">
      <name>References</name>
      <references anchor="sec-normative-references">
        <name>Normative References</name>
        <reference anchor="RFC2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author fullname="S. Bradner" initials="S." surname="Bradner"/>
            <date month="March" year="1997"/>
            <abstract>
              <t>In many standards track documents several words are used to signify the requirements in the specification. These words are often capitalized. This document defines these words as they should be interpreted in IETF documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>
        <reference anchor="RFC8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author fullname="B. Leiba" initials="B." surname="Leiba"/>
            <date month="May" year="2017"/>
            <abstract>
              <t>RFC 2119 specifies common key words that may be used in protocol specifications. This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the defined special meanings.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>
        <reference anchor="RFC8126">
          <front>
            <title>Guidelines for Writing an IANA Considerations Section in RFCs</title>
            <author fullname="M. Cotton" initials="M." surname="Cotton"/>
            <author fullname="B. Leiba" initials="B." surname="Leiba"/>
            <author fullname="T. Narten" initials="T." surname="Narten"/>
            <date month="June" year="2017"/>
            <abstract>
              <t>Many protocols make use of points of extensibility that use constants to identify various protocol parameters. To ensure that the values in these fields do not have conflicting uses and to promote interoperability, their allocations are often coordinated by a central record keeper. For IETF protocols, that role is filled by the Internet Assigned Numbers Authority (IANA).</t>
              <t>To make assignments in a given registry prudently, guidance describing the conditions under which new values should be assigned, as well as when and how modifications to existing values can be made, is needed. This document defines a framework for the documentation of these guidelines by specification authors, in order to assure that the provided guidance for the IANA Considerations is clear and addresses the various issues that are likely in the operation of a registry.</t>
              <t>This is the third edition of this document; it obsoletes RFC 5226.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="26"/>
          <seriesInfo name="RFC" value="8126"/>
          <seriesInfo name="DOI" value="10.17487/RFC8126"/>
        </reference>
        <reference anchor="RFC8259">
          <front>
            <title>The JavaScript Object Notation (JSON) Data Interchange Format</title>
            <author fullname="T. Bray" initials="T." role="editor" surname="Bray"/>
            <date month="December" year="2017"/>
            <abstract>
              <t>JavaScript Object Notation (JSON) is a lightweight, text-based, language-independent data interchange format. It was derived from the ECMAScript Programming Language Standard. JSON defines a small set of formatting rules for the portable representation of structured data.</t>
              <t>This document removes inconsistencies with other specifications of JSON, repairs specification errors, and offers experience-based interoperability guidance.</t>
            </abstract>
          </front>
          <seriesInfo name="STD" value="90"/>
          <seriesInfo name="RFC" value="8259"/>
          <seriesInfo name="DOI" value="10.17487/RFC8259"/>
        </reference>
        <reference anchor="RFC8785">
          <front>
            <title>JSON Canonicalization Scheme (JCS)</title>
            <author fullname="A. Rundgren" initials="A." surname="Rundgren"/>
            <author fullname="B. Jordan" initials="B." surname="Jordan"/>
            <author fullname="S. Erdtman" initials="S." surname="Erdtman"/>
            <date month="June" year="2020"/>
            <abstract>
              <t>Cryptographic operations like hashing and signing need the data to be expressed in an invariant format so that the operations are reliably repeatable. One way to address this is to create a canonical representation of the data. Canonicalization also permits data to be exchanged in its original form on the "wire" while cryptographic operations performed on the canonicalized counterpart of the data in the producer and consumer endpoints generate consistent results.</t>
              <t>This document describes the JSON Canonicalization Scheme (JCS). This specification defines how to create a canonical representation of JSON data by building on the strict serialization methods for JSON primitives defined by ECMAScript, constraining JSON data to the Internet JSON (I-JSON) subset, and by using deterministic property sorting.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="8785"/>
          <seriesInfo name="DOI" value="10.17487/RFC8785"/>
        </reference>
        <reference anchor="RFC4648">
          <front>
            <title>The Base16, Base32, and Base64 Data Encodings</title>
            <author fullname="S. Josefsson" initials="S." surname="Josefsson"/>
            <date month="October" year="2006"/>
            <abstract>
              <t>This document describes the commonly used base 64, base 32, and base 16 encoding schemes. It also discusses the use of line-feeds in encoded data, use of padding in encoded data, use of non-alphabet characters in encoded data, use of different encoding alphabets, and canonical encodings. [STANDARDS-TRACK]</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="4648"/>
          <seriesInfo name="DOI" value="10.17487/RFC4648"/>
        </reference>
        <reference anchor="RFC3339">
          <front>
            <title>Date and Time on the Internet: Timestamps</title>
            <author fullname="G. Klyne" initials="G." surname="Klyne"/>
            <author fullname="C. Newman" initials="C." surname="Newman"/>
            <date month="July" year="2002"/>
            <abstract>
              <t>This document defines a date and time format for use in Internet protocols that is a profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="3339"/>
          <seriesInfo name="DOI" value="10.17487/RFC3339"/>
        </reference>
        <reference anchor="RFC8032">
          <front>
            <title>Edwards-Curve Digital Signature Algorithm (EdDSA)</title>
            <author fullname="S. Josefsson" initials="S." surname="Josefsson"/>
            <author fullname="I. Liusvaara" initials="I." surname="Liusvaara"/>
            <date month="January" year="2017"/>
            <abstract>
              <t>This document describes elliptic curve signature scheme Edwards-curve Digital Signature Algorithm (EdDSA). The algorithm is instantiated with recommended parameters for the edwards25519 and edwards448 curves. An example implementation and test vectors are provided.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="8032"/>
          <seriesInfo name="DOI" value="10.17487/RFC8032"/>
        </reference>
        <reference anchor="RFC8610">
          <front>
            <title>Concise Data Definition Language (CDDL): A Notational Convention to Express Concise Binary Object Representation (CBOR) and JSON Data Structures</title>
            <author fullname="H. Birkholz" initials="H." surname="Birkholz"/>
            <author fullname="C. Vigano" initials="C." surname="Vigano"/>
            <author fullname="C. Bormann" initials="C." surname="Bormann"/>
            <date month="June" year="2019"/>
            <abstract>
              <t>This document proposes a notational convention to express Concise Binary Object Representation (CBOR) data structures (RFC 7049). Its main goal is to provide an easy and unambiguous way to express structures for protocol messages and data formats that use CBOR or JSON.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="8610"/>
          <seriesInfo name="DOI" value="10.17487/RFC8610"/>
        </reference>
        <reference anchor="RFC6901">
          <front>
            <title>JavaScript Object Notation (JSON) Pointer</title>
            <author fullname="P. Bryan" initials="P." role="editor" surname="Bryan"/>
            <author fullname="K. Zyp" initials="K." surname="Zyp"/>
            <author fullname="M. Nottingham" initials="M." role="editor" surname="Nottingham"/>
            <date month="April" year="2013"/>
            <abstract>
              <t>JSON Pointer defines a string syntax for identifying a specific value within a JavaScript Object Notation (JSON) document.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="6901"/>
          <seriesInfo name="DOI" value="10.17487/RFC6901"/>
        </reference>
        <reference anchor="RFC9943">
          <front>
            <title>An Architecture for Trustworthy and Transparent Digital Supply Chains</title>
            <author fullname="H. Birkholz" initials="H." surname="Birkholz"/>
            <author fullname="A. Delignat-Lavaud" initials="A." surname="Delignat-Lavaud"/>
            <author fullname="C. Fournet" initials="C." surname="Fournet"/>
            <author fullname="Y. Deshpande" initials="Y." surname="Deshpande"/>
            <author fullname="S. Lasker" initials="S." surname="Lasker"/>
            <date month="June" year="2026"/>
            <abstract>
              <t>Traceability in supply chains is a growing security concern. While Verifiable Data Structures (VDSs) have addressed specific issues, such as equivocation over digital certificates, they lack a universal architecture for all supply chains. This document defines such an architecture for single-issuer signed statement transparency. It ensures extensibility and interoperability between different transparency services as well as compliance with various auditing procedures and regulatory requirements.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="9943"/>
          <seriesInfo name="DOI" value="10.17487/RFC9943"/>
        </reference>
        <reference anchor="RFC9942">
          <front>
            <title>CBOR Object Signing and Encryption (COSE) Receipts</title>
            <author fullname="O. Steele" initials="O." surname="Steele"/>
            <author fullname="H. Birkholz" initials="H." surname="Birkholz"/>
            <author fullname="A. Delignat-Lavaud" initials="A." surname="Delignat-Lavaud"/>
            <author fullname="C. Fournet" initials="C." surname="Fournet"/>
            <date month="June" year="2026"/>
            <abstract>
              <t>CBOR Object Signing and Encryption (COSE) Receipts prove properties of a Verifiable Data Structure (VDS) to a verifier. VDSs and associated Proof Types enable security properties, such as minimal disclosure, transparency, and non-equivocation. Transparency helps maintain trust over time and has been applied to certificates, end-to-end encrypted messaging systems, and supply chain security. This specification enables concise transparency-oriented systems by building on Concise Binary Object Representation (CBOR) and COSE. The extensibility of the approach is demonstrated by providing CBOR encodings for Merkle inclusion and consistency proofs.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="9942"/>
          <seriesInfo name="DOI" value="10.17487/RFC9942"/>
        </reference>
        <reference anchor="I-D.mih-scitt-agent-action-capsule">
          <front>
            <title>An Agent Action Capsule Profile for SCITT</title>
            <author initials="S." surname="Mih" fullname="Steven Mih">
              <organization>Action State Group, Inc.</organization>
            </author>
            <date>n.d.</date>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-mih-scitt-agent-action-capsule-05"/>
        </reference>
        <reference anchor="I-D.mih-agent-disclosure-envelope">
          <front>
            <title>Disclosure Envelope Profile for Agent Action Capsules</title>
            <author initials="S." surname="Mih" fullname="Steven Mih">
              <organization>Action State Group, Inc.</organization>
            </author>
            <date>n.d.</date>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-mih-agent-disclosure-envelope-00"/>
        </reference>
        <reference anchor="I-D.mih-agent-evidence-request">
          <front>
            <title>An Interaction Model for Requesting Verifiable Evidence</title>
            <author initials="S." surname="Mih" fullname="Steven Mih">
              <organization>Action State Group, Inc.</organization>
            </author>
            <date year="2026" month="October" day="29"/>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-mih-agent-evidence-request-01"/>
        </reference>
        <reference anchor="RFC3986">
          <front>
            <title>Uniform Resource Identifier (URI): Generic Syntax</title>
            <author fullname="T. Berners-Lee" initials="T." surname="Berners-Lee"/>
            <author fullname="R. Fielding" initials="R." surname="Fielding"/>
            <author fullname="L. Masinter" initials="L." surname="Masinter"/>
            <date month="January" year="2005"/>
            <abstract>
              <t>A Uniform Resource Identifier (URI) is a compact sequence of characters that identifies an abstract or physical resource. This specification defines the generic URI syntax and a process for resolving URI references that might be in relative form, along with guidelines and security considerations for the use of URIs on the Internet. The URI syntax defines a grammar that is a superset of all valid URIs, allowing an implementation to parse the common components of a URI reference without knowing the scheme-specific requirements of every possible identifier. This specification does not define a generative grammar for URIs; that task is performed by the individual specifications of each URI scheme. [STANDARDS-TRACK]</t>
            </abstract>
          </front>
          <seriesInfo name="STD" value="66"/>
          <seriesInfo name="RFC" value="3986"/>
          <seriesInfo name="DOI" value="10.17487/RFC3986"/>
        </reference>
      </references>
      <references anchor="sec-informative-references">
        <name>Informative References</name>
        <reference anchor="I-D.mih-agent-evidence-layer">
          <front>
            <title>Evidence Layer</title>
            <author initials="S." surname="Mih" fullname="Steven Mih">
              <organization>Action State Group, Inc.</organization>
            </author>
            <date year="2026" month="October" day="03"/>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-mih-agent-evidence-layer-00"/>
        </reference>
        <reference anchor="I-D.mih-sokolov-scitt-payload-binding">
          <front>
            <title>Canonicalization Declaration for SCITT Signed Statements</title>
            <author initials="S." surname="Mih" fullname="Steven Mih">
              <organization>Action State Group, Inc.</organization>
            </author>
            <author initials="A." surname="Sokolov" fullname="Anton Sokolov">
              <organization>Tyche Institute</organization>
            </author>
            <date year="2026" month="October" day="26"/>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-mih-sokolov-scitt-payload-binding-06"/>
        </reference>
        <reference anchor="RFC9338">
          <front>
            <title>CBOR Object Signing and Encryption (COSE): Countersignatures</title>
            <author fullname="J. Schaad" initials="J." surname="Schaad"/>
            <date month="December" year="2022"/>
            <abstract>
              <t>Concise Binary Object Representation (CBOR) is a data format designed for small code size and small message size. CBOR Object Signing and Encryption (COSE) defines a set of security services for CBOR. This document defines a countersignature algorithm along with the needed header parameters and CBOR tags for COSE. This document updates RFC 9052.</t>
            </abstract>
          </front>
          <seriesInfo name="STD" value="96"/>
          <seriesInfo name="RFC" value="9338"/>
          <seriesInfo name="DOI" value="10.17487/RFC9338"/>
        </reference>
        <reference anchor="RFC9901">
          <front>
            <title>Selective Disclosure for JSON Web Tokens</title>
            <author fullname="D. Fett" initials="D." surname="Fett"/>
            <author fullname="K. Yasuda" initials="K." surname="Yasuda"/>
            <author fullname="B. Campbell" initials="B." surname="Campbell"/>
            <date month="November" year="2025"/>
            <abstract>
              <t>This specification defines a mechanism for the selective disclosure
of individual elements of a JSON data structure used as the payload
of a JSON Web Signature (JWS). The primary use case is the selective
disclosure of JSON Web Token (JWT) claims.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="9901"/>
          <seriesInfo name="DOI" value="10.17487/RFC9901"/>
        </reference>
      </references>
    </references>
    <?line 1627?>

<section numbered="false" anchor="change-log">
      <name>Change Log</name>
      <t>Since -01:</t>
      <ul spacing="normal">
        <li>
          <t>Certificate modes (<xref target="certificate-modes"/>): <tt>completeness.selection</tt> names
the mode of <tt>completeness_certificate</tt>; a verifier reports a mode it does not
implement as not established (<tt>certificate_mode_unsupported</tt>), never as
failed or invalid.</t>
        </li>
        <li>
          <t>Defined the producer-selected certificate (<xref target="producer-selected"/>), in the
shape producers already emit: <tt>selection</tt> <tt>producer-selected</tt>, no range,
each record proven on its own; interval coverage is never established for
it. Added <tt>none</tt> to <tt>payloads_mode</tt>.</t>
        </li>
        <li>
          <t>Non-record leaves (<xref target="non-record-leaves"/>): a range covers padding leaves in
<tt>completeness_certificate.non_record_leaves</tt>, each carried in full; stated
the padding leaf's serialization and identifier, the per-position accounting
rule for per-record membership, and that a leaf of an undefined type leaves
the claim not established, never established. Stated that per-record
membership is the claim, and that unknown <tt>completeness_certificate</tt> members
are ignored. Privacy Considerations: what a padded range reveals. Security
Considerations: reporting an unimplemented mode or an undefined leaf type
as not established can turn a failure into not established, never into
established; why a padding leaf is carried in full.</t>
        </li>
        <li>
          <t>Category coverage (<xref target="category-coverage"/>): the OPTIONAL <tt>category_coverage</tt>
member, a producer's signed declaration of one status per category of a
named category set (<tt>AVAILABLE</tt>, <tt>PARTIAL</tt>, <tt>WITHHELD</tt>, <tt>DELETED</tt>, <tt>ABSENT</tt>,
<tt>UNDISCLOSED</tt>; <tt>UNDISCLOSED</tt> requires <tt>policy_uri</tt>), answering the Evidence
Request <tt>categories</tt> field; five verifier properties, reported separately and
never combined; it declares and does not prove. Created the "Evidence Bundle
category set" registry, with no initial values. The Abstract mentions the
declaration.</t>
        </li>
        <li>
          <t><tt>composed/v1</tt>: defined the controller binding record
(<xref target="composed-binding"/>): its members and claim types, its signing input and
binding digest, the methods <tt>dual_signature/0</tt> and
<tt>dual_signature_credential/0</tt>, revocation statements, and when a binding
applies; and the <tt>controller_binding</tt> join basis with its derivation
(<xref target="composed-joins"/>). A <tt>controller_binding</tt> agreement is always redundant,
never corroborating (<xref target="composed-observers"/>). A verifier that implements
only -01 reads a block that carries a <tt>controller_binding</tt> join as
malformed, since -01's <tt>join-basis</tt> does not list it. Security and Privacy
Considerations: controller bindings. A binding record MAY carry
<tt>key_types</tt>; this document defines the <tt>ed25519</tt> key type, and a record
under any other is treated as under a method a verifier does not
implement.</t>
        </li>
        <li>
          <t>Empty-history Bundles (<xref target="empty-history"/>): a Bundle with no records and no
<tt>root</tt> shows that a stream has no history to disclose, whether it was never
started or its history was cleared (a new stream, with a new <tt>log_id</tt>).
It is well formed and reported as no history, never as failed and never as
an absence. In <tt>composed/v1</tt> it is an <tt>artifact</tt> member, and a join with it
derives <tt>one_sided</tt>; a <tt>controller_binding</tt> join derives from the key that
signed its checkpoint, and is <tt>not_derivable</tt> without one. Security
Considerations: no history. Privacy Considerations: a cleared history's
checkpoint is signed by the cleared stream's key.</t>
        </li>
        <li>
          <t><tt>composed/v1</tt> composed digest: a join without <tt>pointer</tt> sorts before every
join with one, including a <tt>pointer</tt> that is the empty string. -01 sorted
the two alike, so their carried order changed the digest. <tt>pointer</tt> values
compare by their UTF-8 octets.</t>
        </li>
        <li>
          <t>Cites Evidence Request -01 and, informatively, Evidence Layer -00 and the
Canonicalization Declaration -06.</t>
        </li>
      </ul>
      <t>Since -00:</t>
      <ul spacing="normal">
        <li>
          <t>Countersignatures: defined the <tt>countersign/v1</tt> countersignature entry, its
statement, and its five check results; defined countersignature,
countersigner, and self-countersignature, and required a
self-countersignature to be rendered as not independent; registered
<tt>countersign/v1</tt> as a second countersignature type; an entry with no
<tt>type</tt> is processed as <tt>countersign/v1</tt> (the legacy form).</t>
        </li>
        <li>
          <t>Defined and registered the <tt>producer-key/v1</tt> extension kind, which declares
the producer's key and can only cause a countersignature to be reported as
not independent.</t>
        </li>
        <li>
          <t>Defined and registered the <tt>composed/v1</tt> extension kind: one member per
responder carrying its Evidence Request outcome (<tt>artifact</tt>, <tt>refusal</tt> or
<tt>absence</tt>); observers with a role and a custody domain; joins whose
declared state (<tt>agree</tt>, <tt>mismatch</tt>, <tt>unjoined</tt> or <tt>one_sided</tt>) a verifier
re-derives; a fourth completeness claim, composition closure; same-custody
agreement reported as redundant, not corroborating; and a composed digest
(JCS, SHA-256) over the member, observer, join and <tt>not_requested</tt>
declarations. Added Privacy Considerations.</t>
        </li>
        <li>
          <t>Noted two provisional extension kinds, <tt>sd-jwt-issuers/v1</tt> and
<tt>disclosure-policy-decisions/v1</tt>, held for ratification in the interim
registry and not defined by this document.</t>
        </li>
      </ul>
    </section>
    <section numbered="false" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>The authors thank the SCITT working group and the Action State Group
architecture review for the evidence-bundle starting shape.</t>
    </section>
  </back>

</rfc>
