-
Notifications
You must be signed in to change notification settings - Fork 736
NIP-FR: Add Friends-Only Notes #2207
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
Draft
abh3po
wants to merge
2
commits into
nostr-protocol:master
Choose a base branch
from
abh3po:patch-3
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
2 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,219 @@ | ||
| # NIP-FR | ||
|
|
||
| ## Friends-Only Notes | ||
|
|
||
| `draft` `optional` | ||
|
|
||
| This NIP defines a mechanism for publishing encrypted notes visible only to a user-defined friends list using a shared nostr secret: ViewKey. | ||
|
|
||
| ## Motivation | ||
|
|
||
| Users want to share content with a close circle of friends without it being publicly readable. This NIP provides a simple symmetric-key approach where a "ViewKey" encrypts notes and is distributed only to approved friends. | ||
|
|
||
| ## Definitions | ||
|
|
||
| - **ViewKey**: A symmetric key used to encrypt/decrypt friends-only notes | ||
| - **Friend List**: A set of pubkeys authorized to receive the ViewKey | ||
| - **Circle**: A named group of friends (e.g., "friends", "family", "coworkers") | ||
|
|
||
| ## Event Kinds | ||
|
|
||
| | Kind | Description | | ||
| | ------- | ------------------------------------- | | ||
| | `44` | ViewKey share rumor (inside giftwrap) | | ||
| | `2044` | Friends-only encrypted note | | ||
| | `1045` | ViewKey share seal | | ||
| | `30144` | Friend list and ViewKey storage | | ||
|
|
||
| ## Kind 2044: Friends-Only Note | ||
|
|
||
| A friends-only note is a kind `2044` event with content encrypted using NIP-44 with the ViewKey as the shared secret. | ||
|
|
||
| ```json | ||
| { | ||
| "kind": 2044, | ||
| "pubkey": "<author-pubkey>", | ||
| "created_at": <timestamp>, | ||
| "content": "<NIP-44 encrypted with ViewKey>", | ||
| "tags": [], | ||
| "id": "<event-id>", | ||
| "sig": "<signature>" | ||
| } | ||
| ``` | ||
|
|
||
| The decrypted content is the note text (equivalent to kind 1 content). | ||
|
|
||
| ### Multiple Circles | ||
|
|
||
| If a user maintains multiple circles, include `["d", "<circle-id>"]` tag to indicate which circle's ViewKey to use (leaks circle id) | ||
|
|
||
| ## Kind 30144: Friend List | ||
|
|
||
| A parameterized replaceable event storing the friend list and ViewKey for a circle. The content is NIP-44 encrypted to the author's own pubkey. | ||
|
|
||
| ```json | ||
| { | ||
| "kind": 30144, | ||
| "pubkey": "<author-pubkey>", | ||
| "created_at": <timestamp>, | ||
| "content": "<NIP-44 encrypted to self>", | ||
| "tags": [ | ||
| ["d", "<circle-id>"] | ||
| ], | ||
| "id": "<event-id>", | ||
| "sig": "<signature>" | ||
| } | ||
| ``` | ||
|
|
||
| ### Decrypted Content | ||
|
|
||
| The decrypted content is a JSON-encoded tags array: | ||
|
|
||
| ```json | ||
| [ | ||
| ["viewKey", "hex-viewkey>"], | ||
| ["p", "<friend-pubkey-1>"], | ||
| ["p", "<friend-pubkey-2>"] | ||
| ] | ||
| ``` | ||
|
|
||
| - `viewKey`: The symmetric key used to encrypt kind 2044 notes for this circle | ||
| - `p`: Pubkeys of friends in this circle | ||
|
|
||
| The friend list is encrypted to preserve social graph privacy. | ||
|
|
||
| ## ViewKey Distribution | ||
|
|
||
| ViewKeys are distributed using NIP-59 giftwrap with the following structure: | ||
|
|
||
| ``` | ||
| Kind 1059 (gift wrap, encrypted to recipient) | ||
| └── Kind 1045 (seal, encrypted to recipient) | ||
| └── Kind 44 (rumor, the ViewKey payload) | ||
| ``` | ||
|
|
||
| ### Kind 44: ViewKey Share Rumor | ||
|
|
||
| ```json | ||
| { | ||
| "kind": 44, | ||
| "pubkey": "<sender-pubkey>", | ||
| "created_at": <timestamp>, | ||
| "content": "<ViewKey>", | ||
| "tags": [ | ||
| ["d", "<circle-id>"] | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| - `content`: The base64-encoded ViewKey | ||
| - `d` tag: Identifies which circle this ViewKey belongs to | ||
|
|
||
| ### Kind 1045: ViewKey Share Seal | ||
|
|
||
| The kind 44 rumor is wrapped in a kind 1045 seal, encrypted to the recipient using NIP-44. | ||
|
|
||
| ```json | ||
| { | ||
| "kind": 1045, | ||
| "pubkey": "<sender-pubkey>", | ||
| "created_at": <timestamp>, | ||
| "content": "<NIP-44 encrypted kind 44 rumor>", | ||
| "tags": [] | ||
| } | ||
| ``` | ||
|
|
||
| The seal is then wrapped in a standard NIP-59 kind 1059 gift wrap. | ||
|
|
||
| ## Flows | ||
|
|
||
| ### Adding a Friend | ||
|
|
||
| 1. Add the friend's pubkey to the kind 30144 event for the circle | ||
| 2. Update the 30144 event on relays | ||
| 3. Gift wrap the ViewKey (kind 44 rumor) to the new friend | ||
|
|
||
| ### Removing a Friend | ||
|
|
||
| 1. Remove the friend's pubkey from the kind 30144 event | ||
| 2. Generate a new ViewKey | ||
| 3. Update the 30144 event with the new ViewKey | ||
| 4. Gift wrap the new ViewKey to all remaining friends in the circle | ||
|
|
||
| ### Manual Key Rotation | ||
|
|
||
| For security, users may manually rotate the ViewKey: | ||
|
|
||
| 1. Generate a new ViewKey | ||
| 2. Update the kind 30144 event with the new ViewKey | ||
| 3. Gift wrap the new ViewKey to all friends in the circle | ||
|
|
||
| ### Receiving a ViewKey | ||
|
|
||
| When a client receives a gift-wrapped ViewKey: | ||
|
|
||
| 1. Decrypt the gift wrap and extract the kind 44 rumor | ||
| 2. Check if sender is already in one of the user's friend lists: | ||
| - **If yes**: Automatically accept, keep ViewKey in memory, subscribe to sender's kind 2044 events | ||
| - **If no**: Present a friend request UI with three options: | ||
|
|
||
| **Friend Request Options:** | ||
|
|
||
| | Option | Action | | ||
| | --------------------- | --------------------------------------------------------------------------------- | | ||
| | **Accept** | Add to reading list. User sees sender's posts, sender does not see user's posts. | | ||
| | **Accept and Friend** | Add to reading list AND share user's ViewKey back with sender. Mutual friendship. | | ||
| | **Decline** | Store sender in reject list so request isn't shown again. | | ||
|
|
||
| Note: The ViewKey is received regardless of choice - the user can technically decrypt the sender's posts. These options control the client's behavior and whether to reciprocate. | ||
|
|
||
| ### Creating a Friends-Only Post | ||
|
|
||
| 1. Fetch the user's own kind 30144 event for the target circle from relays | ||
| 2. Decrypt the 30144 content to retrieve the ViewKey | ||
| 3. Encrypt the note content using NIP-44 with the ViewKey | ||
| 4. Publish as a kind 2044 event with optional `["d", "<circle-id>"]` tag | ||
|
|
||
| ### Reading Friends-Only Posts | ||
|
|
||
| 1. On session start, fetch giftwrapped messages (kind 1059) addressed to the user | ||
| 2. Decrypt giftwraps to extract ViewKeys, keep in memory | ||
| 3. Query kind 2044 events from senders whose ViewKeys were accepted | ||
| 4. Decrypt each 2044 using the corresponding ViewKey from memory | ||
|
|
||
| ## Client Behavior | ||
|
|
||
| ### Session Initialization | ||
|
|
||
| 1. Fetch kind 1059 (giftwrap) events addressed to the user from relays | ||
| 2. Decrypt each to extract kind 44 rumor containing ViewKey, sender pubkey, and circle-id | ||
| 3. Keep ViewKeys in memory, keyed by sender pubkey and circle-id | ||
|
|
||
| ### Publishing | ||
|
|
||
| 1. Fetch the user's kind 30144 event for the circle from relays | ||
| 2. Decrypt to retrieve the ViewKey | ||
| 3. Encrypt the note content using NIP-44 with the ViewKey | ||
| 4. Publish as a kind 2044 event | ||
|
|
||
| ### Reading | ||
|
|
||
| 1. Query kind 2044 events from senders whose ViewKeys were accepted | ||
| 2. Decrypt using the ViewKey from memory | ||
| 3. If multiple circles exist, try ViewKeys until decryption succeeds | ||
|
|
||
| ### Circle Naming | ||
|
|
||
| The `d` tag identifies circles. Clients MAY use any identifier. Suggested default: `"friends"`. | ||
|
|
||
| ## Tradeoffs | ||
|
|
||
| ### No Backward Secrecy | ||
|
|
||
| When a friend is removed and the ViewKey is rotated, they cannot decrypt **new** notes (forward secrecy). However, they may have cached the old ViewKey and can still decrypt **old** notes. This is an accepted tradeoff for simplicity. | ||
|
|
||
| Clients SHOULD inform users of this limitation when removing friends. | ||
|
|
||
| ### Circle-ID Metadata Leakage | ||
|
|
||
| If kind 2044 notes include a `["d", "<circle-id>"]` tag for efficient decryption, the circle name is visible to anyone. This reveals which circle a post belongs to without revealing its content. | ||
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.
This seems unnecessarily limiting. I would make the viewkey a nostr secret, which would enable you to gift wrap any event to the circle. See #875 for a group scheme I worked on for a while. It would also improve privacy because it would be indistinguishable from regular gift wraps.
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.
true. I just started implementing it with kind 1s and this is an artifact, will fix.