Signed-off-by: Cea Stapleton Cordasco <261786559+cea-block@users.noreply.github.com>
3.7 KiB
Corporate identity
Corporate identity is an optional relay policy enabled with
BUZZ_REQUIRE_CORPORATE_IDENTITY=true. The relay verifies an asymmetric JWT
after the request proves control of a Nostr key, then admits the request only
when the existing community policy also succeeds.
Required JWT policy
BUZZ_CORPORATE_IDENTITY_JWKS_URImust be HTTPS and contain no credentials.- JWTs must have a supported asymmetric algorithm, a
kid, and validexp,iss, andaudclaims. A presentnbfclaim is enforced. BUZZ_CORPORATE_IDENTITY_NPUB_CLAIM, when configured, is mandatory and must equal the authenticated Nostr key. Leaving it unset enables first-use uid-to-key enrollment in the private binding table.- JWKS requests have connect and total timeouts, reject redirects, cap the response at 1 MiB, cache keys for five minutes, and coalesce refreshes.
BUZZ_REQUIRE_CORPORATE_IDENTITY and
BUZZ_ALLOW_CORPORATE_IDENTITY_DELEGATION are strict booleans. Misspellings and
non-UTF-8 values stop configuration loading instead of silently disabling a
gate.
Binding and revocation lifecycle
JWT validation is read-only. The relay creates or refreshes a binding only after admission, allowlist, role, and community membership checks succeed. Invite claims commit the binding, membership, policy evidence, and invite use in one PostgreSQL transaction.
Revocation has three explicit meanings:
principaldisables every key for an issuer-qualified uid. Normal authentication cannot re-enroll the principal with another key.keyrevokes one key but does not silently authorize a replacement.rotationis the audit state written by an explicit atomic old-key to new-key rotation.
WebSocket and audio sessions revalidate the authoritative binding at least every 30 seconds. Direct sessions also close at JWT expiry. Delegated sessions check the owner's binding, so disabling an owner evicts the owner's agents as well as the direct owner session.
Corporate NIP-OA delegation is transport-wide and therefore accepts only an empty conditions string. Conditional tags must be evaluated for a specific operation and are not treated as blanket corporate identity authority.
Privacy and public assertions
BUZZ_CORPORATE_IDENTITY_DISPLAY_CLAIM is private. Its default (email) is
stored only in the community-scoped binding table and audit data; it is not
published to Nostr.
Public projection is separately opt-in with
BUZZ_CORPORATE_IDENTITY_PUBLIC_DISPLAY_CLAIM. When set, that claim is
published as a relay-signed NIP-85 label. Assertions carry both active=true
and an expiration no later than one hour or the JWT's expiry, whichever comes
first. Clients require the relay signature, active status, and a future
expiration. Removing the opt-in publishes an inactive replacement only when a
prior public assertion exists.
NIP-85 events are replaceable relay events and may also remain in downstream caches or archives after replacement. Operators must choose a non-sensitive, user-approved public label and account for that retention when configuring the public claim.
Route policy
Corporate identity applies to authenticated WebSocket and audio connections, the NIP-98 event/query/count bridge, moderation reads, invite mint and claim, Git smart HTTP, media uploads, and protected media reads.
Intentional exemptions are public media reads when media GET authentication is disabled, health/readiness/metrics endpoints, NIP-11 and NIP-05 discovery, operator and admin control planes with their own authentication, secret-backed workflow hooks, public join-policy documents, invite policy-acceptance callbacks, and static local web callbacks. These exemptions must remain in the central route-policy test matrix when routes change.