| Internet-Draft | AAC Disclosure Envelope | September 2026 |
| Mih | Expires 30 March 2027 | [Page] |
This document defines the Disclosure Envelope, an out-of-band wrapper
structure for revealing the raw content behind a digest-only Agent Action
Capsule field to a verifier, without altering the Capsule's own bytes or
recomputing its capsule_id. The Capsule profile
[I-D.mih-scitt-agent-action-capsule] commits some fields as a
[RFC8785]-canonicalized SHA-256 digest only — the content itself is never
carried in the signed, registered record. The initial disclosable fields are
model_attestation.compute_attestation.agent_input_digest and
.agent_output_digest. A Disclosure Envelope wraps an unmodified Capsule
alongside a sibling disclosures object; a verifier recomputes the
JSON-DIGEST of each disclosed value using the same canonicalization the base
profile already uses for capsule_id, and compares it to the digest
committed inside the Capsule. This mechanism is distinct from the per-field
selective-disclosure profile
[I-D.mih-scitt-agent-action-capsule-selective-disclosure]: that mechanism
conceals and later reveals whole payload fields that would otherwise be
carried in clear; this one reveals the content behind a field that was
always present in clear, as a digest, from the moment the Capsule was
signed.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 30 March 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Some Agent Action Capsule fields are, by design, digest-only: the field
carries a [RFC8785] JSON-DIGEST commitment to a value, not the value
itself. model_attestation.compute_attestation.agent_input_digest and
.agent_output_digest ([I-D.mih-scitt-agent-action-capsule], Observation
mode) are the motivating case — a producer commits to the exact agent input
and output that produced an action without carrying that (potentially
large, sensitive, or independently-lifecycled) content in the signed,
anchored record.¶
A producer who later chooses to prove that committed content to a specific
verifier needs a wire format for doing so. Sharing the raw content
out-of-band already works informally, but an ad hoc format invites two
failures this document exists to close: first, a viewer that renders a
disclosed value without recomputing and checking its digest is asserting a
match it never verified; second, a format that embeds the disclosed content
inside the digest-bearing region of the Capsule itself changes the bytes
that were signed, so the Capsule no longer content-addresses to its own
capsule_id — the artifact silently stops being the thing that was
anchored.¶
This document defines a companion wrapper, the Disclosure Envelope, that
keeps the Capsule's bytes untouched and carries disclosed content as a
sibling structure outside them, together with the verifier checks that
recompute and compare each disclosure against its committed digest. The
default posture is WITHHELD: a Disclosure Envelope with an absent or empty
disclosures object is equivalent to sharing the Capsule alone, and
disclosure of any one field is opt-in and independent of any other.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
A JSON object with a capsule member and an OPTIONAL disclosures
member, as defined in Section 4.¶
A named entry in the disclosure-eligibility table (Section 4.2) pairing
a disclosures member name with the Capsule field that carries its
committed digest.¶
The raw JSON value of one disclosable field, carried as the corresponding
member of the disclosures object.¶
As defined in [I-D.mih-scitt-agent-action-capsule]: the lowercase-hex
SHA-256 digest of UTF8({{RFC8785}} JCS(value)). The whole JSON value is
canonicalized recursively; no object-member allow-list, replacer array, or
other key-filtering operation is applied at any depth. This document only
applies to embedded Capsules that conform to the base profile's format-4
requirements.¶
The terms "Capsule", "capsule_id", "Producer", and "Verifier" are as defined in [I-D.mih-scitt-agent-action-capsule].¶
[I-D.mih-scitt-agent-action-capsule-selective-disclosure] and this document both let a producer share less than the full Capsule content, but they solve different problems and share no vocabulary.¶
Selective disclosure conceals a payload field that would otherwise be
carried in clear at signing time, replacing it with a salted-hash
commitment in an _sd array; later disclosure reveals the concealed field
and reconstructs the plain payload. The field's clear-text value was never
in the signed record to begin with.¶
The Disclosure Envelope instead applies to a field that is a digest
by design, unconditionally, in every Capsule that carries it — the
field is never concealed, because it never carried the raw value in the
first place. There is nothing to reconstruct and no _sd/_sd_alg
structure; the Capsule is unmodified in both its disclosed and undisclosed
states, and disclosure is a wrapper added around it, not a transformation
of it.¶
The two mechanisms compose without conflict: a Capsule may simultaneously be an SD-Capsule (per the selective-disclosure profile) and be wrapped in a Disclosure Envelope (per this document). A verifier applies each mechanism's checks independently; neither mechanism's structure is visible to or interpreted by the other.¶
A Disclosure Envelope is a JSON [RFC8259] object:¶
{
"capsule": { ... },
"disclosures": {
"agent_input": { ... },
"agent_output": { ... }
}
}
¶
capsule (REQUIRED): the Capsule payload, byte-for-byte identical in its
JCS-canonicalized form to the payload that was signed and, if registered,
anchored. capsule_id ([I-D.mih-scitt-agent-action-capsule] §5.1) is
computed over capsule alone, exactly as the base profile defines,
using the base profile's own canonical-capsule-form exclusion set
unmodified. No member of the Disclosure Envelope other than capsule is
part of any digest computation the base profile defines, and no member of
the envelope other than capsule was part of the COSE_Sign1
[RFC9052] signature.¶
disclosures (OPTIONAL): an object whose members are disclosable fields
(Section 4.2) the producer has chosen to reveal. A member's absence from
disclosures — including the absence of the whole disclosures object —
means that field is WITHHELD; this is the default posture and requires no
signal in the envelope. A member's presence means REVEALED; its value is
the exact JSON value whose JSON-DIGEST the corresponding Capsule field
committed to at signing time.¶
A Disclosure Envelope MUST NOT be submitted for SCITT registration; the
registrable, signable artifact is the COSE_Sign1 Signed Statement whose
payload is capsule, registered (if at all) with a SCITT Transparency
Service [RFC9943] before any envelope is constructed around it. The envelope is a presentation-layer structure a
producer constructs after the fact, for a verifier that already trusts (or
is independently verifying) the underlying Capsule.¶
A Capsule with no accompanying Disclosure Envelope — the base profile's existing whole-envelope posture — remains fully valid; this document adds an opt-in wrapper, not a requirement.¶
The following table defines the disclosable fields this document
specifies. A disclosures member name not in this table is non-conforming;
a verifier MUST treat it as an unrecognized member (Section 5,
check DE-1) rather than attempting to verify it.¶
| disclosures member | Committed-digest field (in capsule.model_attestation.compute_attestation) |
|---|---|
agent_input
|
agent_input_digest
|
agent_output
|
agent_output_digest
|
Both fields are defined in
[I-D.mih-scitt-agent-action-capsule] (Observation mode). This table is a
Specification Required registry. An extension MUST (1) add a digest-only
Capsule field, (2) register the disclosures member and its committed-digest
field as a pair, (3) add format-4 vectors and increment their vector version,
and (4) require producers to retain the original value so it can be revealed.¶
This revision scopes registrations to committed-digest fields directly under
capsule.model_attestation.compute_attestation. It does not define generic
Capsule-path resolution. Widening the registry beyond that object requires
generic path resolution in every implementation before such a registration can
be made. A disclosure reveals the whole registered member; selective disclosure
of a sub-field is instead the salted-commitment mechanism defined by
[I-D.mih-scitt-agent-action-capsule-selective-disclosure].¶
capsule and disclosures are wrapper-level member names defined by this
document. Neither is a Capsule payload member defined or reserved by
[I-D.mih-scitt-agent-action-capsule] or its companions, so no collision
is possible: a Disclosure Envelope's capsule member and the Capsule
payload it contains are always syntactically distinguishable from a bare
Capsule payload, because a Capsule payload has no member of either name.¶
Verification of a Disclosure Envelope is performed in two independent
phases: base Capsule verification ([I-D.mih-scitt-agent-action-capsule]
§6, Class 1) over capsule alone, and Disclosure verification (Section 5.1)
over the disclosures object. The two phases do not gate each other: a
disclosure mismatch MUST NOT be reported as a capsule_id or Class 1
failure, and a Class 1 failure does not suppress disclosure verification.
A verifier surface MUST report both results and MUST NOT conflate them —
in particular, MUST NOT let a matching disclosure upgrade the Capsule's own
verification status, and MUST NOT let the Capsule's own valid, anchored
status be read as implying anything about a disclosure it has not itself
recomputed and checked.¶
For each member disclosures carries:¶
If the member name is not in the disclosure-eligibility table
(Section 4.2), report disclosure_ineligible_field and skip it.¶
Locate the committed-digest field for this member per Section 4.2. If
capsule does not carry that field (or it is not a well-formed JSON-DIGEST
— 64 lowercase hex characters), report disclosure_no_committed_digest
and treat the member as WITHHELD-equivalent: it MUST NOT be reported as
matching or mismatching, because there is no commitment to check it
against.¶
Compute computed = JSON-DIGEST(value): the lowercase-hex SHA-256 of
UTF8(JCS(value)) over the whole recursively canonicalized value, with no
member filtering or replacer array. This document defines no second hashing
path.¶
Compare computed to the committed digest located in DE-2:¶
If they match: report disclosure_match for this member. The verifier
MAY present this as "REVEALED — match" or equivalent.¶
If they do not match: report disclosure_mismatch for this member. The
verifier MUST present this as a failed verification of the disclosed
content specifically — for example "REVEALED — MISMATCH" — and MUST NOT
present the disclosed value as if it were confirmed.¶
A value that cannot be JCS-canonicalized (for example, one carrying a raw
JSON float, forbidden in digest-bearing content by
[I-D.mih-scitt-agent-action-capsule] §5.1) MUST NOT be treated as
matching; a verifier MUST report disclosure_mismatch for it rather than
raising an error, consistent with the base profile's structured-result,
never-throw contract.¶
A verifier implementing this profile SHOULD include, alongside the base Class 1 result, a structured per-member disclosure result:¶
| Field | Type | Meaning |
|---|---|---|
disclosures_checked
|
integer | Count of disclosures members present in the envelope. |
disclosures_matched
|
integer | Count that produced disclosure_match. |
disclosure_findings
|
array | One finding per disclosures member: {member, code} where code is one of disclosure_match, disclosure_mismatch, disclosure_ineligible_field, disclosure_no_committed_digest. |
Only capsule was covered by the Capsule's COSE_Sign1 signature and, if
registered, by the Transparency Service's Receipt. disclosures is added
after the fact by whichever party constructs the envelope and is not
itself signed. This is why Section 5.1 MUST always recompute and compare
rather than trust a disclosed value's mere presence: presence alone proves
nothing, and a party who can modify the envelope in transit or at rest
(for example, anyone re-hosting a URL-fragment-carried envelope) can insert
an arbitrary value for any disclosure member. The digest recompute against
the value already committed inside the signed Capsule is the entire trust
basis; a verifier that renders "REVEALED" without performing DE-3 is
asserting a check it did not perform.¶
Once a disclosures member is populated, its value is in clear to
anyone who receives the envelope; there is no per-recipient concealment,
salting, or decoy mechanism as in
[I-D.mih-scitt-agent-action-capsule-selective-disclosure]. A producer's
only confidentiality control is which members it chooses to populate for a
given recipient. Producers serving multiple verifiers with different
disclosure needs from the same Capsule construct a distinct envelope per
recipient, omitting members that recipient is not entitled to.¶
Because disclosures is never part of the capsule_id computation,
disclosing content to one verifier does not change, invalidate, or require
re-anchoring the Capsule for any other verifier; every envelope built
around the same capsule value shares the same capsule_id, regardless of
which disclosures members each carries. This is the property that makes
disclosure a strictly additive, reversible-per-recipient operation rather
than a mutation of the anchored record.¶
A Capsule with no accompanying envelope, or an envelope with no
disclosures object, reveals nothing about agent_input_digest or
agent_output_digest beyond the digest itself — a 32-byte commitment does
not leak the committed content. Producers that never intend to disclose
these fields incur no format change and no additional exposure by this
document's existence.¶
This document reserves the wrapper member names capsule and
disclosures (Section 4.3) and the disclosure-eligible field table
(Section 4.2). Neither is a member of the Agent Action Capsule payload
registries of [I-D.mih-scitt-agent-action-capsule] §12; both are
wrapper-level names that are, by construction, never present in a Capsule
payload. IANA is not requested to create a new registry for these members
at this time. The interim registry of record is the REGISTRY.md file of
the source repository of [I-D.mih-scitt-agent-action-capsule], updated to
list the wrapper member names and the disclosure-eligible field table
defined by this document.¶
The following non-normative examples illustrate the mechanism. See the
source repository's vectors/disclosure-envelope/pos-disclosure-envelope-match/
and vectors/disclosure-envelope/neg-disclosure-envelope-mismatch/ for
frozen, machine-checked vectors covering these two cases (kept in a
directory of their own, separate from the base profile's cross-language
vectors/capsule/ corpus — see that directory's README).¶
Given a Capsule whose model_attestation.compute_attestation carries
"agent_input_digest": "<D>" where D = JSON-DIGEST({"amount": "500.00"}),
the envelope¶
{
"capsule": { ... "agent_input_digest": "<D>" ... },
"disclosures": { "agent_input": {"amount": "500.00"} }
}
¶
recomputes JSON-DIGEST({"amount": "500.00"}), which equals <D> — a
verifier reports disclosure_match for agent_input.¶
Replacing the disclosed value with {"amount": "999.00"} while leaving
capsule (and therefore <D>) unchanged produces a disclosures.agent_input
whose recomputed digest differs from <D> — a verifier reports
disclosure_mismatch for agent_input, while capsule's own
capsule_id recomputation and Class 1 verification are unaffected and
still pass.¶
Since -00 (this document): initial publication.¶
The author thanks the SCITT working group for the Signed Statement and Transparency Service model this document's disclosure boundary is drawn against.¶