Signed-off-by: Cea Stapleton Cordasco <261786559+cea-block@users.noreply.github.com>
9.2 KiB
NIP-FI runtime conformance and client-status boundary
This document maps Buzz runtime evidence to the normative NIP-FI specification, formal model, and conformance matrix. It does not make a conformance claim. Discovery remains absent until an injected report proves that every applicable row passed against one reviewed implementation revision.
Discovery gate
RelayInfo::build omits both limitation.federated_identity and the top-level
federated_identity object. ConformanceReadyNipFiDiscovery is the only API
that can add them. It requires all of the following:
- a provider-neutral discovery value with at least one unique supported transport and exactly one enrollment mode;
- a positive finite delegated-lease maximum whenever delegation is advertised;
- an exact 40-character reviewed Git revision;
- an injected complete-stack result asserting that every applicable row passed at that same revision; and
- for
trusted-proxy, deployment evidence for both origin isolation and stripping untrusted inbound assertion-header copies.
The reviewed revision and deployment evidence are gate inputs, not public metadata. NIP-11 exposes no issuer URL, tenant URL, claim name, subject, audience, assertion header name, or provisional NIP number. Unsupported behavior is omitted rather than advertised as partially implemented.
The assertion header is singular at every ingress. Repeated field lines,
comma-combined values, invalid UTF-8, and empty values fail closed. An installed
adapter must supply verified transport provenance; header presence alone is
never classified as trusted-proxy. Protected media GET and HEAD remain
inside the enforcing transport inventory unless a separate reviewed public
media policy is selected.
The optional assertion iat check uses the shared injected authorization
clock and accepts a missing claim. A present value must be an unsigned integer
no later than injected verifier time plus the bounded 60-second skew. The
current jsonwebtoken dependency still evaluates exp and nbf against its
own system-clock source. Therefore AS-3.future-iat is covered, but this
candidate does not claim that all assertion-time checks use one injected clock;
that inherited limitation remains part of the full-stack review.
Relay-authenticated client status
Kind 24244 is a Buzz-local, short-lived presentation contract, not NIP-FI
authorization evidence or a NIP-FI conformance surface. A status is signed by
the trusted relay and scoped to an exact server-resolved authorization domain
and event-author key. A current status contains a binding version, a
privacy-keyed policy revision, a monotonic durable status revision, and a
bounded validity window. A withdrawal contains only its exact scope, revision,
and bounded validity window. The two wire states are:
display_current; orwithdrawn, with no lifecycle cause or historical binding fields.
Clients fold only within one trusted relay/domain/author scope. A lower revision is rejected. An equal revision is idempotent only for the identical signed event; a conflicting equal revision is rejected. Expiry, disconnect, relay-key change, authorization-domain change, or event-author change clears presentation. Revision high-water state may survive a transient disconnect, but it is never authority.
The relay adapter is one-way from VerificationOnlyDisposition to a signed
event. It has no dependency on authorization leases, membership mutation,
event ingest, persistence, subscriptions, pub/sub, ordinary delivery, or
NIP-85. The production seam targets an exact authenticated connection and can
construct its permit only from typed evidence that the RFC presentation,
privacy, and dedicated-client gates passed at one exact reviewed revision. The
stock binary supplies no such evidence, key, or transport, so status remains
disabled by default.
The optional label constructor accepts only privacy-approved server
configuration. There is no constructor from issuer data, subject data,
display_name, mutable profile content, or provider decisions. The policy
revision is a length-framed, domain-separated HMAC under an injected dedicated
client-status privacy key; identical provider values are unlinkable under
distinct keys.
Stable row allocation
The synthetic fixture is
crates/buzz-relay/tests/fixtures/nip_fi_trusted_proxy.json. It contains no
production issuer, subject, domain, key, assertion, or tenant data.
| Row | Evidence in this lane | Full-stack state |
|---|---|---|
TR-1.direct-bypass |
Negative origin-isolation fixture | Deployment proof still required |
TR-1.inbound-header-copy |
Negative header-copy fixture | Deployment proof still required |
TR-1.complete-deployment-evidence |
Positive two-control fixture shape | Real enforced-control evidence still required |
AS-3.future-iat |
Optional assertion iat accepts absence and bounded skew; malformed or farther-future values fail closed. Status also rejects future issue time |
Covered by O4 |
BD-1.cross-domain |
Status validation and folding reject cross-domain scope | Authorization-runtime row must pass at the reviewed revision |
SE-4.invalidation |
Withdrawal and client clearing are covered | Lease invalidation runtime must pass at the reviewed revision |
DG-3.no-finite-bound |
Discovery cannot represent delegation without a positive bound | Delegation authorization must pass at the reviewed revision |
OP-2.discovery |
Default omission, provider-neutral fields, and complete-stack gate | Covered here; final claim still requires all rows |
OP-3.absent |
No real-user route or ordinary delivery path | Covered |
OP-3.implemented |
Dedicated exact-connection production seam exists behind typed complete-stack approval | Disabled unless the approval, privacy key, transport, and runtime are explicitly installed |
OP-4.privacy |
Keyed revision, bounded configured label, field/source scans | Covered |
The local client-status rows are:
J3C-STATUS-RELAY-SIGNERJ3C-STATUS-EXACT-SCOPEJ3C-STATUS-FRESHNESSJ3C-STATUS-REVISION-FOLDJ3C-STATUS-WITHDRAWALJ3C-STATUS-PRIVACYJ3C-STATUS-VERIFY-ONLYJ3C-STATUS-DEDICATED-TRANSPORTJ3C-STATUS-REAL-USER-HIDDEN
These J3C rows test presentation safety only. They cannot substitute for any NIP-FI authorization, lifecycle, session, delegation, or deployment row.
Public projection retirement join
The existing opt-in NIP-85 label projection is separate from both NIP-FI
authorization and kind 24244 client status. Its active assertion is TTL
bounded, but a committed revoke or rotate also needs an inactive parameterized
replacement for the old public key.
The public-projection retirement reconciler is a provider-neutral post-commit
seam. It derives private retry work from committed lifecycle rows and persists
only public event coordinates plus opaque binding generations. The relay reads
the exact relay-authored projection and, when active, writes the existing
active=false, expiration=0, label-free replacement. A missing or already
inactive projection is an idempotent terminal result. Store or clock failure
leaves both lifecycle authority and the active projection unchanged while the
work remains retryable.
Active publication and retirement share the identity-key and parameterized event commit boundaries. Server-only head metadata prevents a delayed rotation job from retiring a later legitimate use of the same key. Startup and periodic reconciliation provide restart recovery and Redis/local delivery retry. This lane does not add authenticated lifecycle endpoints or durable operator audit.
Compatibility cases
| Case | Required result |
|---|---|
| Old relay, new client | No discovery or status; client shows no indicator |
| New relay, old client | Unknown ephemeral status is ignored; ordinary event behavior is unchanged |
| Mixed relay fleet before complete conformance | Discovery stays absent; presentation stays disabled |
| Stale client cache | Expired status is cleared; lower or conflicting revisions cannot restore it |
| Spoofed user event | Wrong signer, kind, tags, content, or signature is rejected |
| Cross-domain replay | Exact expected domain and author mismatch is rejected; scope change clears state |
| Relay signing-key rotation | Old presentation is cleared and the new relay key must be trusted independently |
| Privacy-key rotation | Policy revision changes; it grants no authority and clients accept it only at a higher durable status revision |
| Provider or lifecycle outage | Relay issues an opaque withdrawn status only with authoritative revision evidence, otherwise emits nothing |
| Gate disabled | No presentation runtime is installed; no real-user status is delivered |
Mechanical checks
Run from the repository root in the Hermit environment:
cargo test -p buzz-core client_binding_status
cargo test -p buzz-relay authorization_runtime::status
cargo test -p buzz-relay nip11
cargo test -p buzz-relay --test nip_fi_runtime_conformance
The integration test scans ordinary relay ingest, API, router, state, subscription, connection, and protocol sources, plus desktop, mobile, and web client sources. Any reference to the status kind, contract, or disabled delivery method fails the test.