Role-Based Access Control for Rooms #47

Open
opened 2026-02-17 18:05:41 +00:00 by hodlbod · 16 comments
Owner

We need a way for organizers to assign roles to users, and automatically authorize room access (at least) to those users. We could do this using a proprietary relay interface, or design a relay-level NIP for access control, maybe using UCANs. NIP 29 also has provisions for this, and we would probably want to add it to NIP 43 and 86 as well.

From bastiat

The role color thing is also a pretty essential component of discord's RBAC btw, as well as how it sorts the member list in the right side, makes it easy to eg. find which moderator is online at a glance and so on, and to see the "rank" of who you are speaking to, so to speak. Discord is uniquely good at gameifying the experience, that is a big part of why it has become so popular.

Each server has its own role names and color schemes that help in setting the culture of the server

Even if a role has the exact same permissions as all the other ones, just having the role that has some prestige attached to it, or the threat of having it removed if you behave poorly, is enough to moderate behavior

It also signals to new users that someone has a good relationship with the moderation staff, so even if they arent a moderator themselves, people tend to behave better when they are around
There are also parts of the discord permissions/role system that are a bit subtle, for instance the order of roles in the list of roles inside of server settings has effects

lets say eg. you have two roles in a server, Admin and Member, and you have enabled the "Display Role Members Separately from Online Members" setting on both roles, if a user is both an admin and a member, they will appear in under "Admin" in the member list because it is higher than the Member role in the list

they wont appear under members in the member list, only admin

the position of your role also has impacts on certain permissions, for instance, lets say you have Admin, Moderator and Member roles, then an admin can ban a moderator, but a moderator cannot ban an admin, and both can ban members

We need a way for organizers to assign roles to users, and automatically authorize room access (at least) to those users. We could do this using a proprietary relay interface, or design a relay-level NIP for access control, maybe using UCANs. NIP 29 also has provisions for this, and we would probably want to add it to NIP 43 and 86 as well. From [bastiat](https://coracle.social/npub10g8tpefz2fhrqtwa60fmh5tv7l4fl5wm7na6qwsagnstk4rltrkq4v50ha) > The role color thing is also a pretty essential component of discord's RBAC btw, as well as how it sorts the member list in the right side, makes it easy to eg. find which moderator is online at a glance and so on, and to see the "rank" of who you are speaking to, so to speak. Discord is uniquely good at gameifying the experience, that is a big part of why it has become so popular. > > Each server has its own role names and color schemes that help in setting the culture of the server > > Even if a role has the exact same permissions as all the other ones, just having the role that has some prestige attached to it, or the threat of having it removed if you behave poorly, is enough to moderate behavior > > It also signals to new users that someone has a good relationship with the moderation staff, so even if they arent a moderator themselves, people tend to behave better when they are around > There are also parts of the discord permissions/role system that are a bit subtle, for instance the order of roles in the list of roles inside of server settings has effects > > lets say eg. you have two roles in a server, Admin and Member, and you have enabled the "Display Role Members Separately from Online Members" setting on both roles, if a user is both an admin and a member, they will appear in under "Admin" in the member list because it is higher than the Member role in the list > > they wont appear under members in the member list, only admin > > the position of your role also has impacts on certain permissions, for instance, lets say you have Admin, Moderator and Member roles, then an admin can ban a moderator, but a moderator cannot ban an admin, and both can ban members
hodlbod added this to the Next milestone 2026-02-17 18:05:41 +00:00
hodlbod removed this from the Next milestone 2026-04-02 18:57:08 +00:00
hodlbod added the idea label 2026-04-02 19:29:42 +00:00
hodlbod added this to the Future milestone 2026-04-02 19:29:45 +00:00
hodlbod changed title from RBAC to Role-Based Access Control 2026-04-02 19:42:14 +00:00
hodlbod modified the milestone from Future to Current 2026-04-02 19:43:32 +00:00
hodlbod added this to the Spaces and Rooms project 2026-04-02 19:45:17 +00:00
Contributor

@hodlbod Mind if i work on this issue?

@hodlbod Mind if i work on this issue?
Khushvendra was assigned by hodlbod 2026-04-04 16:09:58 +00:00
Author
Owner

Sure, this one is going to be a very high level of difficulty, with a first pass involving protocol work, maybe even a PR to the NIPs repo. Please go ahead and start by writing a complete proposal in this issue for how to implement this at the protocol level. Try not to scope creep too much, because I'm sure that will be a temptation for something like this. I look forward to seeing what you come up with!

Sure, this one is going to be a very high level of difficulty, with a first pass involving protocol work, maybe even a PR to the NIPs repo. Please go ahead and start by writing a complete proposal in this issue for how to implement this at the protocol level. Try not to scope creep too much, because I'm sure that will be a temptation for something like this. I look forward to seeing what you come up with!
Contributor

Sure, this one is going to be a very high level of difficulty, with a first pass involving protocol work, maybe even a PR to the NIPs repo. Please go ahead and start by writing a complete proposal in this issue for how to implement this at the protocol level. Try not to scope creep too much, because I'm sure that will be a temptation for something like this. I look forward to seeing what you come up with!

Sure, thanks. Ill keep this on mind!

> Sure, this one is going to be a very high level of difficulty, with a first pass involving protocol work, maybe even a PR to the NIPs repo. Please go ahead and start by writing a complete proposal in this issue for how to implement this at the protocol level. Try not to scope creep too much, because I'm sure that will be a temptation for something like this. I look forward to seeing what you come up with! Sure, thanks. Ill keep this on mind!
Khushvendra was unassigned by hodlbod 2026-04-09 21:42:31 +00:00
Author
Owner

Hey @Khushvendra I've unassigned you so that others can jump in on this.

Hey @Khushvendra I've unassigned you so that others can jump in on this.
Contributor

Hey @hodlbod! Sorry about that, have been occupied with some other stuff past couple of days (completely forgot about this one!). Will get to this asap!

Hey @hodlbod! Sorry about that, have been occupied with some other stuff past couple of days (completely forgot about this one!). Will get to this asap!
Contributor

@hodlbod I took a protocol-first pass that stays intentionally narrow, while still unblocking implementation.

RBAC Protocol Proposal (v1, minimal scope)

Problem we need to solve

We already have role labels in NIP-29, but we do not have interoperable semantics for:

  1. Role presentation (color + ordering for member list/gameified status)
  2. Deterministic role hierarchy behavior
  3. Automatic room access from role assignment

That gap makes each relay/client invent its own RBAC model.

Design goals

  1. Reuse existing NIP-29 event flow (especially 9000/9001, 39000-39003)
  2. Avoid new cryptographic/delegation systems in v1 (UCAN etc. out of scope)
  3. Keep backward compatibility: old clients/relays should continue working
  4. Keep changes mostly in NIP-29, with narrow follow-up notes for NIP-43/NIP-86

Non-goals for v1

  1. No deny-rules matrix
  2. No role inheritance graph
  3. No cross-relay/global identity roles
  4. No token/delegation framework

Proposed NIP-29 changes

1) Extend kind:39003 (group roles) with optional structured role metadata

