Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions 11.md
Original file line number Diff line number Diff line change
Expand Up @@ -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", "<sub-id>", "restricted: no active subscription"]
["OK", "<event-id>", 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
Expand Down