diff options
Diffstat (limited to '51.md')
| -rw-r--r-- | 51.md | 209 |
1 files changed, 102 insertions, 107 deletions
| @@ -4,145 +4,140 @@ NIP-51 | |||
| 4 | Lists | 4 | Lists |
| 5 | ----- | 5 | ----- |
| 6 | 6 | ||
| 7 | `draft` `optional` `author:fiatjaf` `author:arcbtc` `author:monlovesmango` `author:eskema` `author:gzuuus` | 7 | `draft` `optional` |
| 8 | 8 | ||
| 9 | 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. | 9 | This NIP defines lists of things that users can create. Lists can contain references to anything, and these references can be **public** or **private**. |
| 10 | 10 | ||
| 11 | 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". | 11 | 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`. |
| 12 | 12 | ||
| 13 | ## Replaceable List Event Example | 13 | When new items are added to an existing list, clients SHOULD append them to the end of the list, so they are stored in chronological order. |
| 14 | 14 | ||
| 15 | Lets say a user wants to create a 'Mute' list and has keys: | 15 | ## Types of lists |
| 16 | ``` | ||
| 17 | priv: fb505c65d4df950f5d28c9e4d285ee12ffaf315deef1fc24e3c7cd1e7e35f2b1 | ||
| 18 | pub: b1a5c93edcc8d586566fde53a20bdb50049a97b15483cb763854e57016e0fa3d | ||
| 19 | ``` | ||
| 20 | The user wants to publicly include these users: | ||
| 21 | 16 | ||
| 22 | ```json | 17 | ## Standard lists |
| 23 | ["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"], | ||
| 24 | ["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"] | ||
| 25 | ``` | ||
| 26 | and privately include these users (below is the JSON that would be encrypted and placed in the event content): | ||
| 27 | 18 | ||
| 28 | ```json | 19 | 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. |
| 29 | [ | ||
| 30 | ["p", "9ec7a778167afb1d30c4833de9322da0c08ba71a69e1911d5578d3144bb56437"], | ||
| 31 | ["p", "8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168"] | ||
| 32 | ] | ||
| 33 | ``` | ||
| 34 | 20 | ||
| 35 | Then the user would create a 'Mute' list event like below: | 21 | For example, _mute list_ can contain the public keys of spammers and bad actors users don't want to see in their feeds or receive annoying notifications from. |
| 36 | 22 | ||
| 37 | ```json | 23 | | name | kind | description | expected tag items | |
| 38 | { | 24 | | --- | --- | --- | --- | |
| 39 | "kind": 10000, | 25 | | Mute list | 10000 | things the user doesn't want to see in their feeds | `"p"` (pubkeys), `"t"` (hashtags), `"word"` (lowercase string), `"e"` (threads) | |
| 40 | "tags": [ | 26 | | Pinned notes | 10001 | events the user intends to showcase in their profile page | `"e"` (kind:1 notes) | |
| 41 | ["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"], | 27 | | Bookmarks | 10003 | uncategorized, "global" list of things a user wants to save | `"e"` (kind:1 notes), `"a"` (kind:30023 articles), `"t"` (hashtags), `"r"` (URLs) | |
| 42 | ["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"], | 28 | | Communities | 10004 | [NIP-72](72.md) communities the user belongs to | `"a"` (kind:34550 community definitions) | |
| 43 | ], | 29 | | Public chats | 10005 | [NIP-28](28.md) chat channels the user is in | `"e"` (kind:40 channel definitions) | |
| 44 | "content": "VezuSvWak++ASjFMRqBPWS3mK5pZ0vRLL325iuIL4S+r8n9z+DuMau5vMElz1tGC/UqCDmbzE2kwplafaFo/FnIZMdEj4pdxgptyBV1ifZpH3TEF6OMjEtqbYRRqnxgIXsuOSXaerWgpi0pm+raHQPseoELQI/SZ1cvtFqEUCXdXpa5AYaSd+quEuthAEw7V1jP+5TDRCEC8jiLosBVhCtaPpLcrm8HydMYJ2XB6Ixs=?iv=/rtV49RFm0XyFEwG62Eo9A==", | 30 | | Blocked relays | 10006 | relays clients should never connect to | `"relay"` (relay URLs) | |
| 45 | ...other fields | 31 | | Search relays | 10007 | relays clients should use when performing search queries | `"relay"` (relay URLs) | |
| 46 | } | 32 | | Simple groups | 10009 | [NIP-29](29.md) groups the user is in | `"group"` ([NIP-29](29.md) group ids + mandatory relay URL) | |
| 47 | ``` | 33 | | Interests | 10015 | topics a user may be interested in and pointers | `"t"` (hashtags) and `"a"` (kind:30015 interest set) | |
| 34 | | Emojis | 10030 | user preferred emojis and pointers to emoji sets | `"emoji"` (see [NIP-30](30.md)) and `"a"` (kind:30030 emoji set) | | ||
| 35 | | Good wiki authors | 10101 | [NIP-54](54.md) user recommended wiki authors | `"p"` (pubkeys) | | ||
| 36 | | Good wiki relays | 10102 | [NIP-54](54.md) relays deemed to only host useful articles | `"relay"` (relay URLs) | | ||
| 48 | 37 | ||
| 38 | ## Sets | ||
| 49 | 39 | ||
| 50 | ## Parameterized Replaceable List Event Example | 40 | 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. |
| 51 | 41 | ||
| 52 | Lets say a user wants to create a 'Categorized People' list of `nostr` people and has keys: | 42 | 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. |
| 53 | ``` | ||
| 54 | priv: fb505c65d4df950f5d28c9e4d285ee12ffaf315deef1fc24e3c7cd1e7e35f2b1 | ||
| 55 | pub: b1a5c93edcc8d586566fde53a20bdb50049a97b15483cb763854e57016e0fa3d | ||
| 56 | ``` | ||
| 57 | The user wants to publicly include these users: | ||
| 58 | 43 | ||
| 59 | ```json | 44 | Aside from their main identifier, the `"d"` tag, sets can optionally have a `"title"`, an `"image"` and a `"description"` tags that can be used to enhance their UI. |
| 60 | ["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"], | ||
| 61 | ["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"] | ||
| 62 | ``` | ||
| 63 | and privately include these users (below is the JSON that would be encrypted and placed in the event content): | ||
| 64 | 45 | ||
| 65 | ```json | 46 | | name | kind | description | expected tag items | |
| 66 | [ | 47 | | --- | --- | --- | --- | |
| 67 | ["p", "9ec7a778167afb1d30c4833de9322da0c08ba71a69e1911d5578d3144bb56437"], | 48 | | Follow sets | 30000 | categorized groups of users a client may choose to check out in different circumstances | `"p"` (pubkeys) | |
| 68 | ["p", "8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168"] | 49 | | Relay sets | 30002 | user-defined relay groups the user can easily pick and choose from during various operations | `"relay"` (relay URLs) | |
| 69 | ] | 50 | | Bookmark sets | 30003 | user-defined bookmarks categories , for when bookmarks must be in labeled separate groups | `"e"` (kind:1 notes), `"a"` (kind:30023 articles), `"t"` (hashtags), `"r"` (URLs) | |
| 70 | ``` | 51 | | 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) | |
| 52 | | Curation sets | 30005 | groups of videos picked by users as interesting and/or belonging to the same category | `"a"` (kind:34235 videos) | | ||
| 53 | | Interest sets | 30015 | interest topics represented by a bunch of "hashtags" | `"t"` (hashtags) | | ||
| 54 | | Emoji sets | 30030 | categorized emoji groups | `"emoji"` (see [NIP-30](30.md)) | | ||
| 55 | | Release artifact sets | 30063 | groups of files of a software release | `"e"` (kind:1063 [file metadata](94.md) events), `"i"` (application identifier, typically reverse domain notation), `"version"` | | ||
| 56 | |||
| 57 | ## Deprecated standard lists | ||
| 58 | |||
| 59 | Some clients have used these lists in the past, but they should work on transitioning to the [standard formats](#standard-lists) above. | ||
| 60 | |||
| 61 | | kind | "d" tag | use instead | | ||
| 62 | | --- | --- | --- | | ||
| 63 | | 30000 | `"mute"` | kind 10000 _mute list_ | | ||
| 64 | | 30001 | `"pin"` | kind 10001 _pin list_ | | ||
| 65 | | 30001 | `"bookmark"` | kind 10003 _bookmarks list_ | | ||
| 66 | | 30001 | `"communities"` | kind 10004 _communities list_ | | ||
| 71 | 67 | ||
| 72 | Then the user would create a 'Categorized People' list event like below: | 68 | ## Examples |
| 69 | |||
| 70 | ### A _mute list_ with some public items and some encrypted items | ||
| 73 | 71 | ||
| 74 | ```json | 72 | ```json |
| 75 | { | 73 | { |
| 76 | "kind": 30000, | 74 | "id": "a92a316b75e44cfdc19986c634049158d4206fcc0b7b9c7ccbcdabe28beebcd0", |
| 75 | "pubkey": "854043ae8f1f97430ca8c1f1a090bdde6488bd5115c7a45307a2a212750ae4cb", | ||
| 76 | "created_at": 1699597889, | ||
| 77 | "kind": 10000, | ||
| 77 | "tags": [ | 78 | "tags": [ |
| 78 | ["d", "nostr"], | 79 | ["p", "07caba282f76441955b695551c3c5c742e5b9202a3784780f8086fdcdc1da3a9"], |
| 79 | ["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"], | 80 | ["p", "a55c15f5e41d5aebd236eca5e0142789c5385703f1a7485aa4b38d94fd18dcc4"] |
| 80 | ["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"], | ||
| 81 | ], | 81 | ], |
| 82 | "content": "VezuSvWak++ASjFMRqBPWS3mK5pZ0vRLL325iuIL4S+r8n9z+DuMau5vMElz1tGC/UqCDmbzE2kwplafaFo/FnIZMdEj4pdxgptyBV1ifZpH3TEF6OMjEtqbYRRqnxgIXsuOSXaerWgpi0pm+raHQPseoELQI/SZ1cvtFqEUCXdXpa5AYaSd+quEuthAEw7V1jP+5TDRCEC8jiLosBVhCtaPpLcrm8HydMYJ2XB6Ixs=?iv=/rtV49RFm0XyFEwG62Eo9A==", | 82 | "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==", |
| 83 | ...other fields | 83 | "sig": "1173822c53261f8cffe7efbf43ba4a97a9198b3e402c2a1df130f42a8985a2d0d3430f4de350db184141e45ca844ab4e5364ea80f11d720e36357e1853dba6ca" |
| 84 | } | 84 | } |
| 85 | ``` | 85 | ``` |
| 86 | 86 | ||
| 87 | Lets say a user wants to create a 'Categorized Bookmarks' list of `bookmarks` and has keys: | 87 | ### A _curation set_ of articles and notes about yaks |
| 88 | ``` | ||
| 89 | priv: fb505c65d4df950f5d28c9e4d285ee12ffaf315deef1fc24e3c7cd1e7e35f2b1 | ||
| 90 | pub: b1a5c93edcc8d586566fde53a20bdb50049a97b15483cb763854e57016e0fa3d | ||
| 91 | ``` | ||
| 92 | The user wants to publicly include these bookmarks: | ||
| 93 | |||
| 94 | ```json | ||
| 95 | ["e", "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "wss://nostr.example.com"], | ||
| 96 | ["a", "30023:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", "wss://nostr.example.com"], | ||
| 97 | ["r", "https://github.com/nostr-protocol/nostr", "Nostr repository"], | ||
| 98 | ``` | ||
| 99 | and privately include these bookmarks (below is the JSON that would be encrypted and placed in the event content): | ||
| 100 | 88 | ||
| 101 | ```json | 89 | ```json |
| 102 | [ | 90 | { |
| 103 | ["r", "https://my-private.bookmark", "My private bookmark"], | 91 | "id": "567b41fc9060c758c4216fe5f8d3df7c57daad7ae757fa4606f0c39d4dd220ef", |
| 104 | ["a", "30001:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", "wss://nostr.example.com"], | 92 | "pubkey": "d6dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c", |
| 105 | ] | 93 | "created_at": 1695327657, |
| 94 | "kind": 30004, | ||
| 95 | "tags": [ | ||
| 96 | ["d", "jvdy9i4"], | ||
| 97 | ["name", "Yaks"], | ||
| 98 | ["picture", "https://cdn.britannica.com/40/188540-050-9AC748DE/Yak-Himalayas-Nepal.jpg"], | ||
| 99 | ["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."], | ||
| 100 | ["a", "30023:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:95ODQzw3ajNoZ8SyMDOzQ"], | ||
| 101 | ["a", "30023:54af95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:1-MYP8dAhramH9J5gJWKx"], | ||
| 102 | ["a", "30023:f8fe95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:D2Tbd38bGrFvU0bIbvSMt"], | ||
| 103 | ["e", "d78ba0d5dce22bfff9db0a9e996c9ef27e2c91051de0c4e1da340e0326b4941e"] | ||
| 104 | ], | ||
| 105 | "content": "", | ||
| 106 | "sig": "a9a4e2192eede77e6c9d24ddfab95ba3ff7c03fbd07ad011fff245abea431fb4d3787c2d04aad001cb039cb8de91d83ce30e9a94f82ac3c5a2372aa1294a96bd" | ||
| 107 | } | ||
| 106 | ``` | 108 | ``` |
| 107 | 109 | ||
| 108 | Then the user would create a 'Categorized Bookmarks' list event like below: | 110 | ### A _release artifact set_ of an Example App |
| 109 | 111 | ||
| 110 | ```json | 112 | ```json |
| 111 | { | 113 | { |
| 112 | "kind": 30001, | 114 | "id": "567b41fc9060c758c4216fe5f8d3df7c57daad7ae757fa4606f0c39d4dd220ef", |
| 115 | "pubkey": "d6dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c", | ||
| 116 | "created_at": 1695327657, | ||
| 117 | "kind": 30063, | ||
| 113 | "tags": [ | 118 | "tags": [ |
| 114 | ["d", "bookmarks"], | 119 | ["d", "ak8dy3v7"], |
| 115 | ["e", "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "wss://nostr.example.com"], | 120 | ["i", "com.example.app"], |
| 116 | ["a", "30023:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", "wss://nostr.example.com"], | 121 | ["version", "0.0.1"], |
| 117 | ["r", "https://github.com/nostr-protocol/nostr", "Nostr repository"], | 122 | ["title", "Example App"], |
| 123 | ["image", "http://cdn.site/p/com.example.app/icon.png"], | ||
| 124 | ["e", "d78ba0d5dce22bfff9db0a9e996c9ef27e2c91051de0c4e1da340e0326b4941e"], // Windows exe | ||
| 125 | ["e", "f27e2c91051de0c4e1da0d5dce22bfff9db0a9340e0326b4941ed78bae996c9e"], // MacOS dmg | ||
| 126 | ["e", "9d24ddfab95ba3ff7c03fbd07ad011fff245abea431fb4d3787c2d04aad02332"], // Linux AppImage | ||
| 127 | ["e", "340e0326b340e0326b4941ed78ba340e0326b4941ed78ba340e0326b49ed78ba"] // PWA | ||
| 118 | ], | 128 | ], |
| 119 | "content": "y3AyaLJfnmYr9x9Od9o4aYrmL9+Ynmsim5y2ONrU0urOTq+V81CyAthQ2mUOWE9xwGgrizhY7ILdQwWhy6FK0sA33GHtC0egUJw1zIdknPe7BZjznD570yk/8RXYgGyDKdexME+RMYykrnYFxq1+y/h00kmJg4u+Gpn+ZjmVhNYxl9b+TiBOAXG9UxnK/H0AmUqDpcldn6+j1/AiStwYZhD1UZ3jzDIk2qcCDy7MlGnYhSP+kNmG+2b0T/D1L0Z7?iv=PGJJfPE84gacAh7T0e6duQ==", | 129 | "content": "Example App is a decentralized marketplace for apps", |
| 120 | ...other fields | 130 | "sig": "a9a4e2192eede77e6c9d24ddfab95ba3ff7c03fbd07ad011fff245abea431fb4d3787c2d04aad001cb039cb8de91d83ce30e9a94f82ac3c5a2372aa1294a96bd" |
| 121 | } | 131 | } |
| 122 | ``` | 132 | ``` |
| 123 | 133 | ||
| 124 | ## List Event Kinds | 134 | ## Encryption process pseudocode |
| 125 | 135 | ||
| 126 | | kind | list type | | 136 | ```scala |
| 127 | | ------ | ----------------------- | | 137 | val private_items = [ |
| 128 | | 10000 | Mute | | 138 | ["p", "07caba282f76441955b695551c3c5c742e5b9202a3784780f8086fdcdc1da3a9"], |
| 129 | | 10001 | Pin | | 139 | ["a", "a55c15f5e41d5aebd236eca5e0142789c5385703f1a7485aa4b38d94fd18dcc4"], |
| 130 | | 30000 | Categorized People | | 140 | ] |
| 131 | | 30001 | Categorized Bookmarks | | 141 | val base64blob = nip04.encrypt(json.encode_to_string(private_items)) |
| 132 | 142 | event.content = base64blob | |
| 133 | 143 | ``` | |
| 134 | ### Mute List | ||
| 135 | |||
| 136 | 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. | ||
| 137 | |||
| 138 | ### Pin List | ||
| 139 | |||
| 140 | 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. | ||
| 141 | |||
| 142 | ### Categorized People List | ||
| 143 | |||
| 144 | 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). | ||
| 145 | |||
| 146 | ### Categorized Bookmarks List | ||
| 147 | |||
| 148 | 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. \ No newline at end of file | ||