Keep current tag valid:

  • ["role", "<role-id>", "<optional-description>"]

Add optional tags in the same event:

  • ["role-label", "<role-id>", "<display-name>"]
  • ["role-color", "<role-id>", "<#RRGGBB>"]
  • ["role-order", "<role-id>", "<integer>"] (higher value = higher precedence)
  • ["role-hoist", "<role-id>", "1"] (optional; for member-list separation behavior)

This gives us Discord-like visual identity and deterministic sorting without breaking older parsers.

2) Clarify role assignment semantics on existing kind:9000 (put-user)

Current format already supports roles in p tag. We formalize behavior:

  • 9000 for a pubkey in a group replaces that pubkey's effective role set for that group.
  • 9001 removes membership and clears effective role state for that group.
  • If multiple active roles exist, highest role-order is the "primary role" for display and precedence checks.

3) Add room role-gates on kind:39000 (group metadata)

Add optional tags:

  • ["access-role", "read", "<role-id>"]
  • ["access-role", "write", "<role-id>"]
  • ["access-role", "join", "<role-id>"]

Semantics:

  • Multiple access-role tags for the same capability are OR.
  • If these tags are absent, existing private/restricted/closed behavior remains unchanged.
  • If present, users with matching role are automatically authorized for that capability (no manual per-room allow step).

This is the key piece that satisfies "assign roles, auto-authorize room access".

4) Precedence rule (minimal normative behavior)

When a moderation action targets another user, relay SHOULD reject actions from users whose highest role-order is lower than or equal to the target's highest role-order.

This captures the important Discord-style hierarchy behavior while staying simple.

Examples

kind:39003 role catalog

{
  "kind": 39003,
  "tags": [
    ["d", "my-room"],
    ["role", "admin", "Full moderation access"],
    ["role-label", "admin", "Admin"],
    ["role-color", "admin", "#ef4444"],
    ["role-order", "admin", "100"],

    ["role", "mod", "Can moderate content"],
    ["role-label", "mod", "Moderator"],
    ["role-color", "mod", "#f59e0b"],
    ["role-order", "mod", "60"],

    ["role", "member", "Default member role"],
    ["role-label", "member", "Member"],
    ["role-color", "member", "#22c55e"],
    ["role-order", "member", "10"]
  ]
}

