nip-11: add access_control field for gated relay discovery#2318
nip-11: add access_control field for gated relay discovery#2318TheIcarusWings wants to merge 1 commit intonostr-protocol:masterfrom
Conversation
Adds an optional access_control field so relays that gate access (behind subscriptions, token holdings, group membership, allowlists, etc.) can advertise that to clients before connection, and so clients can render actionable denial states instead of opaque restricted: closes. The field describes capability advertisement (what is gated, where to obtain access) and intentionally does not attempt to describe how a relay evaluates access, which cannot be meaningfully introspected. Composes with a future RBAC NIP: this advertises what is gated; the RBAC NIP describes how a member's role maps to permissions. Discussed in issue nostr-protocol#2311.
Add examples/nip11-example.json with the exact shape proposed in nostr-protocol/nips#2318, and two Fastify routes to serve it: - GET /nip11-example: always returns the example document - GET / with Accept: application/nostr+json: returns the same document, matching how a real relay exposes its NIP-11 This makes the repo directly runnable as a reference rather than only documentary. A reviewer can clone, npm install, npm run dev, and curl the endpoint to see the exact shape PR #2318 describes.
|
A couple of supporting resources for reviewers and client implementers:
Happy to iterate on any of these if the PR shape changes during review. |
|
Gentle bump. No reviews yet after a week, and there's a relevant convergence worth flagging. NIP-63a (#2315), currently in active review, proposes
Without the first, a client still has to probe the relay and react to a @staab, you confirmed the direction in the issue, flagging in case review cycles are available. @fiatjaf, tagging for NIPs triage. |
|
I appreciate your work and interest @TheIcarusWings, but this LLM you're using is producing too much text, gotta shrink that a little bit or give us some more time. |
|
Fair point. For what it's worth, the LLM is my typist, (apparently a verbose one). The ideas are in good faith. Stepping back to give you proper review space. Ping whenever cycles open up. |
Adds an optional
access_controlfield to NIP-11 so relays that gate access (subscriptions, token holdings, group membership, allowlists, etc.) can advertise what they gate, and clients can render actionable UI before connecting rather than reacting to opaquerestricted:closes after the fact.Discussed in issue #2311.
Motivation
Today, a client has no pre-connection signal that a relay gates read or write access. It connects, sends a
REQorEVENT, receives aCLOSEDorOK: falsewith arestricted:prefix, and has to reactively surface an error to the user. In mixed-feed scenarios (a timeline including a premium post whose relay the user may or may not be subscribed to, or annaddrpointing at a gated relay) the client cannot render "locked, subscribe" on first paint because it has no way to know the relay is gated or where to send the user to obtain access.This PR fills that gap with capability advertisement, not policy introspection.
Scope
In scope:
restricted: <reason>messages so clients can render them uniformlyExplicitly out of scope:
Shape
{ "access_control": { "authentication": "required", "permissions": [ { "action": "read", "kinds": [30402, 30403] }, { "action": "write", "kinds": [1, 30023] } ], "description": "Active subscription required for access to premium content.", "info_url": "https://example.com/subscribe" } }authentication:"required"or"optional". Distinct fromlimitation.auth_required, which only covers connection-level auth.permissions: array of{ action, kinds }withaction∈"read"|"write". Empty/omittedkindsmeans "all kinds for this action." Nomenclature chosen to align with @staab's "read/write policies per event kind" suggestion from NIP-XX: Relay Access Control via Authentication Callbacks #2311 and to read naturally alongside a future NIP-43 RBAC extension.description: human-readable string clients can render.info_url: optional URL where a user can obtain access (subscribe, join, etc.). Clients SHOULD NOT auto-follow.Plus a standardized denial format:
Composition with future RBAC
The spec text explicitly positions this as complementary to a future role-based access NIP. In short:
access_control: "this relay gates kind X for read, here is where to become a member"A client rendering a mixed timeline can use both:
access_controltells it the relay gates the post's kind; the RBAC event tells it whether this user satisfies the gate. No probing, no try-and-see, right state on first paint for both subscribed and non-subscribed users.Reference implementation
Live in production on a creator platform:
GET /withAccept: application/nostr+jsonatwss://premium.nostreon.comHistory
This started in #2311 as a broader proposal for a relay-to-backend callback protocol. @staab pushed back that the relay-to-backend direction has no interoperability surface (it's implementation detail), which was correct. The proposal was refocused onto the client-facing discovery + denial-format side, which is an interop surface, and @staab confirmed it probably makes sense to add to NIP-11. This PR is that change.