-
Notifications
You must be signed in to change notification settings - Fork 721
Add closed communities #875
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,151 @@ | ||||||
| NIP-87 | ||||||
| ======= | ||||||
|
|
||||||
| Closed Groups | ||||||
| ------------------ | ||||||
|
|
||||||
| Some online communities may want a measure of privacy. While nostr's architecture isn't conducive to high levels of privacy due to metadata leakage and the difficulty of implementing ratchets for key rotation, it can provide basic encrypted channels appropriate for use with large groups with dynamic member lists. | ||||||
|
|
||||||
| This NIP depends on [NIP 59](./59.md) for encrypted payloads. | ||||||
|
|
||||||
| # Protocol Description | ||||||
|
|
||||||
| ## Group Definition | ||||||
|
|
||||||
| Groups are identified by the address of a replaceable `kind 35834` event. This may be wrapped using the shared key to support privately-defined groups, or published normally to support a public listing for the private group. | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "kind": 35834, | ||||||
| "content": "", | ||||||
| "tags": [ | ||||||
| ["d", "<uuid>"], | ||||||
| ["name", "<Group name>"], | ||||||
| ["about", "<Group description>"], | ||||||
| ["picture", "<Group thumnail>"], | ||||||
| ["banner", "<Group banner image>"], | ||||||
| ["relay", "<url>"], | ||||||
| ["relay", "<url>"] | ||||||
| ] | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| ## Admin Key | ||||||
|
|
||||||
| The key used to publish the group's `kind 35834` definition event MUST be used for performing privileged actions, such as publishing key rotations. Non-administrative events MAY be published to the group by the admin. | ||||||
|
|
||||||
| Because the admin key is used for managing key rotation, it's recommended that a dedicated key be used for group administration instead of the group creator's personal key. | ||||||
|
|
||||||
| ## Key Sharing | ||||||
|
|
||||||
| Keys are shared via a `kind 24` rumor sealed by the `admin` key, wrapped using an ephemeral key, and addressed to each group member individually, as well as to the admin's pubkey. | ||||||
|
|
||||||
| The rumor MUST have the following tags: | ||||||
|
|
||||||
| - An `a` tag pointing to the group's address. | ||||||
| - A single `privkey` tag containing the shared private key. | ||||||
| - One or more `relay` tags to indicate where messages should be sent. | ||||||
| - The rumor MAY include a `role` tag describing the key's intended use. | ||||||
| - A `role` tag set to `member`. | ||||||
|
|
||||||
| Admins MAY share admin keys with other users the same way, with `role` set to `admin`. Admins SHOULD send admin keys to the user's primary key for use on multiple devices, or across sessions. | ||||||
|
|
||||||
staab marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| ```json | ||||||
| { | ||||||
| "kind": 24, | ||||||
| "content": "", | ||||||
| "tags": [ | ||||||
| ["a", "35834:<admin pubkey>:<group name>"], | ||||||
| ["privkey", "<the private key being shared>"], | ||||||
| ["relay", "<where to find group messages"], | ||||||
| ["role", "<member|admin>"] | ||||||
| ] | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| ## Key Rotation | ||||||
|
|
||||||
| To rotate the shared key, the `admin` must publish a new `kind 24` rumor as described above. This invalidates previous keys and replaces them with a new shared key. | ||||||
|
|
||||||
| An `expiration` tag (defined by NIP-40) MAY be included on the wrapper to support a weak form of forward secrecy (weak, since it relies on relays to delete the event). This can reduce the impact of a member's private key being leaked, which could otherwise expose old shared keys and messages addressed to those keys. | ||||||
|
|
||||||
| When revoking access from pubkeys who previously had access, a key rotation event SHOULD still be sent to those pubkeys, but without the `privkey` tag. | ||||||
|
|
||||||
| ## Access Requests | ||||||
|
|
||||||
| Anyone may request access to a group using a `kind 25` rumor wrapped with an ephemeral key and addressed to the admin's key. The rumor's `content` MAY include a message, and its `tags` MUST include an `a` tag pointing to the group definition event's address. `tags` MAY also include a `claim` which admins can use to validate the access request, for example an invite code or payment receipt. | ||||||
|
|
||||||
| On receipt, an admin MAY choose to admit the pubkey either by rotating keys or sharing the most recent shared key. | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "kind": 25, | ||||||
| "content": "Pleeease let me in", | ||||||
| "tags": [ | ||||||
| ["a", "35834:<admin pubkey>:<group name>"], | ||||||
| ["claim", "NOSTR2023"] | ||||||
| ] | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| ## Exit Requests | ||||||
|
|
||||||
| Anyone may request to be removed from a group using a `kind 26` rumor wrapped with an ephemeral key and addressed to the admin's key. The rumor's `content` MAY include a message, and its `tags` MUST include an `a` tag pointing to the group definition event's address. | ||||||
|
|
||||||
| On receipt, an admin SHOULD remove the pubkey by rotating keys. | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "kind": 26, | ||||||
| "content": "I'm outta here", | ||||||
| "tags": [["a", "35834:<admin pubkey>:<group name>"]] | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| ## Messages | ||||||
|
|
||||||
| Any group member MAY post an event to the group either publicly following NIP 72, or privately. Private events MUST be sealed by the user's own key, wrapped by the shared key, and addressed to the shared key. Anyone with the shared key may decrypt these messages. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
If the wrapper's pubkey is the shared one, members can already filter by author instead of by #p. |
||||||
|
|
||||||
| All messages MUST include an `a` tag (on the rumor if wrapped) pointing to the group definition event's address. Events of any kind MAY be published to the group either publicly or privately. | ||||||
|
|
||||||
| All events SHOULD be published only to relays specified by the `kind 35834` group definition event. | ||||||
|
|
||||||
| ## Member lists | ||||||
|
|
||||||
| Admins MAY publish member lists using `kind 27`. This MAY be published as a normal event, or wrapped and sent to the group. An `op` tag indicates whether the listed pubkeys are being `add`ed to the group, `remove`d from the group, or whether the member list is being `set`. An `a` tag MUST be included pointing to the group definition event's address. | ||||||
|
|
||||||
| ```json | ||||||
| { | ||||||
| "kind": 27, | ||||||
| "content": "", | ||||||
| "tags": [ | ||||||
| ["a", "35834:<admin pubkey>:<group name>"], | ||||||
| ["op", "add"], | ||||||
| ["p", "<pubkey 1>"], | ||||||
| ["p", "<pubkey 2>"], | ||||||
| ] | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| # Other Notes | ||||||
|
|
||||||
| ## Admin key vulnerabilities | ||||||
|
|
||||||
| Since ultimate control of the group lies with the holder of the admin key, the security of this key is paramount. Leakage of this key would result in complete compromise of the group, as well as the doxxing of all members who have posted to the group. Management of this key is up to the group admin, but should be taken seriously. | ||||||
|
|
||||||
| An ideal solution would be to use an air-gapped signing mechanism to publish events, viable since the group admin need not publish often except to rotate shared keys. Also viable would be to manage key access using an nsec bunker. | ||||||
|
|
||||||
| ## Shared key vulnerabilities | ||||||
|
|
||||||
| Any member of the group can implicitly invite new members to the group, since they have the private key (although this would have to happen out-of-band, since the private key is stored in a `rumor`). Any member of a group can dox other members by publishing their wrapped messages. Any member of the group can spam the group, or otherwise DOS the group. Any member can post anonymously to the group by sealing their messages with an alternative or ephemeral key. | ||||||
|
|
||||||
| If any single member leaks the shared secret, all messages can then be decrypted by others until the next key rotation. The use of optional frequent forward secrecy rotation events can mitigate these attacks, provided the server is compliant with the expiration times. | ||||||
|
|
||||||
| ## Anonymous membership | ||||||
|
|
||||||
| To post to a group, you must reveal your public key, doxxing yourself to all other members of a group, all of whom are able in turn to reveal your identity outside the group. To avoid this, users may join groups anonymously by using a keypair separate from their primary key to sign notes. They may then publish an alternative kind0 to the group to maintain a different identity. Clients should help users to manage their alternative key using an appropriate strategy. | ||||||
|
|
||||||
| ## Always-on admin key | ||||||
|
|
||||||
| There is no need for an admin to be always online, since key rotation requests do not expire. However, for larger groups timely key rotation might be desired. Because an admin key is distinct from that of the group owner(s), it would be easy to create an always-on service that can automate key rotations. | ||||||
|
|
||||||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how do we wrap keys with ephemeral keys? What is meant by an ephemeral key? I'm new to cryptography, anything I could read up on? might be good to expand upon this on the nip?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An ephemeral key is just a key that gets generated in an ad-hoc fashion, for example with nostr-tools'
generateSecretKey, and thrown away. It's used instead of a user's regular private key when the identifying information of the public key used to sign is not desirable.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but wouldn't the user need the corresponding pubkey to decrypt the
privkeytag? is it shared out of band? also how does the user get notified about being added on the group? is thekind:24event shared out of band as well?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's all wrapped inside a 1059, which is p-tagged to the recipient. So clients have to decrypt those to find out what is inside. They can decrypt them, because the payload is encrypted for the recipient's private key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @staab intended to have two kind 24 events, one with
adminrole with the editing key and one with thememberrole (terrible name) for the view-only key.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay I think I finally got it, hadn't gone through nip-59 It'd be nice to mention nip-59 on this doc. Was not aware of it and was very confused :P