kind:39000 room access by role

{
  "kind": 39000,
  "tags": [
    ["d", "trading-room"],
    ["name", "Trading"],
    ["restricted"],
    ["access-role", "read", "member"],
    ["access-role", "write", "mod"],
    ["access-role", "write", "admin"]
  ]
}

kind:9000 assign roles to user

{
  "kind": 9000,
  "tags": [
    ["h", "my-room"],
    ["p", "<pubkey>", "member", "mod"]
  ]
}

Backward compatibility

  1. Old clients ignore unknown tags and continue reading role / existing room metadata.
  2. Old relays can keep current behavior; this is additive.
  3. No new required event kinds for v1.

NIP-43 and NIP-86 follow-up (small, separate PRs)

NIP-43 follow-up

  • Add clarification text: relay admission (28934 / 8000) is independent from group/room role authorization, which is evaluated per NIP-29 metadata.
  • Optional: allow extra role values in membership list tags for relays that want relay-level role hints.

NIP-86 follow-up

Add optional group-scoped convenience methods for admin dashboards (relay can translate these into canonical NIP-29 events internally):

  • listgrouproles(group_id)
  • setuserroles(group_id, pubkey, roles[])
  • setgroupaccessroles(group_id, {read:[], write:[], join:[]})

These are convenience APIs only; event-based NIP-29 remains source of truth.

Implementation plan (practical)

Phase 1 (spec PR)

  1. NIP-29 PR with the new optional tags + precedence semantics + examples.
  2. Keep all changes additive and optional.

Phase 2 (relay + Flotilla)

  1. Relay enforces role-order precedence and access-role checks.
  2. Flotilla renders role color/order and hoisted member sections.
  3. Flotilla uses access-role metadata to drive join/write UI state.

Phase 3 (optional)

  1. NIP-86 convenience method support for admin UX.
  2. NIP-43 clarifications once implementations stabilize.

If this direction looks right, my next step is to open a focused NIP-29 PR draft with exact normative wording for the new tags and validation rules, then implement relay/client support behind feature flags.

