NIP-29 ====== Relay-based Groups ------------------ `draft` `optional` `relay` This NIP defines a standard for groups that are only writable by a closed set of users. They can be public for reading by external users or not. Groups are identified by a random string of any length that serves as an _id_. There is no way to create a group, what happens is just that relays (most likely when asked by users) will create rules around some specific ids so these ids can serve as an actual group, henceforth messages sent to that group will be subject to these rules. Normally a group will originally belong to one specific relay, but the community may choose to move the group to other relays or even fork the group so it exists in different forms -- still using the same _id_ -- across different relays. ## Group identity, migration and forking A group is held together by two things: the group _id_ and the relay that currently enforces access rules over that _id_ on behalf of one or more group owners or admins. Because enforcement is tied to a specific relay, the group is only as durable as that relay's willingness and ability to keep hosting it. ### Moving a group to another relay If the relay hosting a group becomes unresponsive, goes offline permanently, or simply becomes untrustworthy, the group can be migrated by copying the existing events to a different relay, as long as that new relay agrees to receive the old group and to continue enforcing the same rules and acknowledging the same admins. ### Forking a group Another situation in which it might be desirable to move a relay to a different group is that of a disagreement between members of the original group. A dissident faction within a group can take the group's history to a different relay that agrees to _change_ the rules of that group and assign admin powers to a different set of users. This is a group _fork_: the new relay keeps the same _id_ but enforces a different set of moderation policies and a different admin roster than the original relay. Forks are not bugs. It's a feature of this NIP that the same _id_ can simultaneously identify different communities with different governance, hosted by different relays, each with their own `kind:39000` metadata event, their own `kind:39001` admin list and their own message history. ### Detecting migrations and forks In order to check whether a group has migrated or whether a fork exists, clients SHOULD periodically -- and MUST, if their primary relay for a group is offline or unreachable -- look at the `kind:10009` event of the group's admins and of trusted friends. The pubkeys of the admins of the groups the user is in SHOULD be cached locally so this check can be performed even when the original relay is down. If a `kind:10009` entry for a group now points to a different relay hint than the one the client was previously using, the client SHOULD notify the user that the group may have moved or been forked, and offer to fetch the group's events from the new relay and, if the user wishes, to migrate their participation to that relay as well, updating the current user's `kind:10009` in the process. ### Replicating a group for resilience Groups that do not want to lose history in case their primary relay goes down SHOULD set up a live backup/replica of all of the group's events on a second relay. The replica relay does not need to enforce any moderation rules of its own -- it exists only to preserve the event history so that, if the primary relay disappears, a new authoritative relay can be chosen and the historical events copied over from the replica. ## Relay-generated events Relays are supposed to generate the events that describe group metadata and group admins. These are _addressable_ events signed by the relay keypair directly, with the group _id_ as the `d` tag. ## Group referencing A group may be identified by a `naddr1...` code to its `kind:39000` metadata event, with the public key set to the relay `self` public key, the `d` identifier set to the group id, the kind set to `39000` and a relay hint to the relay that is hosting the group. An invite code may be appended to the group identifier: ``` naddr1...?invite= ``` Clients should use this code in the `code` tag of the `kind:9021` join request. Since the bech32 charset doesn't include `?`, everything before the `?` is still a valid identifier on its own, so clients that don't understand the suffix can just ignore it. ## The `h` tag Events sent by users to groups (chat messages, text notes, moderation events etc) MUST have an `h` tag with the value set to the group _id_. ## Timeline references In order to not be used out of context, events sent to these groups may contain references to previous events seen from the same relay in the `previous` tag. The choice of which previous events to pick belongs to the clients. The references are to be made using the first 8 characters (4 bytes) of any event in the last 50 events seen by the user in the relay, excluding events by themselves. There can be any number of references (including zero), but it's recommended that clients include at least 3 and that relays enforce this. This is a hack to prevent messages from being broadcasted to external relays that have forks of one group out of context. Relays are expected to reject any events that contain timeline references to events not found in their own database. Clients should also check these to keep relays honest about them. ## Late publication Relays should prevent late publication (messages published now with a timestamp from days or even hours ago) unless they are open to receive a group forked or moved from another relay. ## Group management Groups can have any number of users with elevated access. These users are identified by role labels which are arbitrarily defined by the relays (see also the description of `kind:39003`). What each role is capable of not defined in this NIP either, it's a relay policy that can vary. Roles can be assigned by other users (as long as they have the capability to add roles) by publishing a `kind:9000` event with that user's pubkey in a `p` tag and the roles afterwards (even if the user is already a group member a `kind:9000` can be issued and the user roles must just be updated). The roles supported by the group as to having some special privilege assigned to them should be accessible on the event `kind:39003`, but the relay may also accept other role names, arbitrarily defined by clients, and just not do anything with them. Users with any roles that have any privilege can be considered _admins_ in a broad sense and be returned in the `kind:39001` event for a group. ## Live audio/video (AV) spaces Groups may indicate they support live audio and/or video chat by means of a [LiveKit](https://docs.livekit.io/transport/) server by having the tag `livekit` in the group announcement event. An empty `supported_kinds` tag should be used if the group doesn't support text messages, only live AV chat. This setup allows for both Discord-style independent AV rooms and text rooms and Telegram-style single-roomgroup with support for both text and AV. Upon seeing a `livekit` tag in a group, clients that want to take part in the AV space should call the [token endpoint](https://docs.livekit.io/frontends/build/authentication/endpoint/) at the relay path `/.well-known/nip29/livekit/` with a [NIP-98](98.md)-style `Authorization` header to get back the LiveKit JWT and the URL of the LiveKit server and proceed with the standard LiveKit flow there. The `Authorization` event should be of kind `27235` and contain a tag `["u", "https://relay.tld/.well-known/nip29/livekit/"]`. Relays should enforce access control at the LiveKit JWT creation time according to the group settings. Relays MUST set the `sub` property on the issued JWT (called "identity" in the standard LiveKit libraries) such that the initial 64 characters correspond to the lowercase hex public key of the Nostr user. The same user can be issued multiple JWTs and join the same LiveKit room multiple times, so relays are expected to appending a random identifier to those 64 characters. In order to inform clients about relay support for AV (so they can display that option for users when creating or editing groups) relays should serve a status code `204` at `https://relay.tld/.well-known/nip29/livekit`. ## Event definitions These are the events expected to be found in NIP-29 groups. ### Normal user-created events Groups may accept any event kind, including chats, threads, long-form articles, calendar, livestreams, market announcements and so on. These should be as defined in their respective NIPs, with the addition of the `h` tag. ### User-related group management events These are events that can be sent by users to manage their situation in a group, they also require the `h` tag. - *join request* (`kind:9021`) Any user can send a kind `9021` event to the relay in order to request admission to the group. Relays MUST reject the request if the user has not been added to the group. The accompanying error message SHOULD explain whether the rejection is final, if the request is pending review, or if some other special handling is relevant (e.g. if payment is required). If a user is already a member, the event MUST be rejected with `duplicate: ` as the error message prefix. ```json { "kind": 9021, "content": "optional reason", "tags": [ ["h", ""], ["code", ""] ] } ``` The optional `code` tag may be used by the relay to preauthorize acceptance, together with the `kind:9009` `create-invite` moderation event. - *leave request* (`kind:9022`) Any user can send one of these events to the relay in order to be automatically removed from the group. The relay will automatically issue a `kind:9001` in response removing this user. ```json { "kind": 9022, "content": "optional reason", "tags": [ ["h", ""] ] } ``` ### Group state -- or moderation These are events expected to be sent by the relay master key or by group admins -- and relays should reject them if they don't come from an authorized admin. They also require the `h` tag. - *moderation events* (`kinds:9000-9020`) (optional) Clients can send these events to a relay in order to accomplish a moderation action. Relays must check if the pubkey sending the event is capable of performing the given action based on its role and the relay's internal policy (see also the description of `kind:39003`). ```yaml { "kind": 90xx, "content": "optional reason", "tags": [ ["h", ""], ["previous", /*...*/] ] } ``` Each moderation action uses a different kind and requires different arguments, which are given as tags. These are defined in the following table: | kind | name | tags | | --- | --- | --- | | 9000 | `put-user` | `p` with pubkey hex and optional roles | | 9001 | `remove-user` | `p` with pubkey hex | | 9002 | `edit-metadata` | all the fields of *group-metadata* | | 9005 | `delete-event` | `e` with event id hex | | 9007 | `create-group` | | | 9008 | `delete-group` | | | 9009 | `create-invite` | arbitrary `code` | | 9010 | `update-pin-list` | zero or more `e` with event id hex or `a` with event address | It's expected that the group state (of who is an allowed member or not, who is an admin and with which permission or not, what are the group name and picture etc) can be fully reconstructed from the canonical sequence of these events. ### Group metadata events These events contain the group id in a `d` tag instead of the `h` tag. They MUST be created by the relay master key only (as stated by the [NIP-11](11.md) `"self"` pubkey) and a single instance of each (or none) should exist at all times for each group. They are merely informative but should reflect the latest group state (as it was changed by moderation events over time). - *group metadata* (`kind:39000`) (optional) This event defines the metadata for the group -- basically how clients should display it. It must be generated and signed by the relay in which is found. Relays shouldn't accept these events if they're signed by anyone else. If the group is forked and hosted in multiple relays, there will be multiple versions of this event in each different relay and so on. ```yaml { "kind": 39000, "content": "", "tags": [ ["d", ""], ["name", "Pizza Lovers"], ["picture", "https://pizza.com/pizza.png"], ["banner", "https://pizza.com/banner.png"], ["about", "a group for people who love pizza"], ["private"], ["closed"], ["supported_kinds", "9", "11"] ] // other fields... } ``` - `name`, `picture`, `banner` and `about` are basic metadata for the group for display purposes. - `private` indicates that only members can _read_ group messages. Omitting this tag indicates that anyone can read group messages. - `restricted` indicates that only members can _write_ messages to the group. Omitting this tag indicates that anyone can send messages to the group. - `hidden` indicates that relays should hide group metadata from non-members. Omitting this tag indicates that anyone can request group metadata events. - `closed` indicates that join requests are ignored. Omitting this tag indicates that users can expect join requests to be honored. - `livekit` indicates that a group supports LiveKit-powered media rooms (live audio/video). - `supported_kinds` is a list of stringified kinds numbers the group supports. When not specified all kinds are assumed to be supported. It can contain no items, in which case no kinds are supported (this is the case for AV-only groups, for example). - *group admins* (`kind:39001`) (optional) Each admin is listed along with one or more roles. These roles SHOULD have a correspondence with the roles supported by the relay, as advertised by the `kind:39003` event. ```yaml { "kind": 39001, "content": "list of admins for the pizza lovers group", "tags": [ ["d", ""], ["p", "", "ceo"], ["p", "", "secretary", "gardener"], // other pubkeys... ], // other fields... } ``` - *group members* (`kind:39002`) (optional) It's a list of pubkeys that are members of the group. Relays might choose to not to publish this information, to restrict what pubkeys can fetch it or to only display a subset of the members in it. Clients should not assume this will always be present or that it will contain a full list of members. ```yaml { "kind": 39002, "content": "list of members for the pizza lovers group", "tags": [ ["d", ""], ["p", ""], ["p", ""], ["p", ""], // other pubkeys... ], // other fields... } ``` - *group roles* (`kind:39003`) (optional) This is an event that MAY be published by the relay informing users and clients about what are the roles supported by this relay according to its internal logic. For example, a relay may choose to support the roles `"admin"` and `"moderator"`, in which the `"admin"` will be allowed to edit the group metadata, delete messages and remove users from the group, while the `"moderator"` can only delete messages (or the relay may choose to call these roles `"ceo"` and `"secretary"` instead, the exact role name is not relevant). The process through which the relay decides what roles to support and how to handle moderation events internally based on them is specific to each relay and not specified here. ```yaml { "kind": 39003, "content": "list of roles supported by this group", "tags": [ ["d", ""], ["role", "", ""], ["role", "", ""], // other roles... ], // other fields... } ``` - *livekit participants* (`kind:39004`) (optional) This is an event that MAY be published by the relay informing users and clients about who is live in an AV room. This should be updated whenever a new user leaves or joins a group, clients are expected to be actively subscribed to it. The event MUST contain zero or more `participant` tags with the participant's public key as lowercase hex. ```yaml { "kind": 39004, "content": "", "tags": [ ["d", ""], ["participant", ""], // other participants... ], // other fields... } ``` - *group pinned events* (`kind:39005`) (optional) It's a list of events pinned in the group, in the order they should be displayed. The `kind:9010` moderation event carries the full list as `e` tags (regular events) and `a` tags (addressable events), so pinning, unpinning, reordering and clearing pins are all done by submitting a new list. Whenever one is accepted the relay regenerates this event to mirror it. The relay may choose to limit the number of pins. Clients may display these events at the top of the group view. ```jsonc { "kind": 39005, "content": "", "tags": [ ["d", ""], ["e", ""], ["a", "::"], ["e", ""], // other event references... ], // other fields... } ``` ## Subgroups Groups MAY be organized hierarchically. A subgroup is an ordinary NIP-29 group (same `kind:39000`, same moderation events, same `h` tag mechanics) whose metadata carries a `parent` tag pointing at its parent's `d` identifier: ```jsonc // root group { "kind": 39000, "tags": [["d", "tech"], ["name", "Tech"], ["child", "nostr"]] } // subgroup { "kind": 39000, "tags": [["d", "nostr"], ["name", "Nostr"], ["parent", "tech"], ["child", "nip29"]] } // sub-subgroup { "kind": 39000, "tags": [["d", "nip29"], ["name", "NIP-29"], ["parent", "nostr"]] } ``` A group without a `parent` tag is a root group. To build the tree, clients fetch `{"kinds":[39000]}` and assemble it locally: events without a `parent` tag are roots; the rest are grouped under the `d` referenced by their `parent` tag. The tree is scoped to a single relay. When aggregating across relays a client MAY see conflicting `parent` values for the same `d`; in that case the `kind:39000` from the relay currently serving the group's events is authoritative. ### Relay support detection A relay that supports subgroups SHOULD advertise it in its NIP-11 relay information document under a `nip29` object: ```json { "supported_nips": [29], "nip29": { "subgroups": true } } ``` ### Rules - Parenting and promotion to root are triggered by a `kind:9002` (`edit-metadata`) event from an authorized admin with the desired `parent` value (or no `parent` tag to detach); the relay then updates the group's `kind:39000` accordingly. The relay also updates the parent's `kind:39000` to include the newly added child as a `child` tag (and vice-versa). If a group is switching from one parent to another parent the relay also updates the previous parent accordingly. - Historical events under the group's `h` tag remain valid across these transitions. - A `kind:9002` MAY carry at most one `parent` tag. The resulting `kind:39000` therefore also carries at most one `parent` tag. - Absence of the `parent` tag makes the group a root. - Relays MUST reject a `kind:9002` whose `parent` value would create a cycle, including self-reference. - If a group's declared parent does not exist on the relay the `kind:9002` MUST be rejected. - When a parent is deleted (`kind:9008`), its remaining children automatically become roots. - Relays MUST reject any `kind:9002` carrying `["parent", "X"]` unless the event author is also an admin of `X` (per `X`'s `kind:39001`). - A parent group's `kind:39000` MUST include `["child", ""]` tags as an ordered list of its children; the position of each tag in the array is the order. By default relays can just append new children to the list as they are created, but the list can be rearranged by the parent's admin via a `kind:9002` carrying the desired `["child", ...]` tags in order, and each edit replaces the previous list. `kind:9002` metadata edits that do not include all the children as `child` tags MUST be rejected. ### Lifecycle example ```jsonc // "social" adopts "nostr" (was under "tech") { "kind": 9002, "tags": [["h", "nostr"], ["name", "Nostr"], ["parent", "social"]] } // Relay emits: { "kind": 39000, "tags": [["d", "nostr"], ["name", "Nostr"], ["parent", "social"]] } { "kind": 39000, "tags": [["d", "social"], ["child", "nostr"]] } // Admin promotes "nostr" to root: edit-metadata without a parent tag { "kind": 9002, "tags": [["h", "nostr"], ["name", "Nostr"]] } // Relay emits: { "kind": 39000, "tags": [["d", "nostr"], ["name", "Nostr"]] } { "kind": 39000, "tags": [["d", "social"]] } ``` ### Membership Each subgroup has its own independent membership: users join via `kind:9021` → `kind:9000` like any other group. Nothing is inherited across the parent-child link. Membership does not cascade: being a member of the parent grants no membership in any child, and each group's member list is defined solely by its own `kind:9000`/`kind:9001` events. Admin roles likewise do not inherit; each group's `kind:39001` is authoritative for its own scope, and being an admin of the parent grants no authority over any child unless the child's own `kind:39001` lists the same pubkey. This spec defines no automatic propagation of membership between parent and child. A relay that wants such behavior MAY emit the corresponding `kind:9000`/`kind:9001` moderation events itself, but clients MUST NOT assume it: the absence of a `kind:9000` in a child means the user is not a member of that child, regardless of parent membership. ## Implementation quirks ### Checking your own membership in a group The latest of either `kind:9000` or `kind:9001` events present in a group should tell a user that they are currently members of the group or if they were removed. In case none of these exist the user is assumed to not be a member of the group. ### Adding yourself to a group Anyone can send a `kind:9021` join request to a group. The relay may then generate a `kind:9000` event immediately, or defer that decision to an administrator. If a group is `closed`, join requests are not honored unless they include an invite code. ### Storing your list of groups A definition for `kind:10009` was included in [NIP-51](51.md) that allows clients to store the list of groups a user wants to remember being in.