Skip to content
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

Merged
merged 17 commits into from
Nov 19, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 83 additions & 115 deletions 51.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

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, and image here. (also might be good to mention that any other standard tag can be used, e.g. thumbnail)

`"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) |
Copy link
Contributor

Choose a reason for hiding this comment

The 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"

Copy link
Member Author

Choose a reason for hiding this comment

The 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) |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion (probably for another day)

| Filter sets | 30005 | a list of REQ filters defining custom feeds | `"req"` (a JSON-encoded REQ filter) |

Copy link

Choose a reason for hiding this comment

The 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 kind-30100. I think its better to include such specifications once for all because soon or later feeds customization will be faced (at least thats what we experienced).

content_settings
topics

{
  "id": "ed4291ff8ba4343ca444d03624afcee98257aba4af17410a73144ffdc369c467",
  "kind": 30100,
  "pubkey": "1cb0ebecfbe8e291d46c4e082df1e6a183bb321245ef29df5e2703f84b1d86e1",
  "created_at": 1700102857,
  "content": "",
  "tags": [
    [
      "d",
      "MyFavoriteTopicsInYakihonne"
    ],
    [
      "t",
      "YakiHonne"
    ],
    [
      "t",
      "Technology"
    ],
    [
      "t",
      "Crypto"
    ],
    [
      "t",
      "Travel"
    ],
    [
      "t",
      "Social"
    ],
    [
      "t",
      "Nostr"
    ],
    [
      "t",
      "Economy"
    ],
    [
      "t",
      "Finance"
    ],
    [
      "t",
      "Entrepreneurship"
    ],
    [
      "t",
      "Self improvement"
    ],
    [
      "t",
      "Food"
    ],
    [
      "t",
      "Marketing"
    ],
    [
      "t",
      "Art"
    ],
    [
      "t",
      "Photography"
    ],
    [
      "t",
      "Entertainment"
    ],
    [
      "t",
      "Music"
    ]
  ],
  "sig": "4fdc6a423bc7320b432e2edc1e794895468894b92ad7bb9e119bd16c1a6c1d0098bbd8a33ac25856acbae222fda565d7407c17d2b09f6b326de3762ad30a8a1c"
}

| Emoji sets | 30030 | categorized emoji groups | `"emoji"` (see [NIP-30](30.md)) |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion to add: Interest Set which is kind 30015, and takes a set of t tags.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pablof7z is it worth also mentioning Hightlight lists?

Copy link

Choose a reason for hiding this comment

The 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 kind-30100. I think its better to include such specifications once for all because soon or later feeds customization will be faced (at least thats what we experienced).

content_settings topics

{
  "id": "ed4291ff8ba4343ca444d03624afcee98257aba4af17410a73144ffdc369c467",
  "kind": 30100,
  "pubkey": "1cb0ebecfbe8e291d46c4e082df1e6a183bb321245ef29df5e2703f84b1d86e1",
  "created_at": 1700102857,
  "content": "",
  "tags": [
    [
      "d",
      "MyFavoriteTopicsInYakihonne"
    ],
    [
      "t",
      "YakiHonne"
    ],
    [
      "t",
      "Technology"
    ],
    [
      "t",
      "Crypto"
    ],
    [
      "t",
      "Travel"
    ],
    [
      "t",
      "Social"
    ],
    [
      "t",
      "Nostr"
    ],
    [
      "t",
      "Economy"
    ],
    [
      "t",
      "Finance"
    ],
    [
      "t",
      "Entrepreneurship"
    ],
    [
      "t",
      "Self improvement"
    ],
    [
      "t",
      "Food"
    ],
    [
      "t",
      "Marketing"
    ],
    [
      "t",
      "Art"
    ],
    [
      "t",
      "Photography"
    ],
    [
      "t",
      "Entertainment"
    ],
    [
      "t",
      "Music"
    ]
  ],
  "sig": "4fdc6a423bc7320b432e2edc1e794895468894b92ad7bb9e119bd16c1a6c1d0098bbd8a33ac25856acbae222fda565d7407c17d2b09f6b326de3762ad30a8a1c"
}

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Darecjo if your list is automatically named MyFavoriteTopicsInYakihonne I think that is an indication that you should be using NIP-78. What do you think?

@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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. @pablof7z added 30015 to NDK a while back and we've both been using it as a PRE. I think that there is a good reason to keep separate lists, e.g. I might have a music interests lists with music related stuff, and also an outdoors list that is all about climbing and skiing. I might want to use one or the other to view curated topic feeds.

Maybe that means there is a case to do both 10015 and 30015, in similar style to emoji lists. Where your 10015 list is the full canonical list and can include either t tags or a tags pointing to other 30015 lists.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Darecjo if your list is automatically named MyFavoriteTopicsInYakihonne I think that is an indication that you should be using NIP-78. What do you think?

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"],
]
```

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: Change this to ```json


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
```
Loading