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

NIP-22 - Generic Comment #1233

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
176 changes: 176 additions & 0 deletions 22.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
NIP-22
======

Comment
-------

`draft` `optional`

A comment is meant to be used to comment on
or reply to anything except event kinds
whose specifications have already defined other
ways to comment on or reply to them.

For example, it can be used to comment on a [NIP-23](23.md) `kind:30023` nostr blog post
or on an off-nostr news article or to reply to another comment.

## Structure

A comment is an event of `kind:1111` with plaintext `.content`
(no HTML, no Markdown nor other formatting).

**It must have a `k` tag** pointing to the kind of the subject being commented on.
Its value has a prefix: `"n:"` for nostr subjects and other prefixes (listed in a section ahead)
Copy link
Member

Choose a reason for hiding this comment

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

why prefix with n? Seems like it would be better to just use ints for kinds like we do elsewhere, and use prefixes for other stuff.

Copy link
Contributor Author

@arthurfranca arthurfranca Aug 15, 2024

Choose a reason for hiding this comment

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

Consistency, to make parser simpler. The first character always indicates what type of value will follow after the colon.

Too much weird stuff in this proposal, n:, K, o parsing, e:, a:, r. I like the general idea, but this is too much.

I'm using already existing concepts: prefixes on values like the a tag value, uppercase tag, and variadic fields similar to imeta tag (though not on the first tag value, which we need to be always the same to be searchable).

edit: The variadic fields make the tag extensible if the need arises.

for things that aren't nostr events.

For example, when commenting on a `kind:30023`, the `k` tag is set to "n:30023".
When replying to another `kind:1111`, the `k` tag is set to "n:1111".

The `k` tag is useful to fetch top-level comments about events of specific types, for example,
by filtering with `{ "#k": ["n:30023"], "kinds": [1111] }`.
Comment on lines +22 to +30
Copy link
Collaborator

Choose a reason for hiding this comment

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

I do not see the point of trying to get top level replies only, and we should just keep a tag that specifies the top level note's kind.

Could we not implement non-existence filters and check for the existence for r for this arguably nonexistent use case if we really need to?

Copy link
Contributor Author

@arthurfranca arthurfranca May 17, 2024

Choose a reason for hiding this comment

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

Unfortunatelly, non-existence filters are a dream that will never come true, according to previous 3 or so discussions about it on this repo.

On the lowercase k tag, check previous response. Can also see it being used to hook up on { "#k": ["#:t"] } filter to check for trending hashtags, cause the uppercase version would bring many more events. More on the hashtag use case for context.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Unfortunatelly, non-existence filters are a dream that will never come true, according to previous 3 or so discussions about it on this repo.

Which ones?

They can happen without much performance penalty.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

I honestly wouldn't dismiss it based off of those opinions. You don't actually need a tag exists/doesn't exist index, as 99.99% of tag nonexistence queries come attached with other indexable fields (kind, other tags, author).

I will make a PR soon.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe @mikedilger said the same thing at #683.

The caveat is relay may end up returning less events than the requested (even if there were more on DB) - of course considering client didn't request with a bizarre limit like {limit: 10000} to start with.

Clients would lose the ability of figuring out if there are more events to fetch depending on the number of returned events. Like, if client wants chunks of 5 events, it requests 6. If relay returns 6, client knows it should ask for more.


**An uppercase `K` tag must also be present.** The difference is that it is set to the original post (the one starting the thread) kind.
This way a client can request all comments related to the kind it supports, not only the top-level comments.

### Threads

**There is always an `o` tag** pointing to the original post that started the thread.
Its value can be an event id (with an `"e:"` prefix),
an event address (`"a:"` prefix) or others shown at below section. The second value
is the optional recommended relay url where the referenced event may be found.
This tag is useful to load all messages of a thread at once.

Note that if commenting on a replaceable event, one can choose to add both id and address references.

After the first value, the `o` tag may have a variable number of `<key><space-separator><value>` fields
at any order. When the tag references an event id, both a a [NIP-65](65.md) pubkey hint and a relay hint
are recommended fields while for an event address, just the relay hint.

Examples:

