Take another pass at nip 29

This commit is contained in:
Jon Staab 2024-09-17 16:55:29 -07:00
parent 6f2327cb77
commit eac3077849

371
29.md
View File

@ -6,153 +6,270 @@ Relay-based Groups
`draft` `optional`
This NIP defines a standard for the simplest version of groups that can work.
This NIP defines a standard for the simplest version of groups that can work, with optional extensions that provide read/write protection on both the relay and group level, and metadata such as group information and member lists.
There are two goals of this NIP:
The basic idea is that relays themselves have many characteristics of a group, in that they can enforce read/write policy at will. This mechanism also provides a convenient way to administer multiple sub-groups within a relay, since "canonical" metadata may be published using the pubkey listed in the relay's NIP 11 document.
- Define some conventions for how to use standard relays as groups.
- Introduces a mechanism for splitting relay content into multiple rooms.
# Table of contents
# Relays as groups
- [Relays](#relays)
- [Groups](#groups)
- [Support](#support)
- [Access](#access)
- [Membership](#membership)
- [Migration](#migration)
- [Moderation](#moderation)
- [Federation](#federation)
- [Use Cases](#use-cases)
## Minus Tag
# Relays
To ensure all messages have the appropriate context and/or are not replicated to other relays, a [NIP 70](70.md) `-` tag SHOULD be used when posting to a group relay (if support is indicated per [NIP 11](11.md)). "Follower" relays intended to back up group state MAY bypass this restriction.
Standard nostr relays can already be used as groups without any protocol changes, since every relay stores a unique set of events and may already implement access control. All additional recommendations in this document are optional.
## Relay Migration
To ensure all messages have the appropriate context and/or are not replicated to other relays, a [NIP 70](70.md) `-` tag MAY be used when posting to a relay (if support is indicated per [NIP 11](11.md)). "Follower" relays intended to back up relay state MAY bypass this restriction, although care should be taken to avoid backing up to relays with less strict access control than the master relay.
If a group needs to be moved from one relay to another, this can be done by publishing a `kind xxxxx`, with a `from` tag indicating the current relay, and a `to` tag indicating the next relay:
# Groups
A group is a sub-division of a relay, identified by a short random identifier. Any note may be posted to a group by including an `h` tag with the group's identifier as the tag value:
```json
{
"kind": "xxxxx",
"kind": 1,
"content": "Hello",
"tags": [
["from", "wss://relay.dead.com/"],
["to", "wss://relay.live.com/"],
],
}
```
These events SHOULD be published to the group's current relay, and MAY be published more broadly if more durability is desired (for example in the case where the relay goes down permanently before the migration is published). Anyone MAY publish these events, but end-users SHOULD be given ultimate discretion as to whether the migration is valid, based on group membership or admin status.
## Policy
Policy based on which NIPs/kinds are supported by a given relay should be advertised as defined in [NIP 11](11.md).
## Access Control
Access control (both read and write) SHOULD be implemented using [NIP 42](42.md) AUTH.
Access control policy MAY be advertised (but should not be relied upon by clients) using the `auth_required` and `restricted_writes` keys in the `limitations` section of the relay's NIP 11 document.
After authentication, users MAY request access using a `ACCESS` message sent ONLY to the relay in question. Relay access requests MUST have a `claim` containing an arbitrary string and an optional message to the relay admin.
```json
["ACCESS", "<claim>", "<message>"]
```
Users may also send an empty `ACCESS` message to query their current access:
```json
["ACCESS"]
```
In either case, a relay MUST respond with an `ACCESS` message with a list of user permissions, in addition to the standard `OK` message. Some examples:
```json
["ACCESS"]
["ACCESS", "read"]
["ACCESS", "read", "write"]
```
Valid permissions are `read` and `write`.
## Moderation
Relay-based groups have a single admin, identified by the `pubkey` listed in the relay's [NIP 11](11.md) document.
Any relay member MAY publish a `kind 1984` report to the relay (including a `-` tag if desired/supported). These reports MAY be used by the relay admin to delete events or ban users, or by clients to implement any moderation algorithm desired. Admins or moderators may also choose to escalate reports without banning content by publishing their own `kind 1984` event with the same tags.
## Membership
Users MAY track their own group memberships using a [NIP 51](51.md) kind `1xxxx` event. Tags MAY be either public or encrypted with [NIP 44](44.md), depending on user/client preference.
Membership lists can contain two types of tags: relay memberships (indicated by `r`), and room memberships (indicated by `h`, and including the relay url):
```json
{
"kind": "1xxxx",
"tags": [
["r", "wss://relay.example.com/"],
["r", "wss://relay.other.com/"],
["h", "Marlon Brando", "wss://relay.other.com/"],
["h", "James Dean", "wss://relay.other.com/"]
],
}
```
## Member lists
A `kind 3xxxx` event is a member list. Anyone MAY publish one, but clients SHOULD only trust lists published by the relay's own pubkey, or by moderators mentioned in the relay's member list event.
Lists published by regular members MAY be used for convenience in building member list indexes, but should not be trusted or relied upon.
Member lists should have the following tags:
- A `d` tag is required so that member lists can be split into pieces, but is semantically meaningless.
- An `r` tag indicates the relay's url.
- A `p` tag indicates a member and whether they are a moderator.
```json
{
"kind": "3xxxx",
"tags": [
["d", "9ce3f91a937"],
["r", "wss://relay.example.com/"],
["p", "<pubkey>", "<relay-url>"]
["p", "<pubkey>", "<relay-url>", "moderator"]
],
}
```
These events SHOULD be published only to the group relay, and use a `-` tag if supported.
## Content retention
Relays are not obligated to retain any content. Relays MAY choose not to support any part of this NIP, for example refusing host member lists in order to protect member privacy.
# Rooms
Rooms are defined by an arbitrary string containing any text. Names SHOULD be human-readable, and less than 30 characters.
Anyone MAY publish `kind xxxxx` index events containing `h` tags and the relay on which they exist. This is only a convenience for helping clients build room lists, and shouldn't be relied upon as being accurate or complete. `h` tags MUST indicate what type the room is. Types of rooms and their corresponding event kinds are defined below.
```json
{
"kind": "xxxxx",
"tags": [
["h", "salamanders", "wss://relay.froglovers.com", "chat"],
["h", "frogs", "wss://relay.froglovers.com", "chat"],
["h", "frogs", "wss://relay.catlovers.com", "forum"]
["h", "<group-id>"]
]
}
```
Events posted to rooms MUST include a simple `h` tag matching the room of the form `["h", "<room-name>"]`. An empty string MAY be used to indicate the "global" namespace.
A group's policies are inherited from the relay it is hosted on by default (in other words, if you can't access a relay, you can't access any of the groups within the relay), but can be further refined by the relay implementation.
## Chat
The special `_` group id MAY be used to refer to the relay's "lobby", as distinct from events posted to the relay without an `h` tag.
Two kinds of events MAY be posted to a `chat` room:
## Support
- `kind xxx` events are chat root events, and MUST only be used for top-level messages sent to the room.
- `kind xxx` events are chat reply events, and MUST only be used for replies to messages in the same room.
Relays may choose to either passively or actively support NIP 29.
In both cases, conventions for `kind 1` notes outlined in [NIP 10](10.md) and [NIP 18](18.md) should be followed when constructing chat events.
By default, relays are not expected to do anything special. Instead, group metadata, member lists, moderation, etc. may be inferred by clients from events published by users.
## Forums
Relays that actively support NIP 29 are expected to implement group policy and publish canonical metadata events using the host relay's `pubkey`. This serves to reduce the complexity of implementing compatible clients by offloading chores like building trustworthy member lists to relays.
Two kinds of events MAY be posted to a `forum` room:
If a relay includes `29` in the `supported_nips` field of their NIP-11 document, they MUST actively implement NIP 29. Clients may choose whether to implement fallback behavior for standard relays, or they may limit support only to NIP 29 compliant relays.
- `kind xxx` events are root thread events, and MUST only be used for the first post in a thread.
- `kind xxx` events are thread reply events, and MUST only be used to reply to a _root_ thread event.
## Group metadata
In both cases, conventions for `kind 1` notes outlined in [NIP 10](10.md) and [NIP 18](18.md) should be followed when constructing chat events.
Group metadata is defined by the following tags:
- `name` is a short string briefly describing the group
- `picture` is an image url
- `about` is a longer description of the group
- `public` indicates that events posted to the group can be _read_ by anyone
- `private` indicates that only AUTHed users can read events posted to the group
- `open` indicates that anyone can request to join and the request will be automatically granted
- `closed` indicates that members must be pre-approved or that requests to join will be manually handled.
> Note: it would probably be better to do `["access", "public|private"]` instead of two totally separate tags for the same policy flag.
A group may be created by publishing a `kind:9007` event to the host relay. Anyone with relay access can publish a group metadata event. `content` MAY be a message to the relay admin.
Group metadata may include tags describing group visibility and access, but groups that exist on relays without NIP 29 support indicated in `supported_nips` MUST be considered `public` and `open`.
```json
{
"kind": 9007,
"content": "",
"tags": [
["h", "<group-id>"],
["name", "Pizza Lovers"],
["picture", "https://pizza.com/pizza.png"],
["about", "a group for people who love pizza"],
["public"],
["open"]
]
}
```
A group may be edited by publishing a `kind:9002` event to the host relay. `content` MAY be a message to the relay admin.
```jsonc
{
"kind": 9002,
"content": "",
"tags": [
["h", "<group-id>"],
// ...group metadata tags
]
}
```
A group may be deleted by publishing a `kind:9008` event to the host relay. `content` MAY be a message to the relay admin.
```json
{
"kind": 9008,
"content": "",
"tags": [
["h", "<group-id>"],
]
}
```
On relays that actively implement NIP 29, group creation, edit, and deletion events MUST NOT be served, but MUST instead be interpreted by the relay implementation or admin to result in the creation, deletion, or modification of a `kind:39000` group metadata event. This event MUST be published by the host-relay's `pubkey` in order to be valid.
```jsonc
{
"kind": 39000,
"content": "",
"tags": [
["d", "<group-id>"],
// ...group metadata tags
]
}
```
> Note: it would probably be better to just have users publish kind 39000s instead of all the 9xxx stuff, since a history isn't really needed, similar to how member lists are treated below.
# Access
> Note: this section should be moved to a separate NIP, since it's generally useful. An old draft exists here: https://github.com/nostr-protocol/nips/pull/1079
Access control (both read and write) SHOULD be implemented based on the user currently authenticated via [NIP 42](42.md) AUTH.
Access control queries and requests are implemented using new message types, rather than events, in order to prevent broadcasting of sensitive data by relays that don't implement NIP 29. Any relay, regardless of whether they support NIP 29, MAY implement these verbs using an empty string as the group id.
Users MAY query for their current permissions using the `ACCESS` verb with an optional `group id`.
Relays MAY respond with zero or more messages using the same `ACCESS` verb.
- The first argument MUST be a group id.
- The second argument MUST be a comma-separated list of permissions, either `read` or `write`.
- The third argument MAY be a human-readable message to be displayed to the user.
```json
-> ["ACCESS"]
<- ["ACCESS", "", "read,write"]
<- ["ACCESS", "_", "read,write"]
<- ["ACCESS", "<group1-id>", "read"]
<- ["ACCESS", "<group2-id>", "", "We don't like your kind around here"]
```
Users MAY request access using a `JOIN` message sent ONLY to the relay in question. Relay access requests MUST have a `group id` and MAY have a `claim` containing an arbitrary string (such as an invite code, zap receipt, cashu token, etc). A message to the relay admin MAY be included.
```json
["JOIN", "<group-id>", "<claim>", "<message>"]
```
Users MAY also request that their access be revoked using a `LEAVE` messsage:
```json
["LEAVE"]
```
Relays MAY update internal state and member lists, or ignore these messages.
# Membership
## Group lists
Users MAY track their own group memberships using a [NIP 51](51.md) kind `10009` event. Tags MAY be either public or encrypted with [NIP 44](44.md), depending on user/client preference.
Group IDs are identified by `group`, with each value being the relay url and the group id joined by a `'`. Group id MAY be omitted if bare relay membership is desired.
```json
{
"kind": "10009",
"tags": [
["group", "wss://relay.example.com/"],
["group", "wss://relay.other.com/'a4041f55"],
["group", "wss://relay.other.com/'5d75430e"]
],
}
```
> Note: it may be worth changing this to single-letter tags so that group web of trust can be easily calculated, even if there's no member list. It also might be desirable to split each tag into primary/secondary value, so that `h` tags can be indexed as well, independent of the relay url. Something like this: `[["r", "<relay-url>"], ["h", "<group-id>", "<relay-url>"]]`
## Member lists
A `kind 39001` event is a moderator list. To be considered valid, a moderator list MUST be published by the relay's own pubkey.
A `kind 39002` event is a member list. Anyone MAY publish one, but clients SHOULD only trust lists published by the relay's own pubkey, or by moderators mentioned in the relay's member list event. Member lists published by regular members MAY be used for convenience in building member list indexes, but should not be trusted or relied upon.
Member and moderator lists should have the following tags:
- A `d` tag matching the group's id.
- A `p` tag indicating the person's pubkey and a relay hint.
```json
{
"kind": "39002",
"tags": [
["d", "<group-id>"],
["p", "<pubkey>", "<relay-url>"]
["p", "<pubkey>", "<relay-url>"]
],
}
```
Relays MAY choose to not to publish this information, restrict what pubkeys can fetch it, or reject any member lists published by users to the relay.
# Migration
If a group needs to be moved from one relay to another, this can be done by publishing a `kind 9009`, with the following tags:
- `h` indicates the group id.
- `r` with a `old` mark indicates the current relay.
- `r` with a `new` mark indicates the relay the group is moving to.
```json
{
"kind": "9009",
"tags": [
["h", "<group-id>"],
["r", "wss://relay.dead.com/", "old"],
["r", "wss://relay.live.com/", "new"]
],
}
```
These events SHOULD be published to the relay's current relay, and MAY be published more broadly if more durability is desired (for example in the case where the relay goes down permanently before the migration is published).
Anyone MAY publish these events, but migrations not published by the current relay's pubkey should be treated as a relay fork, rather than a migration.
# Moderation
Any relay member MAY publish a `kind 1984` report to the relay (including a `-` tag if desired/supported). These reports MAY be used by the relay admin to delete events or ban users, or by clients to implement any moderation algorithm desired. The relay operator or moderators may also choose to escalate reports without banning content by publishing their own `kind 1984` event with the same tags. Reports need not include an `h` tag.
# Federation
By default, the same group id on different relays does not mean that the same content will exist in both places. However, relays MAY choose to actively federate with others by aggressively replicating content between the two, or cooperating in some other way.
If a group is federated, this should be indicated on the `kind:39000` group metadata event using one or more `peer` tags indicating another relay url. The peer SHOULD NOT be considered valid unless the designation is mutual.
Clients should take care when supporting simultaneous relay use, since missing context is possible if the federation is not implemented correctly.
# Appendix: Use Cases
The concept of a "group" can mean many things to different people, for example:
- Public, moderated groups with low trust in moderators, for example reddit and forums
- Public-read write-protected "broadcast" telegram groups
- Private, non-sensitive "community" groups, for example facebook community groups
- Private, sensitive direct messages or small group chats, like exist on signal
- Hierarchical groups with many sub-groups and granular permissions, like slack or discord
- High-consistency synchronous chat
- Low-consistency asynchronous microblogging
These differences can be quantified across several axes:
- Size, in number of members
- Read and write access, enforced by AUTH or encryption
- Moderation, from strict to laissez faire
- Trust in relays, from complete to none
- Context-dependence, from low to high
This NIP is not suitable for direct messages or small group chats because of its weak privacy guarantees, and because person-oriented use cases don't benefit from the establishment of a single "place". Instead, messaging protocols like NIP 17 bridge the relay network by following user relay selections, duplicating messages and delivering them to user inboxes.
This NIP is not suitable for "private" groups, since access policies are enforced by relays. Some provisions are made here for "closed" groups, which expect some degree of privacy (for example the use of the `-` tag, relay-enforced access policies, and signature-stripping), but these should not be relied upon for high-stakes information hiding.
This NIP is not suitable for highly censorship-resistant groups by default, since they rely heavily upon relays to host group content. Simultaneous use across multiple relays is possible, but should be carefully considered. For example, chat rooms or forums require strong consistency to avoid leaving out missing context, whereas long-form content and microblogging use cases require less completeness. Relay groups that are strongly federated can improve consensus.
This NIP can accommodate a wide range of moderation approaches, depending on relay implementation and policy. Clients may also implement their own moderation in addition to moderation implemented by relays, for example based on user mutes or web of trust.