-
Notifications
You must be signed in to change notification settings - Fork 602
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
Make NIP-51 useful again #880
Changes from 12 commits
5dc6071
da05b5d
1bbd87c
cf1b172
fd288d4
54befcc
61b7365
4db9914
804ee18
cd598ba
6c3eebf
31382e5
2a0b701
4a77fc5
2d678bc
5e6e7a2
33a719f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,148 +6,116 @@ Lists | |
|
||
`draft` `optional` `author:fiatjaf` `author:arcbtc` `author:monlovesmango` `author:eskema` `author:gzuuus` | ||
|
||
A "list" event is defined as having a list of public and/or private tags. Public tags will be listed in the event `tags`. Private tags will be encrypted in the event `content`. Encryption for private tags will use [NIP-04 - Encrypted Direct Message](04.md) encryption, using the list author's private and public key for the shared secret. A distinct event kind should be used for each list type created. | ||
This NIP defines lists of things that users can create. Lists can contain references to anything, and these | ||
references can be **public** or **private**. | ||
|
||
If a list should only be defined once per user (like the "mute" list) the list is declared as a _replaceable event_. These lists may be referred to as "replaceable lists". Otherwise, the list is a _parameterized replaceable event_ and the list name will be used as the `d` tag. These lists may be referred to as "parameterized replaceable lists". | ||
Public items in a list are specified in the event `tags` array, while private items are specified in a JSON | ||
array that mimics the structure of the event `tags` array, but stringified and encrypted using the same | ||
scheme from [NIP-04](04.md) (the shared key is computed using the author's public and private key) and | ||
stored in the `.content`. | ||
|
||
## Replaceable List Event Example | ||
## Types of lists | ||
|
||
Lets say a user wants to create a 'Mute' list and has keys: | ||
``` | ||
priv: fb505c65d4df950f5d28c9e4d285ee12ffaf315deef1fc24e3c7cd1e7e35f2b1 | ||
pub: b1a5c93edcc8d586566fde53a20bdb50049a97b15483cb763854e57016e0fa3d | ||
``` | ||
The user wants to publicly include these users: | ||
## Standard lists | ||
|
||
```json | ||
["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"], | ||
["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"] | ||
``` | ||
and privately include these users (below is the JSON that would be encrypted and placed in the event content): | ||
Standard lists use non-parameterized replaceable events, meaning users may only have a single list | ||
of each kind. They have special meaning and clients may rely on them to augment a user's profile or | ||
browsing experience. | ||
|
||
```json | ||
[ | ||
["p", "9ec7a778167afb1d30c4833de9322da0c08ba71a69e1911d5578d3144bb56437"], | ||
["p", "8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168"] | ||
] | ||
``` | ||
For example, _mute lists_ can contain the public keys of spammers and bad actors users don't want to see in | ||
their feeds or receive annoying notifications from. | ||
|
||
Then the user would create a 'Mute' list event like below: | ||
| name | kind | description | expected tag items | | ||
| --- | --- | --- | --- | | ||
| Mute list | 10000 | things the user doesn't want to see in their feeds | `"p"` (pubkeys), `"t"` (hashtags), `"word"` (lowercase string), `"e"` (threads) | | ||
| Pinned notes | 10001 | events the user intends to showcase in their profile page | `"e"` (kind:1 notes) | | ||
| Bookmarks | 10003 | things the user intends to save for the future | `"e"` (kind:1 notes), `"a"` (kind:30023 articles) | | ||
| Communities | 10004 | [NIP-72](72.md) communities the user belongs to | `"a"` (kind:34550 community definitions) | | ||
| Public chats | 10005 | [NIP-28](28.md) chat channels the user is in | `"e"` (kind:40 community definitions) | | ||
| Blocked relays | 10006 | relays clients should never connect to | `"relay"` (relay URLs) | | ||
| Search relays | 10007 | relays clients should use when performing search queries | `"relay"` (relay URLs) | | ||
| Emojis | 10030 | user preferred emojis and pointers to emoji sets | `"emoji"` (see [NIP-30](30.md)) and `"a"` (kind:30030 emoji set) | | ||
|
||
```json | ||
{ | ||
"kind": 10000, | ||
"tags": [ | ||
["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"], | ||
["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"], | ||
], | ||
"content": "VezuSvWak++ASjFMRqBPWS3mK5pZ0vRLL325iuIL4S+r8n9z+DuMau5vMElz1tGC/UqCDmbzE2kwplafaFo/FnIZMdEj4pdxgptyBV1ifZpH3TEF6OMjEtqbYRRqnxgIXsuOSXaerWgpi0pm+raHQPseoELQI/SZ1cvtFqEUCXdXpa5AYaSd+quEuthAEw7V1jP+5TDRCEC8jiLosBVhCtaPpLcrm8HydMYJ2XB6Ixs=?iv=/rtV49RFm0XyFEwG62Eo9A==", | ||
...other fields | ||
} | ||
``` | ||
## Sets | ||
|
||
Sets are lists with well-defined meaning that can enhance the functionality and the UI of clients that rely | ||
on them. Unlike standard lists, users are expected to have more than one set of each kind, therefore each of | ||
them must be assigned a different `"d"` identifier. | ||
|
||
## Parameterized Replaceable List Event Example | ||
For example, _relay sets_ can be displayed in a dropdown UI to give users the option to switch to which | ||
relays they will publish an event or from which relays they will read the replies to an event; _curation sets_ | ||
can be used by apps to showcase curations made by others tagged to different topics. | ||
|
||
Lets say a user wants to create a 'Categorized People' list of `nostr` people and has keys: | ||
``` | ||
priv: fb505c65d4df950f5d28c9e4d285ee12ffaf315deef1fc24e3c7cd1e7e35f2b1 | ||
pub: b1a5c93edcc8d586566fde53a20bdb50049a97b15483cb763854e57016e0fa3d | ||
``` | ||
The user wants to publicly include these users: | ||
Aside from their main identifier, the `"d"` tag, sets can optionally have a `"name"`, a `"picture"` and an | ||
`"about"` tag that can be used to enhance their UI. | ||
|
||
```json | ||
["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"], | ||
["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"] | ||
``` | ||
and privately include these users (below is the JSON that would be encrypted and placed in the event content): | ||
| name | kind | description | expected tag items | | ||
| --- | --- | --- | --- | | ||
| Follow sets | 30000 | categorized groups of users a client may choose to check out in different circumstances | `"p"` (pubkeys) | | ||
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. These aren't always for "follows", sometimes they're lists of people that share a certain interest, etc. I think we should call this "People Sets" 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. I agree, but that is the old name and we must change something since that wasn't working. |
||
| Relay sets | 30002 | user-defined relay groups the user can easily pick and choose from during variadic operations | `"relay"` (relay URLs) | | ||
| Curation sets | 30004 | groups of articles picked by users as interesting and/or belonging to the same category | `"a"` (kind:30023 articles), `"e"` (kind:1 notes) | | ||
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. Suggestion (probably for another day)
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. We're doing something similar in YakiHonne related to the user preferences for content customizations via
|
||
| Emoji sets | 30030 | categorized emoji groups | `"emoji"` (see [NIP-30](30.md)) | | ||
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. Suggestion to add: 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. @pablof7z is it worth also mentioning Hightlight lists? 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.
How about this from above it complies in your side and some of what @staab wants to achieve I Think his proposition is based on the advance search he has on coracle.social 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. @Darecjo if your list is automatically named @erskingardner would these things be a single standard list or multiple? It's hard to imagine someone having interests, which is such a generic concept, categorized in different groups. I feel like each interest is already a "group" on its own. On the other hand one interest is translated into potentially huge number of tags -- i.e. if I'm interested in "fruits" I may have the hashtags "banana", "mango", "passionfruit" etc. Or if I'm interested in "banana" I may have "banana", "cavendish", "bananas", "plaintain", "banana-recipes", "cooking-bananas" whatever. So what do we do? Add both a canonical standard interests list and interest sets? 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. Good question. @pablof7z added Maybe that means there is a case to do both 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.
We are going to check the interests list and sets since they are more suitable for our case it is basically the same thing. |
||
|
||
```json | ||
[ | ||
["p", "9ec7a778167afb1d30c4833de9322da0c08ba71a69e1911d5578d3144bb56437"], | ||
["p", "8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168"] | ||
] | ||
``` | ||
## Deprecated standard lists | ||
|
||
Some clients have used these lists in the past, but they should work on transitioning to the [standard formats](#standard-lists) above. | ||
|
||
| kind | "d" tag | use instead | | ||
| --- | --- | --- | | ||
| 30000 | `"mute"` | kind 10000 _mute list_ | | ||
| 30001 | `"pin"` | kind 10001 _pin list_ | | ||
| 30001 | `"bookmark"` | kind 10003 _bookmarks list_ | | ||
| 30001 | `"communities"` | kind 10004 _communities list_ | | ||
|
||
## Examples | ||
|
||
Then the user would create a 'Categorized People' list event like below: | ||
### A _mute list_ with some public items and some encrypted items | ||
|
||
```json | ||
{ | ||
"kind": 30000, | ||
"id": "a92a316b75e44cfdc19986c634049158d4206fcc0b7b9c7ccbcdabe28beebcd0", | ||
"pubkey": "854043ae8f1f97430ca8c1f1a090bdde6488bd5115c7a45307a2a212750ae4cb", | ||
"created_at": 1699597889, | ||
"kind": 10000, | ||
"tags": [ | ||
["d", "nostr"], | ||
["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"], | ||
["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"], | ||
["p", "07caba282f76441955b695551c3c5c742e5b9202a3784780f8086fdcdc1da3a9"], | ||
["p", "a55c15f5e41d5aebd236eca5e0142789c5385703f1a7485aa4b38d94fd18dcc4"] | ||
], | ||
"content": "VezuSvWak++ASjFMRqBPWS3mK5pZ0vRLL325iuIL4S+r8n9z+DuMau5vMElz1tGC/UqCDmbzE2kwplafaFo/FnIZMdEj4pdxgptyBV1ifZpH3TEF6OMjEtqbYRRqnxgIXsuOSXaerWgpi0pm+raHQPseoELQI/SZ1cvtFqEUCXdXpa5AYaSd+quEuthAEw7V1jP+5TDRCEC8jiLosBVhCtaPpLcrm8HydMYJ2XB6Ixs=?iv=/rtV49RFm0XyFEwG62Eo9A==", | ||
...other fields | ||
"content": "TJob1dQrf2ndsmdbeGU+05HT5GMnBSx3fx8QdDY/g3NvCa7klfzgaQCmRZuo1d3WQjHDOjzSY1+MgTK5WjewFFumCcOZniWtOMSga9tJk1ky00tLoUUzyLnb1v9x95h/iT/KpkICJyAwUZ+LoJBUzLrK52wNTMt8M5jSLvCkRx8C0BmEwA/00pjOp4eRndy19H4WUUehhjfV2/VV/k4hMAjJ7Bb5Hp9xdmzmCLX9+64+MyeIQQjQAHPj8dkSsRahP7KS3MgMpjaF8nL48Bg5suZMxJayXGVp3BLtgRZx5z5nOk9xyrYk+71e2tnP9IDvSMkiSe76BcMct+m7kGVrRcavDI4n62goNNh25IpghT+a1OjjkpXt9me5wmaL7fxffV1pchdm+A7KJKIUU3kLC7QbUifF22EucRA9xiEyxETusNludBXN24O3llTbOy4vYFsq35BeZl4v1Cse7n2htZicVkItMz3wjzj1q1I1VqbnorNXFgllkRZn4/YXfTG/RMnoK/bDogRapOV+XToZ+IvsN0BqwKSUDx+ydKpci6htDRF2WDRkU+VQMqwM0CoLzy2H6A2cqyMMMD9SLRRzBg==?iv=S3rFeFr1gsYqmQA7bNnNTQ==", | ||
"sig": "1173822c53261f8cffe7efbf43ba4a97a9198b3e402c2a1df130f42a8985a2d0d3430f4de350db184141e45ca844ab4e5364ea80f11d720e36357e1853dba6ca" | ||
} | ||
``` | ||
|
||
Lets say a user wants to create a 'Categorized Bookmarks' list of `bookmarks` and has keys: | ||
``` | ||
priv: fb505c65d4df950f5d28c9e4d285ee12ffaf315deef1fc24e3c7cd1e7e35f2b1 | ||
pub: b1a5c93edcc8d586566fde53a20bdb50049a97b15483cb763854e57016e0fa3d | ||
``` | ||
The user wants to publicly include these bookmarks: | ||
|
||
```json | ||
["e", "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "wss://nostr.example.com"], | ||
["a", "30023:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", "wss://nostr.example.com"], | ||
["r", "https://github.com/nostr-protocol/nostr", "Nostr repository"], | ||
``` | ||
and privately include these bookmarks (below is the JSON that would be encrypted and placed in the event content): | ||
### A _curation set_ of articles and notes about yaks | ||
|
||
```json | ||
[ | ||
["r", "https://my-private.bookmark", "My private bookmark"], | ||
["a", "30001:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", "wss://nostr.example.com"], | ||
] | ||
``` | ||
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. Nitpick: Change this to |
||
|
||
Then the user would create a 'Categorized Bookmarks' list event like below: | ||
|
||
```json | ||
{ | ||
"kind": 30001, | ||
"id": "567b41fc9060c758c4216fe5f8d3df7c57daad7ae757fa4606f0c39d4dd220ef", | ||
"pubkey": "d6dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c", | ||
"created_at": 1695327657, | ||
"kind": 30004, | ||
"tags": [ | ||
["d", "bookmarks"], | ||
["e", "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "wss://nostr.example.com"], | ||
["a", "30023:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", "wss://nostr.example.com"], | ||
["r", "https://github.com/nostr-protocol/nostr", "Nostr repository"], | ||
["d", "jvdy9i4"], | ||
["name", "Yaks"], | ||
["picture", "https://cdn.britannica.com/40/188540-050-9AC748DE/Yak-Himalayas-Nepal.jpg"], | ||
["about", "The domestic yak, also known as the Tartary ox, grunting ox, or hairy cattle, is a species of long-haired domesticated cattle found throughout the Himalayan region of the Indian subcontinent, the Tibetan Plateau, Gilgit-Baltistan, Tajikistan and as far north as Mongolia and Siberia."], | ||
["a", "30023:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:95ODQzw3ajNoZ8SyMDOzQ"], | ||
["a", "30023:54af95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:1-MYP8dAhramH9J5gJWKx"], | ||
["a", "30023:f8fe95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:D2Tbd38bGrFvU0bIbvSMt"], | ||
["e", "d78ba0d5dce22bfff9db0a9e996c9ef27e2c91051de0c4e1da340e0326b4941e"] | ||
], | ||
"content": "y3AyaLJfnmYr9x9Od9o4aYrmL9+Ynmsim5y2ONrU0urOTq+V81CyAthQ2mUOWE9xwGgrizhY7ILdQwWhy6FK0sA33GHtC0egUJw1zIdknPe7BZjznD570yk/8RXYgGyDKdexME+RMYykrnYFxq1+y/h00kmJg4u+Gpn+ZjmVhNYxl9b+TiBOAXG9UxnK/H0AmUqDpcldn6+j1/AiStwYZhD1UZ3jzDIk2qcCDy7MlGnYhSP+kNmG+2b0T/D1L0Z7?iv=PGJJfPE84gacAh7T0e6duQ==", | ||
...other fields | ||
"content": "", | ||
"sig": "a9a4e2192eede77e6c9d24ddfab95ba3ff7c03fbd07ad011fff245abea431fb4d3787c2d04aad001cb039cb8de91d83ce30e9a94f82ac3c5a2372aa1294a96bd" | ||
} | ||
``` | ||
|
||
## List Event Kinds | ||
|
||
| kind | list type | | ||
| ------ | ----------------------- | | ||
| 10000 | Mute | | ||
| 10001 | Pin | | ||
| 30000 | Categorized People | | ||
| 30001 | Categorized Bookmarks | | ||
| 30002 | Categorized Relay Sets | | ||
## Encryption process pseudocode | ||
|
||
|
||
### Mute List | ||
|
||
An event with kind `10000` is defined as a replaceable list event for listing content a user wants to mute. Any standardized tag can be included in a Mute List. | ||
|
||
### Pin List | ||
|
||
An event with kind `10001` is defined as a replaceable list event for listing content a user wants to pin. Any standardized tag can be included in a Pin List. | ||
|
||
### Categorized People List | ||
|
||
An event with kind `30000` is defined as a parameterized replaceable list event for categorizing people. The 'd' parameter for this event holds the category name of the list. The tags included in these lists MUST follow the format of kind 3 events as defined in [NIP-02 - Contact List and Petnames](02.md). | ||
|
||
### Categorized Bookmarks List | ||
|
||
An event of kind `30001` is defined as a parameterized replaceable list event for categorizing bookmarks. The 'd' parameter for this event holds the category name of the list. The bookmark lists may contain metadata tags such as 'title', 'image', 'summary' as defined in [NIP-23 - Long-form Content](23.md). Any standardized tag can be included in a Categorized Bookmark List. | ||
|
||
### Categorized Relay Set | ||
|
||
An event of kind `30002` is defined as a parameterized replaceable list event for categorizing relays. The 'd' parameter for this event holds the category name of the list. The relays lists may contain metadata tags such as 'title', 'image', 'summary' as defined in [NIP-23 - Long-form Content](23.md). These sets can be used by clients in order to determine which relays to query in different scenarios. | ||
```scala | ||
val private_items = [ | ||
["p", "07caba282f76441955b695551c3c5c742e5b9202a3784780f8086fdcdc1da3a9"], | ||
["a", "a55c15f5e41d5aebd236eca5e0142789c5385703f1a7485aa4b38d94fd18dcc4"], | ||
] | ||
val base64blob = nip04.encrypt(json.encode_to_string(private_items)) | ||
event.content = base64blob | ||
``` |
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'd strongly prefer
title
,description
, andimage
here. (also might be good to mention that any other standard tag can be used, e.g.thumbnail
)