Refusal reason-code registry
Status: Normative for HERALD v0.2 conformance.
Source: EXT §10.1 (envelope shape), §10.2 (rate publishing), §10.3 (per-sigil notice), §5.4 (refusal transparency).
A refusal attestation's reason field MUST contain a code from this registry. A refusal whose reason is not in the registry — and not prefixed x- per the experimental-code rule below — MUST be treated by verifiers as malformed and rejected.
This registry is referenced by:
- EXT §5.4 ("per-reason histogram (with reason codes drawn from a reason-code registry, [CHARTER §refusal-reasons])") — the existing
[CHARTER §refusal-reasons]cross-reference is a phantom; the registry lives here. Reviewer should patch the EXT cite to point to this document. See Migration notes below. - EXT §10.1 (
reason: "<registry-id>"). - EXT §10.2 (per-reason histogram in
refusal_report). - EXT §10.3 (per-sigil notice's "dominant reason code").
Identifier convention
- Lowercase, underscore-separated, single namespace (no
refusal:prefix; thereasonfield is already inside a refusal attestation, so the namespace is unambiguous). - Matches the EXT §6.1 underscored convention (D-311).
- Stable across protocol minor versions (a code is only deprecated when the corresponding refusal pattern itself is removed, not on every spec rev).
Schema per entry
Each entry has five fields:
id— the canonical underscored identifier.- Description — one sentence; what behaviour the conduit is reporting.
- Appealable —
yes/no/sometimes. Ifyes, the per-sigil notice ([EXT §10.3]) MUST include an appeals contact. Ifsometimes, jurisdiction-dependent. - Privacy-sensitive —
yes/no/partial. Ifyesorpartial, per-sigil notices and reports MUST NOT include subject content or third-party identifiers in this category beyond the dominant-reason code itself. Thevalue.refusedenvelope-id stays opaque. - Related sections — SPEC/EXT/THREATS sections that motivate the code.
Categories
Codes are grouped into seven categories for histogram aggregation. The category is not part of the wire format — it's a documentation convenience for refusal_report summary structures.
1. Rate / capacity (transient, retry-friendly)
2. Capability / policy (semantic mismatch)
3. Legal / compelled (refusals the conduit is required to make)
4. Authorisation (sender not entitled)
5. Sigil state (origin sigil's lifecycle blocks the request)
6. Envelope validity (the envelope itself is broken or stale)
7. Quorum / confidence (multi-party requirements unmet)
Registry — v0.2
Rate / capacity
| ID | Description | Appealable | Privacy-sensitive | Related |
|---|---|---|---|---|
rate_limit_exceeded | Sender exceeded the conduit's published rate limit. | yes | no | EXT §5.4, [SPEC §5.2] |
capacity_exceeded | Conduit is overloaded; transient. Senders SHOULD apply backoff. | yes | no | EXT §5.4 |
cost_floor_breached | Sender did not meet the conduit's published rate floor (per capability manifest). | yes | no | [EXT §4.1] (capability manifest), [THREATS §subsidy-as-attack] |
Capability / policy
| ID | Description | Appealable | Privacy-sensitive | Related |
|---|---|---|---|---|
capability_not_supported | Conduit does not support the requested capability or transport. Senders should re-resolve via the capability manifest. | no | no | [EXT §4.1] |
policy_violation | Envelope content violates the conduit's published policy. The policy MUST be a published, named document; arbitrary "we don't like it" is non-conformant. | yes | partial | [EXT §10.3] |
privacy_redaction_required | Conduit's privacy filter detected sensitive content (e.g. PII) and conduit lacks the redaction capability. Sender may re-submit with redaction. | sometimes | yes | [THREATS §exfiltration-via-relay] |
Legal / compelled
| ID | Description | Appealable | Privacy-sensitive | Related |
|---|---|---|---|---|
legal_compulsion | Refusal under a legal/regulatory obligation that the conduit is permitted to disclose. The disclosure SHOULD be in the per-sigil notice's appeals contact. | sometimes | yes | [THREATS §refusal-as-denial-of-speech] |
legal_compulsion_undisclosed | Refusal under a legal obligation the conduit CANNOT disclose (gag-bound). Conduits operating in jurisdictions where gag orders are possible MUST emit this code when applicable; using legal_compulsion to mask a gagged refusal is non-conformant. Verifiers and senders SHOULD treat this code as a strong signal to seek alternate routes (per fallback, [EXT §10.4]). | no | yes | [THREATS §refusal-as-denial-of-speech], [SPEC §382] |
sanctions_screening | Sender's sigil or apparent destination is on a sanctions list the conduit honours. Distinct from legal_compulsion because sanctions are typically disclosable but jurisdiction-specific. | sometimes | yes | — |
Authorisation
| ID | Description | Appealable | Privacy-sensitive | Related |
|---|---|---|---|---|
authorisation_required | Sender is not authorised on this conduit (private conduit, allowlist-only, or paid-only without payment proof). | sometimes | no | [EXT §5.1] |
quorum_membership_required | Sender is required to be a member of a named quorum (per Herald or capability rules) and is not. | yes | no | [EXT §3.3], [EXT §4.x] (quorum membership — see D-312) |
Sigil state
| ID | Description | Appealable | Privacy-sensitive | Related |
|---|---|---|---|---|
sigil_unknown | Origin sigil has no birth attestation findable by the conduit. Sender should publish or republish birth. | yes | no | [SPEC §2.1, §2.4] |
sigil_deprecated | Origin sigil is in deprecated state per its own attestation; the conduit honours the deprecation. | no | no | [SPEC §2.4], [EXT §1] |
sigil_on_bestiary | Origin sigil appears on a Bestiary list the conduit honours. The notice SHOULD name the Bestiary list (not the listing reason — that's bestiary-side due process). | yes | no | [EXT §8] |
lineage_unproven | Required lineage attestation for this envelope is missing or fails the renderer-conformance distinct-rendering rule (T-021). | yes | no | [EXT §3.2], [SPEC §2.3], (T-021 future) |
Envelope validity
| ID | Description | Appealable | Privacy-sensitive | Related |
|---|---|---|---|---|
envelope_invalid_signature | Origin signature failed verification. Hard refusal; sender should not retry without re-signing. | no | no | [SPEC §3.4], [EXT §6] |
envelope_expired | Envelope's expires field is in the past at the moment the conduit considers relay. | no | no | [SPEC §3.1] |
envelope_replay | Nonce already seen within the conduit's replay-cache window. | no | no | [SPEC §3.7] |
envelope_malformed | Envelope failed CBOR-deterministic canonicalisation or required-field check. | no | no | [SPEC §3.5], [EXT §13 — D-308 dom-sep] |
trace_parent_invalid | trace_parent is missing, malformed, or violates the trace-parent binding rule (E.trace ≠ parent.trace). | no | no | [SPEC §3.3a] |
routing_unknown | Destination sigil is not addressable via this conduit; sender should re-resolve. The fallback field SHOULD be populated when known. | yes | no | [EXT §11] (discovery), [EXT §10.4] |
Quorum / confidence
| ID | Description | Appealable | Privacy-sensitive | Related |
|---|---|---|---|---|
confidence_below_floor | Sender's published confidence on this envelope is below the conduit's floor (optional capability). The floor MUST be in the conduit's capability manifest. | yes | no | [EXT §4.1] |
quorum_not_met | Envelope required Herald-quorum endorsement that did not reach the configured threshold. | yes | no | [EXT §3.3] |
Experimental codes (x- prefix)
A conduit MAY use a reason code outside this registry if and only if it is prefixed x- (e.g. x-greylisting-trial). Rules:
- Verifiers MUST NOT reject envelopes that reference
x--prefixed reason codes; they SHOULD log and pass through. x-codes MUST NOT count toward conformance reporting categories. A conduit reporting >5% of refusals asx--prefixed is non-conformant for refusal-transparency purposes — the registry is not a backdoor.- A conduit using a
x-code that proves stable should propose registration via the process below.
Registration process for new codes
A new (non-x-) reason code is added by:
1. Proposing the code in a R-NNN research task or a D-NNN discovery task with the same five fields (id, description, appealable, privacy-sensitive, related sections).
2. Discussion period of at least 30 days on the registry. (For v0.2 draft, this is waived; future stable versions enforce it.)
3. Promotion by adding the code to this registry document and bumping the registry version. Existing implementations that don't recognise the new code MUST treat it as malformed and refuse.
4. No deletions without deprecation. Per the EXT §13 lifecycle rule (12-month deprecation per identifier), removing a reason code requires a notice period and a transition period during which both old and new codes are accepted.
Privacy-sensitive code rules
Codes marked privacy-sensitive: yes or partial carry these additional constraints in refusal_report and per-sigil notice:
- Per-sigil notice ([EXT §10.3]) MUST NOT include subject content for any privacy-sensitive code.
refusal_report([EXT §10.2]) MAY include the count under the code, but MUST NOT include sigils, content fragments, or third-party identifiers under privacy-sensitive codes.- For
legal_compulsion_undisclosedspecifically: the conduit's per-sigil notice MUST still be issued (sigil, code, appeals contact); the conduit MUST NOT name the compelling authority or the compulsion's content.
Migration notes
EXT §5.4 (line 388) currently reads:
"...per-reason histogram (with reason codes drawn from a reason-code registry, [CHARTER §refusal-reasons])."
This reference is a phantom anchor — [CHARTER §refusal-reasons] does not exist in the current CHARTER (1019 lines, no such section). The registry lives in EXT (this document, normative for protocol shape). CHARTER may, optionally, gloss the cultural meaning of refusal in a separate section; that's out of scope for D-310.
Proposed EXT patch (reviewer should apply):
- per-reason histogram (with reason codes drawn from a reason-code registry, [CHARTER §refusal-reasons]).
+ per-reason histogram (with reason codes drawn from the reason-code registry, [EXT §10.5]).And add a new §10.5 at the end of EXT §10:
### 10.5 Reason-code registry
The `reason` field of a `refusal` attestation MUST be a code drawn from the
HERALD refusal reason-code registry, distributed as `registries/refusal-reasons.md`.
The registry specifies the canonical underscored identifier, whether the
refusal is appealable, whether it is privacy-sensitive, and the related
SPEC/EXT/THREATS sections.
Codes outside the registry are non-conformant unless prefixed `x-` (experimental).
A `x-` code MUST NOT exceed 5% of refusals in any reporting window.
A new code is added per the registration process documented in
`registries/refusal-reasons.md` (registration → discussion → promotion → no
deletions without deprecation).Appendix A — Worked examples
A.1 Standard rate-limit refusal
{
"type": "refusal",
"value": {
"refused": "env:7f3a2b8e9c1d2f4a",
"reason": "rate_limit_exceeded",
"by": "sigil:relay.example.k8...",
"ts": 1746460800,
"fallback": null
}
}Per-sigil notice (only required if rate against this sigil exceeds threshold):
conduitsigil:relay.example.k8..., period2026-05-05, targetsigil:agent.alpha..., refused 240/2400 attempts (10%), dominant reasonrate_limit_exceeded, appeals:mailto:[email protected].
A.2 Gag-bound refusal
{
"type": "refusal",
"value": {
"refused": "env:09a4...",
"reason": "legal_compulsion_undisclosed",
"by": "sigil:relay.example.k8...",
"ts": 1746460800,
"fallback": "sigil:relay.alt.j2..."
}
}Per-sigil notice:
conduitsigil:relay.example.k8..., period2026-05-05, targetsigil:agent.alpha..., refused 100% underlegal_compulsion_undisclosed, fallbacksigil:relay.alt.j2..., appeals: see fallback.
The conduit names the code, the count, and the fallback. It does NOT name the compelling authority, the order's contents, or the affected message bodies. The code itself is the disclosure that something is being compelled.
A.3 Bestiary refusal
{
"type": "refusal",
"value": {
"refused": "env:c3d5...",
"reason": "sigil_on_bestiary",
"by": "sigil:relay.example.k8...",
"ts": 1746460800,
"fallback": null
}
}Per-sigil notice MUST name the Bestiary list (e.g. "spam-2026-05") so the affected sigil can pursue the bestiary's own due-process channel ([EXT §8]). It MUST NOT include the listing reason — that's bestiary-side, not conduit-side.
v0.2, 2026-05-05.