@hodlbod I took a protocol-first pass that stays intentionally narrow, while still unblocking implementation. ## RBAC Protocol Proposal (v1, minimal scope) ### Problem we need to solve We already have role labels in NIP-29, but we do not have interoperable semantics for: 1. Role presentation (color + ordering for member list/gameified status) 2. Deterministic role hierarchy behavior 3. Automatic room access from role assignment That gap makes each relay/client invent its own RBAC model. ## Design goals 1. Reuse existing NIP-29 event flow (especially 9000/9001, 39000-39003) 2. Avoid new cryptographic/delegation systems in v1 (UCAN etc. out of scope) 3. Keep backward compatibility: old clients/relays should continue working 4. Keep changes mostly in NIP-29, with narrow follow-up notes for NIP-43/NIP-86 ## Non-goals for v1 1. No deny-rules matrix 2. No role inheritance graph 3. No cross-relay/global identity roles 4. No token/delegation framework ## Proposed NIP-29 changes ### 1) Extend `kind:39003` (group roles) with optional structured role metadata Keep current tag valid: - `["role", "<role-id>", "<optional-description>"]` Add optional tags in the same event: - `["role-label", "<role-id>", "<display-name>"]` - `["role-color", "<role-id>", "<#RRGGBB>"]` - `["role-order", "<role-id>", "<integer>"]` (higher value = higher precedence) - `["role-hoist", "<role-id>", "1"]` (optional; for member-list separation behavior) This gives us Discord-like visual identity and deterministic sorting without breaking older parsers. ### 2) Clarify role assignment semantics on existing `kind:9000` (put-user) Current format already supports roles in `p` tag. We formalize behavior: - `9000` for a pubkey in a group replaces that pubkey's effective role set for that group. - `9001` removes membership and clears effective role state for that group. - If multiple active roles exist, highest `role-order` is the "primary role" for display and precedence checks. ### 3) Add room role-gates on `kind:39000` (group metadata) Add optional tags: - `["access-role", "read", "<role-id>"]` - `["access-role", "write", "<role-id>"]` - `["access-role", "join", "<role-id>"]` Semantics: - Multiple `access-role` tags for the same capability are OR. - If these tags are absent, existing `private/restricted/closed` behavior remains unchanged. - If present, users with matching role are automatically authorized for that capability (no manual per-room allow step). This is the key piece that satisfies "assign roles, auto-authorize room access". ### 4) Precedence rule (minimal normative behavior) When a moderation action targets another user, relay SHOULD reject actions from users whose highest role-order is lower than or equal to the target's highest role-order. This captures the important Discord-style hierarchy behavior while staying simple. ## Examples ### `kind:39003` role catalog ```json { "kind": 39003, "tags": [ ["d", "my-room"], ["role", "admin", "Full moderation access"], ["role-label", "admin", "Admin"], ["role-color", "admin", "#ef4444"], ["role-order", "admin", "100"], ["role", "mod", "Can moderate content"], ["role-label", "mod", "Moderator"], ["role-color", "mod", "#f59e0b"], ["role-order", "mod", "60"], ["role", "member", "Default member role"], ["role-label", "member", "Member"], ["role-color", "member", "#22c55e"], ["role-order", "member", "10"] ] } ``` ### `kind:39000` room access by role ```json { "kind": 39000, "tags": [ ["d", "trading-room"], ["name", "Trading"], ["restricted"], ["access-role", "read", "member"], ["access-role", "write", "mod"], ["access-role", "write", "admin"] ] } ``` ### `kind:9000` assign roles to user ```json { "kind": 9000, "tags": [ ["h", "my-room"], ["p", "<pubkey>", "member", "mod"] ] } ``` ## Backward compatibility 1. Old clients ignore unknown tags and continue reading `role` / existing room metadata. 2. Old relays can keep current behavior; this is additive. 3. No new required event kinds for v1. ## NIP-43 and NIP-86 follow-up (small, separate PRs) ### NIP-43 follow-up - Add clarification text: relay admission (`28934` / `8000`) is independent from group/room role authorization, which is evaluated per NIP-29 metadata. - Optional: allow extra role values in membership list tags for relays that want relay-level role hints. ### NIP-86 follow-up Add optional group-scoped convenience methods for admin dashboards (relay can translate these into canonical NIP-29 events internally): - `listgrouproles(group_id)` - `setuserroles(group_id, pubkey, roles[])` - `setgroupaccessroles(group_id, {read:[], write:[], join:[]})` These are convenience APIs only; event-based NIP-29 remains source of truth. ## Implementation plan (practical) ### Phase 1 (spec PR) 1. NIP-29 PR with the new optional tags + precedence semantics + examples. 2. Keep all changes additive and optional. ### Phase 2 (relay + Flotilla) 1. Relay enforces role-order precedence and access-role checks. 2. Flotilla renders role color/order and hoisted member sections. 3. Flotilla uses access-role metadata to drive join/write UI state. ### Phase 3 (optional) 1. NIP-86 convenience method support for admin UX. 2. NIP-43 clarifications once implementations stabilize. --- If this direction looks right, my next step is to open a focused NIP-29 PR draft with exact normative wording for the new tags and validation rules, then implement relay/client support behind feature flags.
Author
Owner

This is a great first pass. A few problems that need to be solved:

  • Tightly coupling roles with permissions feels broken. Just because someone is a "supporter" and someone is a "super supporter" doesn't mean the latter should be able to ban the former. Permissions need to be assigned explicitly to roles I think.
  • We need a way to optionally define roles on the "server" level which are inherited by rooms, or restrict how those work. I don't think this should be done dynamically, because it would break implementations that only do nip 29, but don't do nip 43. Luckily, group roles are generated by the relay itself, so it shouldn't be too hard to validate the roles assigned to a given room/user and reject invalid assignments. Relays can also manage role definitions and assignments at will.

With that in mind, here are some ideas to improve this:

  • I think it would make more sense to move access-role on 39000 to role-access on 39003. It doesn't make sense to spread roles across too many events.
  • Small thing: instead of a hex code, we should specify role-color as a hue (0-255) which can be used in HSL or OKLCH color schemas.
  • The implicit mapping of roles to admins in nip 29 feels very open ended and makes me nervous. Maybe we could propose a role-permission which can be any privileged 9xxx kind (9000, 9001, 9002, 9005, 9009). This allows the group admins list to specify multiple roles (as defined in nip 29), each of which carry explicit permissions. This decouples abilities from roles, because you can have a role with no permissions.
  • Nip 43 roles should mirror this schema for consistency. Which means we should add kinds for relay roles, relay admins, and add role names to the relay members list.
  • We probably need to add roles to the nip 29 members list too, for the cosmetic purposes mentioned in the discord use case. If multiple roles are assigned to a member, we can by convention show the first one in the list.