- `["o", "e:<event-id>", "p <event-pubkey, optional>", "r <relay-url, optional>"]`
- `["o", "a:<event-kind>:<event-pubkey>:<d-tag, optional>", "r <relay-url, optional>"]`

Also, **there is always an `r` tag** pointing to the subject being directly commented on or replied to, using the
same structure of the `o` tag.
This tag is useful to lazily load a thread.

### Other possible `o`/`r` tag values and their corresponding `K/k` tag values

| `o`/`r` tag | corresponding `K/k` tag |
| - | - |
| "r:`<url>`" | "r:`<domain>`" |
| "i:`<nip-73-id>`" | "i:`<nip-73-kind>`" |
| "t:`<topic>`" | "#:t" |
| "g:`<geohash>`" | "#:g" |

#### Thread Relays

If the original post being commented on is a nostr event and
the client supports the [NIP-65](65.md) relay usage spec,
`kind:1111` events should be sent atleast to the original post author's `read` relays.

### Event Examples

```js
{
kind: 1111,
content: 'Agree with your comment.',
tags: [
// referencing the OP
["o", "r:https://abc.com/articles/1"],
// replying to a parent kind:1111 comment
["r", "e:5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "p f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca", "r wss://example.relay"],
// the OP "kind"; for an url, the kind is its domain
["K", "r:https://abc.com"]
// the parent kind
["k", "n:1111"]
]
// other fields
}
```

```js
{
kind: 1111,
content: 'Great blog post! Check this out nostr:npub1sn0wdenkukak0d9dfczzeacvhkrgz92ak56egt7vdgzn8pv2wfqqhrjdv9.',
tags: [
// top-level comments have the same o and r tags
["o", "a:30023:3c9849383bdea883b0bd16fece1ed36d37e37cdde3ce43b17ea4e9192ec11289:f9347ca7", "r wss://example.relay"],
["r", "a:30023:3c9849383bdea883b0bd16fece1ed36d37e37cdde3ce43b17ea4e9192ec11289:f9347ca7", "r wss://example.relay"],
// the OP kind
["K", "n:30023"]
// the parent kind
["k", "n:30023"],
// the nostr:npub1... mentioned on .content
["p", "84dee6e676e5bb67b4ad4e042cf70cbd8681155db535942fcc6a0533858a7240"]
]
// other fields
}
```

Note that ideally there are no `p` tags unless the pubkey is being mentioned on the `.content`.

### Examples for Commenting on NIP-73 External Content

A podcast comment example:

```js
{
id: "80c48d992a38f9c445b943a9c9f1010b396676013443765750431a9004bdac05",
pubkey: "f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca",
kind: 1111,
content: "This was a great episode!",
tags: [
[
"o",
"i:podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f",
// note the "r" followed by a space then the url hint
"r https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"
],
// same value as "o" tag above, because it is a top-level comment (not a reply to a comment)
[
"r",
"i:podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f",
"r https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"
],

["K", "i:podcast:item:guid"], // uppercase K, this is the OP (original post)'s kind, that is, a podcast item
["k", "i:podcast:item:guid"] // lowercase k; in the case of a top-level comment, it is the same as the uppercase K
],
created_at: 1723748973
}
```

A reply to a podcast comment example:

```js
{
kind: 1111,
content: "I'm replying to the above comment.",
tags: [
[
"o",
"i:podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f",
"r https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"
],
// the "r" tag is a reference to the above comment
[
"r",
// "e:"<id-of-the-above-comment>
"e:80c48d992a38f9c445b943a9c9f1010b396676013443765750431a9004bdac05",
// "p"<space><pubkey-of-the-above-comment-author>
"p f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca",
// "r"<space><url-of-the-relay-where-above-comment-was-fetched-from>
"r wss://example.relay"
],

["K", "i:podcast:item:guid"], // uppercase K, this is the OP (original post)'s kind, that is, a podcast item
["k", "n:1111"] // lowercase k; this is the above comment's kind
]
// other fields
}
```

Note that for podcast items, the K or k tag is `i:podcast:item:guid`,
for books it is `i:isbn` while for movies it is `i:isan`.