docs/adr/0060-rdap-origin-guard-fail-closed.md
Rama master · réplica de lectura del repositorio original.
# ADR-0060: RDAP Origin-Guard Fail-Closed — a Broken Resolver Never Consults the Second Leg
## Metadata
| Field | Value |
| -------------- | ------------------------------------------------ |
| **Status** | Accepted |
| **Date** | 2026-08-17 |
| **Authors** | AlessioBrillo |
| **Deciders** | AlessioBrillo |
| **Supersedes** | N/A |
| **Relates to** | ADR-0002, ADR-0039, ADR-0050, ADR-0051, ADR-0058 |
| **Project** | DOMINUS |
## Context
ADR-0058 introduced the per-TLD origin-overlap guard for the RDAP 2-of-2
consensus gate: before consulting the second leg, the stage resolves the
candidate TLD's authoritative RDAP origins (IANA bootstrap, rdap.org
excluded) and skips the second opinion — downgrading the verdict as
unverifiable — whenever the second leg's origin is authoritative for that
TLD. The guard exists to stop a rubber stamp: with the default `rdap.org`
second leg, an authoritative registry answering the primary query could
confirm its own verdict and pass a wrong "Available" straight into scoring.
The guard's resolver failure path was **fail-open**. `resolveAuthoritativeOrigins`
caught every rejection from the resolver and returned `undefined` — the same
value used for "guard not configured" — logging "origin-overlap check skipped
for the TLD" and proceeding to consult the second leg. When the IANA
bootstrap is unreachable (network partition, DNS failure, API outage), the
guard cannot prove the second leg is disjoint from the TLD's authoritative
origins; consulting it can produce exactly the rubber-stamp verdict the guard
exists to prevent. ADR-0002 mandates conservatism: the engine must be more
conservative than the commercial appraisers, not more generous. A guard that
disarms itself on the very outage it is meant to protect against violates
that principle and the ADR-0039 fail-closed posture the DNS and RDAP gates
already follow.
## Decision Drivers
1. **Conservatism (ADR-0002)** — "more conservative, never more generous";
a rubber-stamped second opinion must not pass under any failure mode.
2. **Fail-closed parity (ADR-0039/ADR-0050/ADR-0058)** — the RDAP gate
already downgrades on second-leg errors and timeouts; a resolver failure
is the same class of uncertainty and must behave identically.
3. **Distinguishable semantics** — "guard not configured" (operator chose no
guard) and "guard configured but broken" (operator expects protection)
must not collapse into one value; the latter must fail closed.
4. **Observability** — guard failures must be countable separately from
generic unverifiable verdicts, so degraded runs are attributable
(ADR-0039 degradation flagging).
## Considered Options
### Option A: Fail-closed resolver handling (chosen)
A resolver rejection propagates to the per-candidate handler, which skips
the second leg, increments `unverifiable` and a new `originGuardUnavailable`
counter, and downgrades the candidate to `Unknown` (filtered). Only an
explicitly unconfigured resolver — or an unparsable secondary endpoint,
which cannot prove overlap (ADR-0058 stance on typos) — preserves the
consult-the-second-leg behaviour. A new Prometheus series
`dominus_rdap_consensus_origin_guard_unavailable_total` and a matching field
on the JSON snapshot make failures attributable; the existing degraded-run
flagging (unverifiable ratio) covers the run-level signal automatically.
- **Pros**: rubber stamp impossible on resolver failure; semantics of
`undefined` stay "guard off, operator's choice"; zero-config deployments
unaffected; new counter surfaces the failure mode.
- **Cons**: an IANA bootstrap outage now degrades Available verdicts for
every affected TLD (the intended conservative cost); requires updating the
ADR-0058 test that documented the fail-open behaviour.
### Option B: Cache the failure and keep consulting the second leg (status quo)
The resolver failure is logged once per TLD and the second opinion proceeds
unchecked, as today.
- **Pros**: no behaviour change; verdicts survive bootstrap outages.
- **Cons**: the guard disarms on the exact outage class it protects against;
a rubber-stamped "Available" can reach scoring; violates ADR-0002 and the
fail-closed posture of ADR-0039/0050/0058.
### Option C: Resolver failure forces a re-check through WHOIS
Instead of skipping the second leg, route the candidate through the WHOIS
rescue leg (ADR-0051) regardless of the rescue flag.
- **Pros**: a second opinion still happens, via an independent channel.
- **Cons**: conflates two gates; requires WHOIS to be wired; the WHOIS rescue
is opt-in by design (ADR-0051) and forcing it changes billing/egress
characteristics; more complex than the failure warrants.
## Decision
Option A. The guard's resolver failure is fail-closed: the second leg is
never consulted when the guard cannot prove disjointness, the verdict is
downgraded as unverifiable, and the failure is counted in a new
`originGuardUnavailable` tally surfaced on the Prometheus endpoint.
## Consequences
### Positive
- A broken IANA bootstrap cannot smuggle a rubber-stamped second opinion
into scoring — the conservative posture holds under outage.
- Guard state is unambiguous: `undefined` means "operator disabled the
guard", never "guard failed".
- The failure is observable (new counter + JSON field) and participates in
the existing degraded-run flagging.
### Negative
- A bootstrap outage now downgrades every Available verdict in affected
TLDs until the resolver recovers — reduced yield during outages, the
deliberate price of the conservative default.
### Neutral
- The ADR-0058 stage test documenting the fail-open path was rewritten to
assert the fail-closed behaviour.