From 2e6a40900efb5978b58c96f7c919411949728521 Mon Sep 17 00:00:00 2001 From: Joao Costa Date: Wed, 15 Apr 2026 14:31:21 +0100 Subject: [PATCH] nip-11: add access_control field for gated relay discovery 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 #2311. --- 11.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/11.md b/11.md index d1acab7b00..15ca932cc2 100644 --- a/11.md +++ b/11.md @@ -176,6 +176,44 @@ Relays that require payments may want to expose their fee schedules. } ``` +### Access Control + +Relays that gate access behind external conditions (subscriptions, token holdings, group membership, allowlists, etc.) may advertise those requirements so clients can surface them to the user *before* connecting or publishing, rather than reactively after a `restricted:` close. + +```jsonc +{ + "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" + }, + // other fields... +} +``` + +- `authentication`: either `"required"` or `"optional"`. Indicates whether [NIP-42](42.md) auth is needed before the gated actions can be performed. This complements `limitation.auth_required`, which only covers connection-level auth. + +- `permissions`: an array of `{ action, kinds }` objects describing what the relay gates. `action` is one of `"read"` or `"write"`. `kinds` is the list of event kinds the gate applies to; an empty or omitted `kinds` array means the gate applies to all kinds for that action. Multiple entries with the same action MAY be used to describe disjoint kind sets. This field does not attempt to describe *how* the relay evaluates access, only *what* is gated. + +- `description`: a human-readable string a client MAY render to the user (for example: "Active subscription required"). Plain text, no markup. + +- `info_url`: an optional URL where a user can obtain access (subscribe, join, acquire tokens, etc.). Clients SHOULD NOT auto-follow this URL and SHOULD present it as a user-initiated action. + +This field describes *capability advertisement*, not runtime policy. Arbitrary access policy cannot be meaningfully introspected, and this NIP does not attempt to. Relays implementing role-based access control via a future NIP (e.g. a relay-level extension of [NIP-29](29.md)) MAY use `access_control` alongside it: `access_control` tells a client what the relay gates and where a non-member can go to become one; the RBAC NIP describes how an authenticated member's role maps to permissions once they are known. + +When a relay denies a gated action, it SHOULD use a human-meaningful reason after the standard `restricted:` prefix defined in [NIP-42](42.md) and [NIP-01](01.md), so that clients can surface an actionable message rather than an opaque failure: + +``` +["CLOSED", "", "restricted: no active subscription"] +["OK", "", false, "restricted: write access requires membership"] +``` + +Clients encountering a `restricted:` reason SHOULD render it to the user together with `access_control.info_url` when available. + ### Examples ```yaml