Want to make a second pass with this in mind? When opening PRs to NIP 29/43 we should separate them out to be as granular as we can.

This is a great first pass. A few problems that need to be solved: - Tightly coupling roles with permissions feels broken. Just because someone is a "supporter" and someone is a "super supporter" doesn't mean the latter should be able to ban the former. Permissions need to be assigned explicitly to roles I think. - We need a way to optionally define roles on the "server" level which are inherited by rooms, or restrict how those work. I don't think this should be done dynamically, because it would break implementations that only do nip 29, but don't do nip 43. Luckily, group roles are generated by the relay itself, so it shouldn't be too hard to validate the roles assigned to a given room/user and reject invalid assignments. Relays can also manage role definitions and assignments at will. With that in mind, here are some ideas to improve this: - I think it would make more sense to move `access-role` on `39000` to `role-access` on `39003`. It doesn't make sense to spread roles across too many events. - Small thing: instead of a hex code, we should specify `role-color` as a hue (0-255) which can be used in HSL or OKLCH color schemas. - The implicit mapping of roles to admins in nip 29 feels very open ended and makes me nervous. Maybe we could propose a `role-permission` which can be any privileged 9xxx kind (9000, 9001, 9002, 9005, 9009). This allows the group admins list to specify multiple roles (as defined in nip 29), each of which carry explicit permissions. This decouples abilities from roles, because you can have a role with no permissions. - Nip 43 roles should mirror this schema for consistency. Which means we should add kinds for relay roles, relay admins, and add role names to the relay members list. - We probably need to add roles to the nip 29 members list too, for the cosmetic purposes mentioned in the discord use case. If multiple roles are assigned to a member, we can by convention show the first one in the list. Want to make a second pass with this in mind? When opening PRs to NIP 29/43 we should separate them out to be as granular as we can.
Contributor

@hodlbod Thanks for the detailed feedback. I made a second pass that incorporates all of this and keeps scope tight.

RBAC Protocol Proposal (v2, revised)

Core corrections from v1

  1. Roles are identity/UX labels, not permissions.
  2. Permissions are assigned explicitly to roles.
  3. Room access mapping is moved from 39000 into 39003.
  4. Role color uses hue (0-255), not hex.
  5. Relay-level and room-level role schemas are aligned, but room behavior remains self-contained for NIP-29-only implementations.

Updated NIP-29 model

1) Keep roles centralized in kind 39003

kind 39003 remains the single role schema event for a room/group.

Additive tags (all optional):

  • ["role", "", ""]

  • ["role-label", "", ""]

  • ["role-color", "", ""]

  • ["role-order", "", ""] // UI sorting only

  • ["role-permission", "", "<9xxx-kind>"]

    • Allowed values in first pass: 9000, 9001, 9002, 9005, 9009
    • Multiple tags allowed per role
  • ["role-access", "", "read|write|join"]

    • This replaces v1 access-role on 39000

Normative clarification:

  • role-order MUST NOT imply moderation capability.
  • A role with zero role-permission tags is valid (cosmetic/status-only role).

2) Clarify role assignment on 9000/9001

  • 9000 updates a user's assigned role set for that room/group.
  • 9001 removes membership and clears role assignment in that room/group.
  • Relay MUST reject any role in 9000 that is not defined/allowed by the room's current 39003 schema.

3) Explicitly decouple “admin” from role names

The implicit “admin” interpretation in NIP-29 should be tightened:

  • Effective capabilities are derived from role-permission tags, not from role names.
  • 39001 is an informative listing of users with elevated roles; it is not the permission source of truth.
  • Relay authorization checks MUST use resolved role-permission state.

4) Add role annotations to member/admin listings for UX

To support Discord-like cosmetic display and sorting:

  • Extend 39002 member entries to optionally include role names after pubkey:
    • ["p", "", "", "", ...]
  • 39001 already carries role names; keep it aligned with 39002.
  • Convention: if multiple roles are present, clients display the first one as primary.

Relay-level roles + inheritance without breaking NIP-29-only clients

Problem

We want optional server-level role definitions (NIP-43 side), but room authorization must still work for clients/relays that only implement NIP-29.

Rule

Room 39003 must remain fully authoritative for room validation.

If relay-level roles are inherited, relay materializes the resolved role set into room 39003 and validates against that resolved set.

That means:

  • NIP-29-only clients can still parse one room-local schema and behave correctly.
  • NIP-43 is additive, not required for basic room RBAC correctness.

NIP-43 alignment (separate PR series)

