Files
buzz/docs/nips/NIP-FI-CONF.md
Tyler d97780b477 docs(nips): comprehensive NIP-FI — core + claimable profiles (EDGE/LIFECYCLE/DELEG/CONF) (#5946)
## What

Comprehensive NIP-FI against `main`: one normative core plus four
separately
claimable profiles, replacing the single-document structure of #3726
(which was
based on #1485's branch, not `main`). Six documents, 1,975 lines, docs
only.

- **`NIP-FI.md` (core, 632 lines)** — issuer-qualified identity `(iss,
sub)`,
independent Nostr proof, client-attached assertion, partial bijection
with
durable tombstones, atomic final admission, bounded leases, private
denials
  with a closed response vocabulary, retire/revoke/rotate, two contract
identities (`assertion_policy_id`, `transport_contract_id`), per-policy
  `skew` / `maximum_assertion_age` / `maximum_status_age` with missing
configuration denying, a closed token-class rule (`at+jwt`,
`nip-fi+jwt`,
  named compatibility; ID tokens always deny), declared freshness class
  (`offline-jwt` | `current-status`), server-declared body authorization
  relevance (NIP-98 payload-binding fix, including a `payload` tag on an
irrelevant-body operation), BCP 14, "equivalent" defined over identity /
bounds / provenance classes, a compact non-normative worked wire
example,
  and a non-normative comparison with DPoP, mTLS-bound tokens, and HTTP
  Message Signatures. FI-INV-01..16 are normative core text. The
behavioral-oracle table lists exactly 30 oracle IDs, one per row, with
no
  shorthand.
- **`NIP-FI-EDGE.md`** — trusted-edge surface: the
`trusted-proxy-hmac-v2`
envelope + canonicalization, or a private authenticated-edge adapter
under a
reviewed contract; `authorization_domain_id` derivation (exact 16 RFC
9562
UUID bytes); `proof_transport_code` registry (0x01 NIP-42, 0x02 NIP-98;
  0x03 Git smart-HTTP and 0x04 Blossom reserved pending their transport
contracts; 0x05–0x7f unassigned pending published stable specifications)
  + extension procedure; body-acquisition bounds; three normative test
vectors. An independent Nostr proof (the NIP-98 event in
`Authorization`,
  which reaches the verifier byte-identical, or the NIP-42 event after
connect) is the only decision input outside the MAC; absent or
incomplete
  provenance on an edge-required route is `missing_evidence`,
  present-but-failing provenance is `evidence_rejected`.
  Header-trust-without-provenance is nonconformant.
- **`NIP-FI-LIFECYCLE.md`** — provision / disable / re-enable /
administrative
expiry (`binding_not_after`) / pending-replacement lineage, one
conformance
trace per privileged transition; every binding-creating transition
declares
whether it continues or establishes a grant; a private-condition table
for
  CONF enumeration agreement.
- **`NIP-FI-DELEG.md`** — delegated agents; explicit temporal boundaries
matching core's inclusive-`nbf`/exclusive-`exp` idiom, with the
delegated
`skew` configured by this profile; lease deadline anchored to the lease
  issue instant; strict path separation — a delegated request carries no
assertion or provenance field, so it cannot traverse an edge-provenance
  route and uses ingress on which NIP-FI-EDGE is not required.
- **`NIP-FI-CONF.md`** — conformance evidence: an immutable claim tuple
  including the governing document revision and exit fixture digest; the
  complete 16-row denial-fixture enumeration with three mechanical
  enumeration-agreement checks; mutation adequacy with a countable
  denominator — one retained killed mutant per literal oracle-table row
(30 core + 6 EDGE + 11 LIFECYCLE + 7 DELEG + 4 CONF = 58), rows selected
structurally by their first cell, never by section title, with the
release
gate and CONF's own oracle rows stated in the same listed-oracle terms
and
  a mutant defined for CONF's own report- and suite-subject oracles; an
  interoperability exit test compared over signing inputs (per-transport
NIP-01 serialization for the NIP-98 and NIP-42 proofs; decoded protected
header and claims as JSON values for the assertion), with a shared exit
  fixture pinning complete pre-signature header/claim JSON and complete
unsigned event fields for both transports, and mandatory negative
controls.
  `FI-CONF-INTEROP-EXIT` is `deferred` with reason
`no-independent-implementation` until a second independent
implementation
  exists; the canonical fixture is editor-authored at
`docs/nips/fixtures/nip-fi-conf-exit.json` and is **not in this PR** —
until
it is published a claim records `pending-canonical-fixture`, valid only
  while the exit test is deferred. Explicit not-applicable dispositions,
including `offline-jwt` deployments for the two current-status oracles
and
  absence of a revocation-bounded external capability projection for
  `FI-TRACE-CAPABILITY-REVOCATION`.
- **`NIP-FI-MODEL.md`** — non-normative companion; defines no
requirement or
  conformance claim and is not claimable.

## Why

The prior draft rated 9 (soundness) / 6 (minimalness) / 7 (elegance) /
7 (correctness) in adversarial + comparative review. This restructure
keeps the
two-invariant spine untouched, makes everything else a claimable
profile, and
collapses five stacked versioning mechanisms into two contract
identities.

Mutation adequacy counts one mutant per literal oracle-table row — a set
two
implementers enumerate identically — instead of "each normative
requirement,"
which had four defensible readings.

Resolved product calls (owner-approved):
1. Enrollment/denial posture is private — boolean enrollment discovery,
TOFU
extension claim not self-advertised, `key_mismatch →
authorization_denied`
   joins the denial anonymity set, and replayed evidence is classed
   `authorization_denied` so resubmission reveals nothing about commit.
2. Revocation honesty — only `current-status` deployments may advertise
an
   unconditional residual-revocation bound; `offline-jwt` advertises
unbounded/unknown. Access tokens keep RFC 9068 `at+jwt`; `nip-fi+jwt` is
   reserved for a separately minted Buzz assertion.

## Acceptance bar

- Nothing in core is deletable without losing a stated core guarantee.
- From the core document plus the CONF exit fixture, a second
implementer can
produce a valid request equal over the request compared object (signing
  inputs), and a byte-exact public denial per class — no reference
  implementation.
- Every oracle-table row ships a retained killed mutant satisfying only
the
  entry it was selected for.
- Both deployment profiles (trusted proxy = EDGE, client-held OIDC =
core
  client-attached) pass the same lifecycle conformance suite.

## Status

Ready at head e720a5cda. Every revision below is on this branch: the
2026-08-17 and 2026-08-18 review laps (Wren, Dawn, Perci, Sami, Mari,
Quinn)
closed at 513e03b06, 17d455a9b, and 4f913a813; the 2026-08-20 external
line-by-line review (R1–R10, R12) closed across 56e74141e..772ba7a72;
the
2026-08-20/21 adversarial lap (#6437) squash-merged as b8db13d88; the
round-3
external review (R13, R14), the DELEG×EDGE composition note, and three
terminology nits closed at e720a5cda; R11 is this description. Oracle
census: 58 (30 core, 6 EDGE, 11 LIFECYCLE, 7 DELEG, 4 CONF).

Known follow-ups, filed after merge and out of scope here: adapter-only
edge
deployments and FI-EDGE claimability; an EDGE private-condition table
for
CONF's enumeration-agreement check; an enumerable definition of the
positive/negative oracle sets used by the global mutation controls;
NIP-OA's clock-free verification versus NIP-FI-DELEG's wall-clock
expiry.

Supersedes #3726 as the spec vehicle; #1485 remains the design-history
anchor.

---------

Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Signed-off-by: Max <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
Signed-off-by: Perci <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@buzz.block.builderlab.xyz>
Signed-off-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Signed-off-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@buzz.block.builderlab.xyz>
Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
Signed-off-by: Meli <5aaa86bce934fc3445fc254aab560a40923f10252f92107e665073dede0e04d3@buzz.block.builderlab.xyz>
Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Max <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
Co-authored-by: Perci <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@buzz.block.builderlab.xyz>
Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Co-authored-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@buzz.block.builderlab.xyz>
Co-authored-by: Meli <5aaa86bce934fc3445fc254aab560a40923f10252f92107e665073dede0e04d3@buzz.block.builderlab.xyz>
2026-08-21 17:18:24 -04:00

376 lines
21 KiB
Markdown

NIP-FI-CONF
===========
Conformance evidence profile
----------------------------
`draft` `optional`
**Dependencies**: NIP-FI core. Applies additionally to any claimed
NIP-FI-EDGE, NIP-FI-LIFECYCLE, and NIP-FI-DELEG profile.
The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT", and
"MAY" in this document are to be interpreted as described in BCP 14 (RFC 2119
and RFC 8174) when, and only when, they appear in all capitals.
## Abstract
NIP-FI core and its profiles state required behavior. This profile states what
counts as evidence that an implementation has it: the claim unit, the evidence
rules, the complete denial-fixture enumeration, mutation adequacy, and the
interoperability exit test.
This profile is separately claimable and is never advertised in discovery:
conformance is a property of a reviewed revision, not a wire feature. It
defines no wire behavior, denial mapping, invariant, or admission rule; where
it names one, NIP-FI core or the owning profile is normative.
## Claim unit
A conformance claim names exactly one immutable tuple:
```text
(implementation revision,
adapter revision,
build artifact digest,
deployment revision,
governing document revision,
exit fixture digest,
claimed profiles,
assertion_policy_id,
transport_contract_id,
enrollment mode)
```
Changing any element creates a new claim. Results from one tuple MUST NOT be
carried into another. A report contains every applicable oracle from core and
every claimed profile exactly once, with status `pass` or `not-applicable`
only, except that `FI-CONF-INTEROP-EXIT` alone may instead carry `deferred`
under the condition in **Interoperability exit test**. Blank, skipped,
expected-failure, and not-run results cannot support a claim
(`FI-CONF-CLAIM-COMPLETE`).
Enrollment mode is part of the claim unit and is private: it is recorded in
the access-controlled report, never in discovery or any public artifact.
## Evidence rules
Each passing oracle records the claim tuple, a stable test identifier and
adapter entry point, the command with start time, end time, exit status, and
any random seed, the synthetic input or a privacy-safe digest of it, the
before-and-after authoritative state relevant to the oracle, the expected and
observed outcomes, and artifact locations with SHA-256 digests. Stateful
oracles use an isolated database or namespace and inspect committed state
rather than inferring it from a response. Concurrency oracles record every
contender and the single serialized outcome. Time-boundary oracles use a
controlled clock.
Adapters MUST drive public or production-equivalent entry points. A storage
helper MAY inspect state or inject a dependency outage; it MUST NOT replace the
operation under test. Calling an internal authorization function without
traversing the protected ingress does not satisfy ingress coverage.
None of the following satisfies any oracle: searching source, documentation,
schemas, or binaries for a token; asserting that a route calls a named
function; recording a test name without its execution result; using a mock to
prove a deployed network boundary; citing a check from another revision; or
marking an oracle passed because the feature is configured.
`FI-TRACE-TOFU-THEFT` takes an access-controlled **configuration** witness
only; under the private-posture rule no discovery witness for enrollment mode
can exist. Discovery invariance is proved separately by
`FI-TRACE-DISCOVERY-PRIVATE`.
Requirements marked `[deployment artifact: ...]` in core or a profile are
evidenced by the named access-controlled review record at the claimed
deployment revision, not by a behavioral oracle. A claim listing an artifact
without the record is incomplete.
Reports and artifacts hold private deployment detail and MUST remain access
controlled. They MUST NOT enter public reports, examples, discovery, or
protocol output, and MUST NOT contain raw assertions, secrets, or unredacted
`iss`, `sub`, or claim values. The shared exit fixture is exempt: its values
are synthetic by construction and name no real principal, issuer, or key.
## Denial fixtures
`FI-TRACE-DENIAL-ORACLE` requires one fixture per **private condition**, not
one per public class; a per-class suite compares a class against itself. The
enumeration below is the required fixture set (`FI-CONF-DENIAL-FIXTURES`). The
public-class column restates NIP-FI core, which owns the mapping and the bytes.
| # | Private condition | Public class | Defined by |
|---|---|---|---|
| 1 | assertion, proof, or delegation evidence absent | `missing_evidence` | core |
| 2 | edge provenance absent or incomplete on an edge-required route (assertion may be present) | `missing_evidence` | NIP-FI-EDGE |
| 3 | evidence present but rejected: signature, key selection, issuer, audience, time, size, ambiguity, token class, body binding, or edge provenance (present but rejected) | `evidence_rejected` | core, NIP-FI-EDGE |
| 4 | replayed evidence — committed replay identity already claimed | `authorization_denied` | core, NIP-FI-EDGE |
| 5 | `key_mismatch` — asserted key is not the proven actor | `authorization_denied` | core |
| 6 | `attestation_required` — attested-key enrollment without a matching key claim | `authorization_denied` | core |
| 7 | `binding_conflict` — either side of the active relation is taken | `authorization_denied` | core |
| 8 | `pair_retired` | `authorization_denied` | core |
| 9 | `key_revoked` | `authorization_denied` | core |
| 10 | `policy_denied` — local operation policy | `authorization_denied` | core |
| 11 | `binding_required` — enrollment policy creates no binding at this request: provisioned mode with no binding, or any unrecognized policy value | `authorization_denied` | core |
| 12 | `identity_disabled` | `authorization_denied` | NIP-FI-LIFECYCLE |
| 13 | `explicit_replacement_required` — pending lineage | `authorization_denied` | NIP-FI-LIFECYCLE |
| 14 | `binding_expired` — administrative expiry | `authorization_denied` | NIP-FI-LIFECYCLE |
| 15 | `delegation_not_current` — owner or relationship no longer current | `authorization_denied` | NIP-FI-DELEG |
| 16 | `dependency_unreadable` | `authorization_unavailable` | core |
Private-condition names are fixture identifiers, not wire values; a deployment
MAY use other internal reason codes if every enumerated condition has a
fixture. Rows for an unclaimed profile are `not-applicable` with absence
evidence. A profile that introduces a private condition MUST add its row; an
unenumerated condition escapes this oracle entirely.
**Enumeration agreement.** `policy_denied` and `dependency_unreadable` are the
*prose-only allowlist*: core conditions that core states in prose and does not
name symbolically. The suite MUST check mechanically at the claimed head, by
symbol and never by row number, and every check MUST be green on the unmutated
documents before any mutant is scored:
1. every symbol core denies by name has a row here attributed to core with the
same public class;
2. the set of symbols in core-attributed rows equals core's symbolic denial set
together with the allowlist, exactly, and the allowlist is disjoint from
that set; and
3. for each claimed profile that owns a private-denial-condition table, the
set of `(identifier, public class)` pairs in that table equals the set of
pairs attributed to that profile here, exactly; a row with multiple owners
contributes its pair to each.
If a later core names an allowlisted symbol, check 2's disjointness fails
until the allowlist entry is deleted, and check 1 validates the promoted
symbol's class.
**Anonymity comparison.** Every `authorization_denied` row is in the
private-state anonymity set. Between two private conditions on one
implementation, every response byte as transmitted MUST agree — transfer
framing included — except values a server cannot hold constant across two
instants, such as `Date`. This is wider than the interoperability object
below: within one implementation, any byte that varies by private condition is
a disclosure, whatever field it sits in.
**Interoperability compared object.** Between two implementations, comparison
is over what core pins and nothing more. Over Nostr: the complete relay message
excluding only the event or subscription identifier echoed from the request,
as compact JSON with no insignificant whitespace per NIP-01. Over HTTP: the
status code; the content per RFC 9110 Section 6.4, after transfer decoding with
chunk framing and trailers excluded; and the exact values of only the header
fields core's denial table names, field names matched case-insensitively per
RFC 9110 Section 5.1. `Content-Length` is not pinned. Header order and unnamed
fields are outside the object, and their values MUST NOT depend on the private
condition. A field core names that an implementation cannot hold constant MUST
be reported with the reason, and its value MUST be independent of the private
condition. If core later pins another field, it joins with no edit here.
**Run discipline.** The oracle runs a fixed positive iteration count on a
pinned isolated runner at the exact claimed head. Before the run the operator
records the environment, public-response corpus, bounds, sampling method,
statistical rule, noise treatment, and acceptance threshold. A breach fails the
gate, MUST NOT trigger an automatic retry, and is retained and investigated
before a separately authorized rerun.
`authorization_unavailable` is observably distinct from `authorization_denied`.
This is accepted residual: it discloses no per-principal state, and collapsing
it would make fail-closed behavior undiagnosable.
**Negative control.** The suite MUST include an implementation deliberately
patched to vary its denial response by private condition, and it MUST fail
this oracle.
## Mutation adequacy
An oracle that cannot fail is untested text that reads as tested. The
denominator is the **listed oracle**: every table row whose first cell names
exactly one complete literal oracle identifier, in NIP-FI core, in each claimed
normative profile, and in this document when CONF is claimed — selected by
that cell, not by section title. It is not the set of normative sentences, RFC
2119 keywords, or invariant labels, none of which two readers enumerate alike.
For each listed oracle the suite MUST retain at least one **mutant**: an
implementation variant that violates a requirement that oracle governs,
together with that oracle's failing output (`FI-CONF-MUTATION`). Evidence is
the exact patch identity, the oracle identifier, and the retained failure
output at the claimed head. For this document's own oracles the implementation
under test includes the conformance suite and its report; a mutant is a single
variant of the suite or report that the entry's own oracle rejects.
While `FI-CONF-INTEROP-EXIT` is validly deferred it remains in claim
completeness but is excluded from this section's mutation and global-control
obligations, since its failing output cannot exist without the run. Both
obligations attach with the run and MUST be discharged before either
implementation's interoperable conformance claim is accepted. No other
oracle's obligation under this section is deferrable.
Normative prose outside the oracle tables remains binding but is not a second
denominator. Prose that no listed oracle can detect is untestable text: add the
oracle that detects it, or delete it.
1. **One at a time.** Mutants are applied singly against an otherwise
unmodified implementation, so layered defenses cannot mask each other.
2. **Attribution.** The kill MUST come from the entry's own oracle. A mutant
killed only by another oracle establishes coverage for neither.
3. **One entry per mutant.** A mutant satisfies only the entry it was selected
for, even when it also kills other oracles.
4. **Reachability.** The suite MUST witness that a fixture reaches the mutated
decision, not merely the enclosing operation.
5. **Survivors are recorded.** A mutant its named oracle fails to kill is a
defect in the specification or the suite. It is recorded with that
disposition and MUST NOT be waived or replaced by an easier mutant.
Two global controls bound the suite. A deny-everything implementation MUST
fail every positive oracle; an allow-everything implementation MUST fail every
negative oracle. Neither substitutes for per-entry mutants.
## Interoperability exit test
A claim of core conformance requires evidence that the documents alone are
sufficient to build against (`FI-CONF-INTEROP-EXIT`). Two implementations that
have not shared code and have not consulted a common reference implementation
each produce, from NIP-FI core and any claimed profile documents alone:
- one valid `client-attached` request, over WebSocket upgrade and over HTTP,
compared over its signing inputs as defined below; and
- one byte-exact public denial response for each of the four public classes,
on each transport where the class can be decided, compared over the
interoperability compared object under **Denial fixtures**.
Independence is a claim about code, not inputs: two implementations given
different issuers, keys, or clocks cannot produce equal bytes. The run is
therefore parameterized by a **shared exit fixture** that both sides load and
neither side authors:
- one issuer identity and one JWK set, including the private key needed to
mint assertions and the `kid` selecting it;
- one assertion per denial class and one for the valid request, each as
complete pre-signature protected-header and claim-set JSON values —
including `alg`, `typ`, `kid`, every member the policy allows, and fixed
`iss`, `sub`, `aud`, `nostr_pubkey`, `client_id`, `iat`, `exp`, and token
class;
- one Nostr secret key for the proof, with the complete unsigned event fields
for each transport — the NIP-98 event over HTTP and the NIP-42 event with
its challenge and relay values over the WebSocket upgrade — including
`created_at`;
- one frozen evaluation instant, and the skew and lifetime bounds in force;
and
- the domain, target resource, operation, and enrollment policy for each case.
The canonical fixture is authored by this document's editors, not by any
claiming implementation, and MUST be published as a single file at
`docs/nips/fixtures/nip-fi-conf-exit.json` in the same repository as these
documents, with its SHA-256 digest, before any `FI-CONF-INTEROP-EXIT` run.
Both sides MUST load that file, MUST verify the digest before the run, and
MUST record the digest with the evidence; a run against any other fixture
instance is not `FI-CONF-INTEROP-EXIT` evidence. While the canonical fixture
is unpublished, the claim tuple's exit fixture digest records the reserved
value `pending-canonical-fixture`, valid only in a claim whose
`FI-CONF-INTEROP-EXIT` result is `deferred`. Publication changes the element
and therefore creates a new claim.
**Request compared object.** Signature octets are excluded, because conforming
implementations need not agree on them (randomized `ES256` and fresh-aux
BIP-340 do not) and no document here pins JWS or JSON member order. The
compared object is the **signing inputs**: for each transport's Nostr proof,
the NIP-01 serialization the event id is taken over, compared against its own
transport's serialization; for the assertion, the decoded protected header and
claim set compared as JSON values with member order excluded. Every value the
compared object depends on MUST be pinned in the fixture.
The exchanged artifact per case is the complete request and response frame on
each transport — for HTTP the request line, headers, and body and the response
status, headers, and body; for Nostr the complete client and relay messages —
so that a mismatch can be explained from fields outside the compared object.
The test passes when outputs compare equal over their compared objects and
each implementation accepts the other's valid request and reproduces the
other's denials. Exit evidence includes the exchanged artifacts and each
implementation's statement of independence. A divergence traced to an
underspecified value is a defect in the specification, not in either
implementation, and is fixed there.
**Negative control.** One implementation is patched to emit a denial that
differs from the other only outside the compared object — a header core does
not name, or reordered fields — and the run MUST still pass. A run that fails
this control is comparing more than core pins; the exit test is then the
defect. The control is retained with the evidence.
`FI-CONF-INTEROP-EXIT` is REQUIRED only once a second implementation meeting
the independence conditions exists. Until then a conformance claim MUST record
it as deferred with the machine-readable reason
`no-independent-implementation`. A deferred exit test MUST be run and passed
before the second implementation's conformance claim is accepted, and the
first implementation's claim MUST be re-evidenced against that run.
## Applicability
`not-applicable` requires a machine-readable reason and behavioral proof that
the surface is absent:
- edge oracles only when no trusted-edge profile is accepted, none is
advertised, and executable cases reject every trusted-edge evidence shape;
- snapshot-rotation oracles only when no local key or status snapshot source
is configured and executable evidence proves the absence;
- `FI-TRACE-TOFU-THEFT` only when TOFU is neither configurable nor configured
and executable first-use cases deny;
- `FI-TRACE-CURRENT-STATUS-STALE` and `FI-TRACE-CURRENT-STATUS-REVOKED` only
when every configured assertion policy declares freshness class
`offline-jwt` and executable cases prove a presented witness is never
consulted;
- `FI-TRACE-CAPABILITY-REVOCATION` only when no external capability
projection requiring a declared revocation bound is configured, and
executable evidence proves no assertion capability or local-policy value
claims such a bound;
- lifecycle and delegation oracles only when the profile is unclaimed,
disabled, and denied on every ingress; and
- every other oracle is required for an enforcing deployment.
An implementation that supports an optional surface runs its oracles even when
one deployed domain does not activate it.
## Release gate
Before NIP-FI enforcement or discovery is enabled, reviewers verify, at one
reviewed revision, that:
- one immutable claim tuple passes every applicable oracle other than a
validly deferred `FI-CONF-INTEROP-EXIT`;
- if the canonical fixture was published before the review, the tuple's exit
fixture digest is not `pending-canonical-fixture`;
- the protected-ingress inventory has no uncovered or competing authority;
- every listed oracle, other than a validly deferred `FI-CONF-INTEROP-EXIT`,
has a killed, attributed, reachable mutant and every survivor is recorded;
- the denial-fixture enumeration is complete for the claimed profiles and its
negative control fails as required;
- the interoperability exit test has passed against an independent
implementation, or is recorded as deferred because none exists;
- every named deployment artifact exists at the claimed deployment revision;
and
- public and operational sinks pass privacy-canary inspection.
Documentation review, source review, and static scans are review inputs. They
close no item in this gate.
## Behavioral oracles
| ID | Required outcome |
|---|---|
| `FI-CONF-CLAIM-COMPLETE` | A report missing an applicable oracle, duplicating one, carrying a result from another claim tuple, claiming a status other than `pass`/`not-applicable` — or `deferred` on any oracle other than `FI-CONF-INTEROP-EXIT` — or omitting mutant evidence for any oracle other than a deferred `FI-CONF-INTEROP-EXIT`, or recording the exit fixture digest `pending-canonical-fixture` with any `FI-CONF-INTEROP-EXIT` result other than `deferred`, is rejected. |
| `FI-CONF-DENIAL-FIXTURES` | Every enumerated private condition has a fixture; core and each claimed profile pass exact identifier/class/owner enumeration agreement; anonymity-set responses compare byte-identical; the distinguishing negative control fails. |
| `FI-CONF-MUTATION` | Every listed oracle — except `FI-CONF-INTEROP-EXIT` while validly deferred, per **Mutation adequacy** — has a singly-applied, attributed, reachability-witnessed mutant killed by that entry's own oracle; the deny-everything and allow-everything global controls fail every oracle **Mutation adequacy** requires of them, with retained evidence; survivors are recorded, not waived. |
| `FI-CONF-INTEROP-EXIT` | Two independent implementations produce, from the documents alone, valid requests equal over the request compared object and per-class denials equal over the denial compared object, and accept each other's output. |
## Security considerations
Conformance evidence is a privileged artifact: it enumerates private denial
conditions, enrollment posture, and deployment topology that the protocol
deliberately keeps off the wire. Publishing a report, a fixture corpus, or a
mutant catalogue would disclose exactly what `FI-INV-13` and
`FI-TRACE-DISCOVERY-PRIVATE` protect.
A passing suite bounds the behaviors it exercises and nothing else. Mutation
adequacy raises the cost of a masked defect; it does not prove absence of
defects, and a claim that cites this profile as proof of security rather than
of tested behavior is misusing it.