Audit D-312 — Attestation-shape ratification: dream, silence, quorum_membership, revoke_capability_manifest
Worker: worker-glossary-2026-05-05
Date: 2026-05-05
Spec sections touched: [SPEC §4.2], [EXT §4.6], [EXT §12], [EXT §7.4], registries/standard-attestations.md §3.3, §3.5, §3.6, §4.3
Triggering findings: worker-B's T-012 NOTES.md F-5 (dream/silence shape underspecification), F-6 (revoke_capability_manifest lacks a domain-separation tag); INTERPRETATION I-3 (the four targets named-but-not-fully-shaped in SPEC/EXT).
TL;DR
All four target attestation values were given minimal canonical
shapes by worker-B in the T-012 standard-attestations registry.
After cross-checking each shape against SPEC §4.2, EXT §4.6, EXT
§7.4, and EXT §12, this audit:
1. Ratifies worker-B's shapes for dream, silence,
quorum_membership, and revoke_capability_manifest as canonical
for v0.2.
2. Adds a domain-separation tag herald.revoke_capability_manifest.v1
to EXT §12 and references it from EXT §4.6 and the registry
§4.3 entry. Worker-B's F-6 was correct that the omission is a
real gap; cross-protocol signature-reuse is the same hazard
herald.capability_manifest.v1 defends against on the issuance
side.
3. Does not add domain-separation tags for dream or silence.
Their hardening rules constrain value content (sanitisation,
non-contentful) rather than the carrying envelope's signing
scope, so a dedicated tag would not earn its keep.
4. Does not add a domain-separation tag for quorum_membership —
one is already registered (herald.quorum_membership.v1,
EXT §12 line 789).
The shapes are docs-only; nothing in the wire format changes for any
existing implementation.
Per-target verdict
dream — RATIFY worker-B's §3.3 as canonical
Worker-B's value shape:
{
"category": "<registry-id>", # required, categorical
"alternate_summary": "<free string>", # optional, sanitised
"would_have_said_to": "<sigil>" # optional
}Cross-check. [SPEC §4.2] hardening row says only that dream
"SHOULD be sanitised (categorical, not contentful)." The category
field is the load-bearing one; alternate_summary is the sanitised
prose escape valve; would_have_said_to is the addressee that the
unsent path would have had. All three are textual and CBOR-clean.
[CHARTER §3] (semantics) and [SPEC §9 / sigil:dreamer] (transport
rule) bracket the attestation; the registry's §3.3 already encodes
the archive-only routing rule for sigil:dreamer envelopes.
No additional tag. A dream envelope is signed under the origin
tag (herald.envelope.v1 / herald.sig.origin.v1). The protection
against misuse is non-relay (the sigil:dreamer conduit role is
archive-only), not signature-scope discrimination. Adding a tag
would not change verifier behaviour.
No SPEC-side change required. SPEC §4.2 already declares the
hardening rule; the value-shape lives in the registry per the
existing pattern for cultural attestations.
silence — RATIFY worker-B's §3.6 as canonical
Worker-B's value shape:
{
"category": "<registry-id>", # required, categorical
"reason": "<free string>" # optional
}Cross-check. [SPEC §4.2] hardening row says the withheld field
"MUST describe a category of withheld content, not the content
itself." Worker-B's two-field shape encodes this exactly: category
is the categorical class; reason is a free-text non-contentful
hint. additionalProperties: false enforces no covert side-channels.
No additional tag. Same reasoning as dream: the hardening is
a value-content rule, not a signing-scope rule.
No SPEC-side change required.
quorum_membership — RATIFY worker-B's §3.5 as canonical
Worker-B's value shape:
{
"collective": "<collective-sigil>", # required
"joined_at": <Unix seconds>, # required
"until": <Unix seconds> # optional
}Cross-check. [EXT §7.4] (line 555): "Members MUST sign their
own birth into the collective via a quorum_membership attestation
referencing the collective sigil." EXT does not enumerate fields;
worker-B's three-field shape captures the minimum (membership
target, when joined, optional sunset).
[EXT §12] (line 789) already registersherald.quorum_membership.v1. Worker-B's validation rule 1 ("Reject
unless the carrying envelope is signed under domain-separation tagherald.quorum_membership.v1 at index 0") is correct.
No additional EXT/SPEC change required. The shape is implementable
today.
revoke_capability_manifest — RATIFY shape, ADD domain-separation tag
Worker-B's value shape:
{
"revokes": "<envelope-id of the manifest>", # required
"ts": <Unix seconds>, # required
"reason": "<free string>" # optional
}Cross-check. [EXT §4.6] (line 346): "A subject sigil MAY revoke
a prior manifest by emitting a revoke_capability_manifest
attestation referencing the manifest envelope's id. Revocations
follow the same audit-log inclusion rules as molt envelopes." This
fixes the semantics; worker-B's three-field shape captures the
minimum (referenced envelope id, timestamp, optional human reason).
Domain-separation tag — ADD. EXT §4.6 currently does not
register a tag, and worker-B's F-6 correctly flagged the gap. The
issuance side (capability_manifest) is taggedherald.capability_manifest.v1; the revocation side should be
tagged symmetrically. Without a distinct tag:
- A capability-manifest signature could in principle be replayed
as a revocation if the canonical payloads happened to align —
unlikely but the whole point of domain separation per
[SPEC §3.6] / [THREATS §4.6] is to remove "happened to align" as
a vulnerability class.
- A revocation signature is structurally a state-transition
signature (it changes whether a capability advertisement is in
effect). Every other state-transition signature in EXT §12 has a
tag (herald.molt.v1, herald.deprecation.v1,
herald.refusal.v1, herald.capability_manifest.v1,
herald.bestiary_entry.v1, herald.delisting.v1). Revocation is
the missing peer.
Recommended tag: herald.revoke_capability_manifest.v1.
(Worker-B's F-6 originally proposed .v2; that was correct for
the pre-D-308 SPEC. Post-D-308 the canonical version suffix is.v1 — see D-308 RECEIPT for the rationale.)
Patches
Patch 1 — EXT §12 (add registry row, between current lines 780 and 781)
Insert:
| `herald.revoke_capability_manifest.v1` | manifest revocations ([EXT §4.6]) | |Place it adjacent to the herald.capability_manifest.v1 row so the
issuance/revocation pair sit together visually.
Patch 2 — EXT §4.6 (line 346)
Current:
A subject sigil MAY revoke a prior manifest by emitting a `revoke_capability_manifest` attestation referencing the manifest envelope's `id`. Revocations follow the same audit-log inclusion rules as molt envelopes ([EXT §1.3]). Stale manifests may also expire by `expires` without explicit revocation.Proposed:
A subject sigil MAY revoke a prior manifest by emitting a `revoke_capability_manifest` attestation referencing the manifest envelope's `id`. The carrying envelope MUST be signed under domain-separation tag `herald.revoke_capability_manifest.v1` ([EXT §12]). Revocations follow the same audit-log inclusion rules as molt envelopes ([EXT §1.3]). Stale manifests may also expire by `expires` without explicit revocation.Patch 3 — registries/standard-attestations.md §4.3 (line 1216)
Current:
- **Domain-separation tag:** none registered specifically for revocation; the carrying envelope is signed by the subject sigil's current key under `herald.envelope.v2`/`herald.sig.origin.v1`. (EXT §4.6 specifies the envelope-shape rule but does not register a dedicated tag in EXT §12.)Proposed:
- **Domain-separation tag:** `herald.revoke_capability_manifest.v1` ([EXT §12]). Symmetric with `herald.capability_manifest.v1` on the issuance side. The carrying envelope MUST be signed under this tag at index 0 by the subject sigil's current key.Patch 4 — registries/standard-attestations.md §4.3 validation rules (after line 1256)
Add a new rule at the top (renumber existing rules):
1. Reject unless the carrying envelope is signed under domain-separation tag `herald.revoke_capability_manifest.v1` at index 0 ([EXT §12]).Worker-B's existing rules become 2–5.
Patch 5 — registries/standard-attestations.md Appendix A (line 1289)
Current:
| `dream`, `silence`, `quorum`, `revoke_capability_manifest` | (no dedicated tag — sign under origin) | (none registered in EXT §12) | carried in ordinary envelopes |Proposed (split into two rows):
| `dream`, `silence`, `quorum` | (no dedicated tag — sign under origin) | (none registered in EXT §12) | hardening is value-shape, not signing-scope |
| `revoke_capability_manifest` | (n/a — tag landed via D-312) | `herald.revoke_capability_manifest.v1` | symmetric with issuance |Note: the SPEC §3.6 column on row 1 is left "no dedicated tag" because
SPEC §3.6 has no row for these three (and shouldn't — their
hardening is value-content, not domain-separation). The note column
documents the design rationale.
What does NOT change
- Test vectors (
test-vectors/v0.2/): the four target shapes
appear in zero current vectors. Nothing to regenerate.
- CHARTER: CHARTER §3 references
dream,silence,
quorum_membership, and (indirectly) capability manifests
through prose, but enumerates none of their shapes. No CHARTER
edit is needed.
- THREATS: THREATS.md cites
quorumand capability-manifest
replay (THREATS §4.7) but does not cite revocation specifically.
After Patch 1–2 land, a future THREATS audit (D-306-style) might
add a single sentence acknowledging that revocation is now
protected by domain-separation, but that's not a D-312
deliverable.
- SPEC §4.2 hardening table: the rows for
dream,silence,
quorum are correct as-written. No SPEC change is needed for
the three "ratify as-is" targets.
What I considered but rejected
- Adding a
herald.attestation.dream.v1tag for symmetry.
Rejected: dream protections are routing/archive rules
(sigil:dreamer conduit role) plus content-sanitisation, not
signing-scope discrimination. A dedicated tag would not change
any verifier check that isn't already done another way.
- Adding a
herald.attestation.silence.v1tag for symmetry.
Rejected: same reasoning. The non-contentful rule is unverifiable
cryptographically; a tag wouldn't help.
- Strengthening the
dreamshape with aconfidencefield.
Considered but explicitly out of scope per the brief ("specify
shapes that SPEC/EXT name but don't fully shape" — not "improve
the shapes"). Worker-B's three-field shape is the minimum that
SPEC §4.2 implies.
- Adding a
categoryregistry fordream/silence. Worth
doing but a separate task. The shape rule says the category
field MUST be <registry-id>; the registry of valid categories
is not enumerated anywhere yet. Recommend a follow-on task
D-316 (or similar) to publish the dream/silence category lists.
- Touching Appendix A's
.v2SPEC §3.6 column entries. D-308
fixed SPEC.md to .v1 but did not update this Appendix's left
column (per its RECEIPT, "Did not modify CHARTER or registries").
The Appendix is now stale. Fixing it is not D-312 work; flagged
to NOTES.md for whoever picks up the post-D-308 registry sweep.
Verification
After patches 1–5, the following should hold:
1. grep -nE "herald\\.revoke_capability_manifest" EXT.md returns
the new tag row in §12 and the in-line reference in §4.6 (≥ 2
matches).
2. grep -nE "herald\\.revoke_capability_manifest" registries/standard-attestations.md
returns the §4.3 entry's "Domain-separation tag" line, the
validation rule, and the new Appendix A row (≥ 3 matches).
3. The phrase "no dedicated tag" no longer applies to
revoke_capability_manifest anywhere in the tree.
4. Worker-B's INTERPRETATION I-3 footprint shrinks: F-5 (dream/
silence shapes) is now ratified, F-6 (revoke tag) is now closed.
The remaining F-3 (refusal reason-code registry) is D-310's
responsibility — already in flight.
5. The glossary-check tool (T-019) should not introduce new
findings on the patched docs. (revoke_capability_manifest
was already an attestation-not-in-registry warning candidate
pre-D-312, but worker-B's §4.3 entry already pre-empted it.)
A future Herald-attestation registry (out of scope; mentioned in
D-309 NOTES) should also document the revocation form on the
Herald-side; D-312's domain-separation tag remains the canonical
signing-scope marker for the agent-side revocation.