Mirror the same schema concepts at relay scope:

  1. Add relay roles event kind (role catalog)
  2. Add relay admins event kind (admins + assigned roles)
  3. Add role names to relay members listing (compatible extension)
  4. Reuse the same tag grammar where possible:
    • role
    • role-label
    • role-color (hue)
    • role-order
    • role-permission
    • role-access (if relay wants relay-level access semantics)

This keeps 29/43 conceptually consistent.

Validation behavior (minimal but strict)

Relay MUST reject:

  1. 9000 assignments containing unknown roles.
  2. Moderation actions requiring 9xxx capability if the sender has no role-permission granting that kind.
  3. role-permission values outside allowed privileged 9xxx list in this first pass.

Granular PR plan

NIP-29 PR 1: Role schema + explicit permissions

  • Add role-color(hue), role-order (UI only), role-permission, role-access tags on 39003
  • Clarify role-order is non-authoritative for permissions

NIP-29 PR 2: Assignment and listings

  • Tighten 9000/9001 role assignment validation rules
  • Extend 39002 with optional role names in p tags
  • Clarify 39001 informational vs authorization source of truth

NIP-29 PR 3: Inheritance materialization rule

  • Specify that room 39003 is authoritative and relay must materialize any inherited relay roles there

NIP-43 PR 1: Relay role catalog/admin/member extensions

  • Introduce relay-scoped role/admin events
  • Add optional role names in relay member list entries

NIP-43 PR 2: 29/43 interoperability text

  • Define how relay-level roles are projected into room 39003 for NIP-29 compatibility

This keeps each PR reviewable and avoids a giant coupled spec diff.


If this direction is good, I will draft PR-ready normative text in this exact sequence (29 PR1 -> 29 PR2 -> 29 PR3 -> 43 PR1 -> 43 PR2).

@hodlbod Thanks for the detailed feedback. I made a second pass that incorporates all of this and keeps scope tight. ## RBAC Protocol Proposal (v2, revised) ### Core corrections from v1 1. Roles are identity/UX labels, not permissions. 2. Permissions are assigned explicitly to roles. 3. Room access mapping is moved from 39000 into 39003. 4. Role color uses hue (0-255), not hex. 5. Relay-level and room-level role schemas are aligned, but room behavior remains self-contained for NIP-29-only implementations. ## Updated NIP-29 model ### 1) Keep roles centralized in kind 39003 kind 39003 remains the single role schema event for a room/group. Additive tags (all optional): - ["role", "<role-id>", "<optional-description>"] - ["role-label", "<role-id>", "<display-name>"] - ["role-color", "<role-id>", "<hue-0-255>"] - ["role-order", "<role-id>", "<integer>"] // UI sorting only - ["role-permission", "<role-id>", "<9xxx-kind>"] - Allowed values in first pass: 9000, 9001, 9002, 9005, 9009 - Multiple tags allowed per role - ["role-access", "<role-id>", "read|write|join"] - This replaces v1 access-role on 39000 Normative clarification: - role-order MUST NOT imply moderation capability. - A role with zero role-permission tags is valid (cosmetic/status-only role). ### 2) Clarify role assignment on 9000/9001 - 9000 updates a user's assigned role set for that room/group. - 9001 removes membership and clears role assignment in that room/group. - Relay MUST reject any role in 9000 that is not defined/allowed by the room's current 39003 schema. ### 3) Explicitly decouple “admin” from role names The implicit “admin” interpretation in NIP-29 should be tightened: - Effective capabilities are derived from role-permission tags, not from role names. - 39001 is an informative listing of users with elevated roles; it is not the permission source of truth. - Relay authorization checks MUST use resolved role-permission state. ### 4) Add role annotations to member/admin listings for UX To support Discord-like cosmetic display and sorting: - Extend 39002 member entries to optionally include role names after pubkey: - ["p", "<pubkey>", "<role1>", "<role2>", ...] - 39001 already carries role names; keep it aligned with 39002. - Convention: if multiple roles are present, clients display the first one as primary. ## Relay-level roles + inheritance without breaking NIP-29-only clients ### Problem We want optional server-level role definitions (NIP-43 side), but room authorization must still work for clients/relays that only implement NIP-29. ### Rule Room 39003 must remain fully authoritative for room validation. If relay-level roles are inherited, relay materializes the resolved role set into room 39003 and validates against that resolved set. That means: - NIP-29-only clients can still parse one room-local schema and behave correctly. - NIP-43 is additive, not required for basic room RBAC correctness. ## NIP-43 alignment (separate PR series) Mirror the same schema concepts at relay scope: 1. Add relay roles event kind (role catalog) 2. Add relay admins event kind (admins + assigned roles) 3. Add role names to relay members listing (compatible extension) 4. Reuse the same tag grammar where possible: - role - role-label - role-color (hue) - role-order - role-permission - role-access (if relay wants relay-level access semantics) This keeps 29/43 conceptually consistent. ## Validation behavior (minimal but strict) Relay MUST reject: 1. 9000 assignments containing unknown roles. 2. Moderation actions requiring 9xxx capability if the sender has no role-permission granting that kind. 3. role-permission values outside allowed privileged 9xxx list in this first pass. ## Granular PR plan ### NIP-29 PR 1: Role schema + explicit permissions - Add role-color(hue), role-order (UI only), role-permission, role-access tags on 39003 - Clarify role-order is non-authoritative for permissions ### NIP-29 PR 2: Assignment and listings - Tighten 9000/9001 role assignment validation rules - Extend 39002 with optional role names in p tags - Clarify 39001 informational vs authorization source of truth ### NIP-29 PR 3: Inheritance materialization rule - Specify that room 39003 is authoritative and relay must materialize any inherited relay roles there ### NIP-43 PR 1: Relay role catalog/admin/member extensions - Introduce relay-scoped role/admin events - Add optional role names in relay member list entries ### NIP-43 PR 2: 29/43 interoperability text - Define how relay-level roles are projected into room 39003 for NIP-29 compatibility This keeps each PR reviewable and avoids a giant coupled spec diff. --- If this direction is good, I will draft PR-ready normative text in this exact sequence (29 PR1 -> 29 PR2 -> 29 PR3 -> 43 PR1 -> 43 PR2).
hodlbod changed title from Role-Based Access Control to Role-Based Access Control for Rooms 2026-04-11 16:45:01 +00:00
Author
Owner

This looks great. I think we should probably just do two PRs, one to nip 29 and one (after that one has been accepted/implemented) to nip 43. I've opened #192 for the NIP 43 part; let's just focus on NIP 29 here. Next step would be to open a PR on the nips repo, want to go ahead and do that?

This looks great. I think we should probably just do two PRs, one to nip 29 and one (after that one has been accepted/implemented) to nip 43. I've opened https://gitea.coracle.social/coracle/flotilla/issues/192 for the NIP 43 part; let's just focus on NIP 29 here. Next step would be to open a PR on the nips repo, want to go ahead and do that?
Contributor

Sure, works for me

Sure, works for me
Contributor

@hodlbod Updated plan aligned with your guidance:

Sequencing

  1. NIP-29 PR first (this issue: #47)
  2. NIP-43 PR second (issue #192), only after NIP-29 is accepted and implemented

Scope for the NIP-29 PR only

  1. Keep role schema centralized on kind 39003

    • role
    • role-label
    • role-color (hue 0-255)
    • role-order (UI ordering only)
    • role-permission (explicit privileged 9xxx kinds)
    • role-access (read/write/join)
  2. Clarify authorization model

    • Permissions are derived from role-permission, not role names or role-order
    • role-order is explicitly non-authoritative for moderation capability
  3. Tighten assignment/listing behavior

    • 9000/9001 validation against defined roles
    • Optional role names in 39002 member entries (cosmetic/client UX)
    • 39001 remains informational; not the permission source of truth
  4. Compatibility rule

    • Room-level 39003 remains authoritative for room validation
    • If relay-level inheritance exists internally, relay materializes resolved roles into room 39003 so NIP-29-only implementations still work

Explicit non-goals in this PR

  1. No NIP-43 relay role kinds yet
  2. No NIP-86 additions yet
  3. No delegation/UCAN model

Immediate next steps

  1. Draft final normative NIP-29 wording as a minimal diff
  2. Open PR on nostr-protocol/nips referencing #47
  3. Implement and validate in relay/client
  4. Then start #192 for NIP-43 mirror work

If this looks right, Should i start with the implementation?

@hodlbod Updated plan aligned with your guidance: ## Sequencing 1. NIP-29 PR first (this issue: #47) 2. NIP-43 PR second (issue #192), only after NIP-29 is accepted and implemented ## Scope for the NIP-29 PR only 1. Keep role schema centralized on kind 39003 - role - role-label - role-color (hue 0-255) - role-order (UI ordering only) - role-permission (explicit privileged 9xxx kinds) - role-access (read/write/join) 2. Clarify authorization model - Permissions are derived from role-permission, not role names or role-order - role-order is explicitly non-authoritative for moderation capability 3. Tighten assignment/listing behavior - 9000/9001 validation against defined roles - Optional role names in 39002 member entries (cosmetic/client UX) - 39001 remains informational; not the permission source of truth 4. Compatibility rule - Room-level 39003 remains authoritative for room validation - If relay-level inheritance exists internally, relay materializes resolved roles into room 39003 so NIP-29-only implementations still work ## Explicit non-goals in this PR 1. No NIP-43 relay role kinds yet 2. No NIP-86 additions yet 3. No delegation/UCAN model ## Immediate next steps 1. Draft final normative NIP-29 wording as a minimal diff 2. Open PR on nostr-protocol/nips referencing #47 3. Implement and validate in relay/client 4. Then start #192 for NIP-43 mirror work If this looks right, Should i start with the implementation?
Author
Owner

Yep, looks good!

Yep, looks good!
Contributor

@hodlbod Opened NIP-29 PR, scoped to this issue only. (https://github.com/nostr-protocol/nips/pull/2316)
(NIP-43 follow-up remains in #192)

@hodlbod Opened NIP-29 PR, scoped to this issue only. (https://github.com/nostr-protocol/nips/pull/2316) (NIP-43 follow-up remains in #192)
Contributor

Hey @hodlbod, I've got an implementation plan mapped out for the Flotilla side of NIP-29 RBAC, but I have a few quick questions regarding scope and @welshman dependencies before the implementation part....

  • Admin Role Management UI: Should we include an admin-facing "Manage Roles" UI (to create, edit, or delete roles/colors/permissions) in this first pass, or should we just display the existing roles read-only? Implementing the management UI would require building out kind:9002 (edit-metadata) or NIP-86 method flows for roles. My recommendation is to keep it read-only for v1, and handle the admin management UI in a follow-up PR.

  • Welshman ROOM_ROLES Constant: Does @welshman/util currently export a constant for kind:39003? I see ROOM_ADMINS (39001), ROOM_MEMBERS (39002), and ROOM_META (39000), but I didn't spot 39003. If it's not exported yet, I can define ROOM_ROLES = 39003 locally in Flotilla for now.

  • Member List Role Annotations: The NIP-29 updates allow p tags in kind:39002 (members list) to carry role names after the pubkey for cosmetic/UX purposes. Does the current Welshman getPubkeyTagValues (or similar utility) preserve the extra positional tag values, or does it only extract index [1]? I think this affects how we'll parse the member roles from the event.

lmk your thoughts on the UI scope, and then imo we can start implementing stuff!

Hey @hodlbod, I've got an implementation plan mapped out for the Flotilla side of NIP-29 RBAC, but I have a few quick questions regarding scope and `@welshman` dependencies before the implementation part.... - **Admin Role Management UI**: Should we include an admin-facing "Manage Roles" UI (to create, edit, or delete roles/colors/permissions) in this first pass, or should we just display the existing roles read-only? Implementing the management UI would require building out `kind:9002` (edit-metadata) or NIP-86 method flows for roles. My recommendation is to keep it read-only for v1, and handle the admin management UI in a follow-up PR. - **Welshman `ROOM_ROLES` Constant**: Does `@welshman/util` currently export a constant for `kind:39003`? I see `ROOM_ADMINS` (39001), `ROOM_MEMBERS` (39002), and `ROOM_META` (39000), but I didn't spot 39003. If it's not exported yet, I can define `ROOM_ROLES = 39003` locally in Flotilla for now. - **Member List Role Annotations**: The NIP-29 updates allow p tags in `kind:39002` (members list) to carry role names after the pubkey for cosmetic/UX purposes. Does the current Welshman `getPubkeyTagValues` (or similar utility) preserve the extra positional tag values, or does it only extract index `[1]`? I think this affects how we'll parse the member roles from the event. lmk your thoughts on the UI scope, and then imo we can start implementing stuff!
Author
Owner
  • Yes, let's leave roles read-only and create an issue for adding the management UI later.
  • Welshman doesn't have 39003, feel free to define it locally in Flotilla and I'll add it to welshman later.
  • getPubkeyTagValues doesn't keep the roles, you're right that we'll have to switch member lists to a list of structs instead.
- Yes, let's leave roles read-only and create an issue for adding the management UI later. - Welshman doesn't have 39003, feel free to define it locally in Flotilla and I'll add it to welshman later. - `getPubkeyTagValues` doesn't keep the roles, you're right that we'll have to switch member lists to a list of structs instead.
Author
Owner

This is still going through some design iterations: https://github.com/nostr-protocol/nips/pull/2376/changes

This is still going through some design iterations: https://github.com/nostr-protocol/nips/pull/2376/changes
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: coracle/flotilla#47