upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPablo Fernandez <p@f7z.io>2023-06-08 22:32:17 +0200
committerGitHub <noreply@github.com>2023-06-08 22:32:17 +0200
commit6baacf6fb1badf3ba2480a88519e357b3cbdaaac (patch)
treeb3d211bc3683b340d678c3574519f327e7b3858e
parent964bc5b5ce946ab66aae945084549f26ffdef70f (diff)
parent14a887d43b654f41de5d271e27ef69048f183fb4 (diff)
Merge branch 'master' into nip31
-rw-r--r--01.md5
-rw-r--r--05.md6
-rw-r--r--07.md5
-rw-r--r--09.md2
-rw-r--r--11.md60
-rw-r--r--15.md4
-rw-r--r--16.md2
-rw-r--r--23.md2
-rw-r--r--28.md2
-rw-r--r--30.md56
-rw-r--r--33.md4
-rw-r--r--45.md8
-rw-r--r--47.md20
-rw-r--r--57.md44
-rw-r--r--89.md116
-rw-r--r--README.md8
16 files changed, 271 insertions, 73 deletions
diff --git a/01.md b/01.md
index a07a0df..33a10e7 100644
--- a/01.md
+++ b/01.md
@@ -99,7 +99,7 @@ This NIP defines no rules for how `NOTICE` messages should be sent or treated.
99## Basic Event Kinds 99## Basic Event Kinds
100 100
101 - `0`: `set_metadata`: the `content` is set to a stringified JSON object `{name: <username>, about: <string>, picture: <url, string>}` describing the user who created the event. A relay may delete past `set_metadata` events once it gets a new one for the same pubkey. 101 - `0`: `set_metadata`: the `content` is set to a stringified JSON object `{name: <username>, about: <string>, picture: <url, string>}` describing the user who created the event. A relay may delete past `set_metadata` events once it gets a new one for the same pubkey.
102 - `1`: `text_note`: the `content` is set to the plaintext content of a note (anything the user wants to say). Markdown links (`[]()` stuff) are not plaintext. 102 - `1`: `text_note`: the `content` is set to the plaintext content of a note (anything the user wants to say). Do not use Markdown! Clients should not have to guess how to interpret content like `[]()`. Use different event kinds for parsable content.
103 - `2`: `recommend_server`: the `content` is set to the URL (e.g., `wss://somerelay.com`) of a relay the event creator wants to recommend to its followers. 103 - `2`: `recommend_server`: the `content` is set to the URL (e.g., `wss://somerelay.com`) of a relay the event creator wants to recommend to its followers.
104 104
105A relay may choose to treat different message kinds differently, and it may or may not choose to have a default way to handle kinds it doesn't know about. 105A relay may choose to treat different message kinds differently, and it may or may not choose to have a default way to handle kinds it doesn't know about.
@@ -107,5 +107,6 @@ A relay may choose to treat different message kinds differently, and it may or m
107## Other Notes: 107## Other Notes:
108 108
109- Clients should not open more than one websocket to each relay. One channel can support an unlimited number of subscriptions, so clients should do that. 109- Clients should not open more than one websocket to each relay. One channel can support an unlimited number of subscriptions, so clients should do that.
110- The `tags` array can store a tag identifier as the first element of each subarray, plus arbitrary information afterward (always as strings). This NIP defines `"p"` — meaning "pubkey", which points to a pubkey of someone that is referred to in the event —, and `"e"` — meaning "event", which points to the id of an event this event is quoting, replying to or referring to somehow. See [NIP-10](https://github.com/nostr-protocol/nips/blob/127d5518bfa9a4e4e7510490c0b8d95e342dfa4b/10.md) for a detailed description of "e" and "p" tags. 110- The `tags` array can store a tag identifier as the first element of each subarray, plus arbitrary information afterward (always as strings). This NIP defines `"p"` — meaning "pubkey", which points to a pubkey of someone that is referred to in the event —, and `"e"` — meaning "event", which points to the id of an event this event is quoting, replying to or referring to somehow. See [NIP-10](10.md) for a detailed description of "e" and "p" tags.
111- The `<recommended relay URL>` item present on the `"e"` and `"p"` tags is an optional (could be set to `""`) URL of a relay the client could attempt to connect to fetch the tagged event or other events from a tagged profile. It MAY be ignored, but it exists to increase censorship resistance and make the spread of relay addresses more seamless across clients. 111- The `<recommended relay URL>` item present on the `"e"` and `"p"` tags is an optional (could be set to `""`) URL of a relay the client could attempt to connect to fetch the tagged event or other events from a tagged profile. It MAY be ignored, but it exists to increase censorship resistance and make the spread of relay addresses more seamless across clients.
112- Clients should use the created_at field to judge the age of a metadata event and completely replace older metadata events with newer metadata events regardless of the order in which they arrive. Clients should not merge any filled fields within older metadata events into empty fields of newer metadata events.
diff --git a/05.md b/05.md
index a7b42b0..56b9156 100644
--- a/05.md
+++ b/05.md
@@ -6,7 +6,7 @@ Mapping Nostr keys to DNS-based internet identifiers
6 6
7`final` `optional` `author:fiatjaf` `author:mikedilger` 7`final` `optional` `author:fiatjaf` `author:mikedilger`
8 8
9On events of kind `0` (`set_metadata`) one can specify the key `"nip05"` with an [internet identifier](https://datatracker.ietf.org/doc/html/rfc5322#section-3.4.1) (an email-like address) as the value. Although there is a link to a very liberal "internet identifier" specification above, NIP-05 assumes the `<local-part>` part will be restricted to the characters `a-z0-9-_.`, case insensitive. 9On events of kind `0` (`set_metadata`) one can specify the key `"nip05"` with an [internet identifier](https://datatracker.ietf.org/doc/html/rfc5322#section-3.4.1) (an email-like address) as the value. Although there is a link to a very liberal "internet identifier" specification above, NIP-05 assumes the `<local-part>` part will be restricted to the characters `a-z0-9-_.`, case-insensitive.
10 10
11Upon seeing that, the client splits the identifier into `<local-part>` and `<domain>` and use these values to make a GET request to `https://<domain>/.well-known/nostr.json?name=<local-part>`. 11Upon seeing that, the client splits the identifier into `<local-part>` and `<domain>` and use these values to make a GET request to `https://<domain>/.well-known/nostr.json?name=<local-part>`.
12 12
@@ -50,7 +50,7 @@ or with the **optional** `"relays"` attribute:
50 50
51If the pubkey matches the one given in `"names"` (as in the example above) that means the association is right and the `"nip05"` identifier is valid and can be displayed. 51If the pubkey matches the one given in `"names"` (as in the example above) that means the association is right and the `"nip05"` identifier is valid and can be displayed.
52 52
53The optional `"relays"` attribute may contain an object with public keys as properties and arrays of relay URLs as values. When present, that can be used to help clients learn in which relays that user may be found. Web servers which serve `/.well-known/nostr.json` files dynamically based on the query string SHOULD also serve the relays data for any name they serve in the same reply when that is available. 53The optional `"relays"` attribute may contain an object with public keys as properties and arrays of relay URLs as values. When present, that can be used to help clients learn in which relays the specific user may be found. Web servers which serve `/.well-known/nostr.json` files dynamically based on the query string SHOULD also serve the relays data for any name they serve in the same reply when that is available.
54 54
55## Finding users from their NIP-05 identifier 55## Finding users from their NIP-05 identifier
56 56
@@ -76,7 +76,7 @@ Clients may treat the identifier `_@domain` as the "root" identifier, and choose
76 76
77### Reasoning for the `/.well-known/nostr.json?name=<local-part>` format 77### Reasoning for the `/.well-known/nostr.json?name=<local-part>` format
78 78
79By adding the `<local-part>` as a query string instead of as part of the path the protocol can support both dynamic servers that can generate JSON on-demand and static servers with a JSON file in it that may contain multiple names. 79By adding the `<local-part>` as a query string instead of as part of the path, the protocol can support both dynamic servers that can generate JSON on-demand and static servers with a JSON file in it that may contain multiple names.
80 80
81### Allowing access from JavaScript apps 81### Allowing access from JavaScript apps
82 82
diff --git a/07.md b/07.md
index 3b7a1d2..ee4e372 100644
--- a/07.md
+++ b/07.md
@@ -26,7 +26,10 @@ async window.nostr.nip04.decrypt(pubkey, ciphertext): string // takes ciphertext
26 26
27- [horse](https://github.com/fiatjaf/horse) (Chrome and derivatives) 27- [horse](https://github.com/fiatjaf/horse) (Chrome and derivatives)
28- [nos2x](https://github.com/fiatjaf/nos2x) (Chrome and derivatives) 28- [nos2x](https://github.com/fiatjaf/nos2x) (Chrome and derivatives)
29- [Alby](https://getalby.com) (Chrome and derivatives, Firefox, Safari) 29- [Alby](https://getalby.com) (Chrome and derivatives, Firefox)
30- [Blockcore](https://www.blockcore.net/wallet) (Chrome and derivatives) 30- [Blockcore](https://www.blockcore.net/wallet) (Chrome and derivatives)
31- [nos2x-fox](https://diegogurpegui.com/nos2x-fox/) (Firefox) 31- [nos2x-fox](https://diegogurpegui.com/nos2x-fox/) (Firefox)
32- [Flamingo](https://www.getflamingo.org/) (Chrome and derivatives) 32- [Flamingo](https://www.getflamingo.org/) (Chrome and derivatives)
33- [AKA Profiles](https://github.com/neilck/aka-extension) (Chrome, stores multiple keys)
34- [TokenPocket](https://www.tokenpocket.pro/) (Android, IOS, Chrome and derivatives)
35- [Nostrmo](https://github.com/haorendashu/nostrmo_faq#download) (Android, IOS)
diff --git a/09.md b/09.md
index 89781fb..a73e0ab 100644
--- a/09.md
+++ b/09.md
@@ -20,7 +20,7 @@ For example:
20 "pubkey": <32-bytes hex-encoded public key of the event creator>, 20 "pubkey": <32-bytes hex-encoded public key of the event creator>,
21 "tags": [ 21 "tags": [
22 ["e", "dcd59..464a2"], 22 ["e", "dcd59..464a2"],
23 ["e", "968c5..ad7a4"], 23 ["e", "968c5..ad7a4"]
24 ], 24 ],
25 "content": "these posts were published by accident", 25 "content": "these posts were published by accident",
26 ...other fields 26 ...other fields
diff --git a/11.md b/11.md
index 8951835..b0d6003 100644
--- a/11.md
+++ b/11.md
@@ -69,18 +69,18 @@ are rejected or fail immediately.
69```json 69```json
70{ 70{
71... 71...
72 limitation: { 72 "limitation": {
73 max_message_length: 16384, 73 "max_message_length": 16384,
74 max_subscriptions: 20, 74 "max_subscriptions": 20,
75 max_filters: 100, 75 "max_filters": 100,
76 max_limit: 5000, 76 "max_limit": 5000,
77 max_subid_length: 100, 77 "max_subid_length": 100,
78 min_prefix: 4, 78 "min_prefix": 4,
79 max_event_tags: 100, 79 "max_event_tags": 100,
80 max_content_length: 8196, 80 "max_content_length": 8196,
81 min_pow_difficulty: 30, 81 "min_pow_difficulty": 30,
82 auth_required: true, 82 "auth_required": true,
83 payment_required: true, 83 "payment_required": true,
84 } 84 }
85... 85...
86} 86}
@@ -141,11 +141,11 @@ all, and preferably an error will be provided when those are received.
141```json 141```json
142{ 142{
143... 143...
144 retention: [ 144 "retention": [
145 { kinds: [0, 1, [5, 7], [40, 49]], time: 3600 }, 145 { "kinds": [0, 1, [5, 7], [40, 49]], "time": 3600 },
146 { kinds: [[40000, 49999], time: 100 }, 146 { "kinds": [[40000, 49999]], "time": 100 },
147 { kinds: [[30000, 39999], count: 1000 }, 147 { "kinds": [[30000, 39999]], "count": 1000 },
148 { time: 3600, count: 10000 } 148 { "time": 3600, "count": 10000 }
149 ] 149 ]
150... 150...
151} 151}
@@ -154,7 +154,7 @@ all, and preferably an error will be provided when those are received.
154`retention` is a list of specifications: each will apply to either all kinds, or 154`retention` is a list of specifications: each will apply to either all kinds, or
155a subset of kinds. Ranges may be specified for the kind field as a tuple of inclusive 155a subset of kinds. Ranges may be specified for the kind field as a tuple of inclusive
156start and end values. Events of indicated kind (or all) are then limited to a `count` 156start and end values. Events of indicated kind (or all) are then limited to a `count`
157and or time period. 157and/or time period.
158 158
159It is possible to effectively blacklist Nostr-based protocols that rely on 159It is possible to effectively blacklist Nostr-based protocols that rely on
160a specific `kind` number, by giving a retention time of zero for those `kind` values. 160a specific `kind` number, by giving a retention time of zero for those `kind` values.
@@ -175,8 +175,8 @@ It is not possible to describe the limitations of each country's laws
175and policies which themselves are typically vague and constantly shifting. 175and policies which themselves are typically vague and constantly shifting.
176 176
177Therefore, this field allows the relay operator to indicate which 177Therefore, this field allows the relay operator to indicate which
178country's' laws might end up being enforced on them, and then 178countries' laws might end up being enforced on them, and then
179indirectly on their users's content. 179indirectly on their users' content.
180 180
181Users should be able to avoid relays in countries they don't like, 181Users should be able to avoid relays in countries they don't like,
182and/or select relays in more favourable zones. Exposing this 182and/or select relays in more favourable zones. Exposing this
@@ -185,7 +185,7 @@ flexibility is up to the client software.
185```json 185```json
186{ 186{
187... 187...
188 relay_countries: [ 'CA', 'US' ], 188 "relay_countries": [ "CA", "US" ],
189... 189...
190} 190}
191``` 191```
@@ -208,9 +208,9 @@ To support this goal, relays MAY specify some of the following values.
208```json 208```json
209{ 209{
210... 210...
211 language_tags: [ 'en', 'en-419' ], 211 "language_tags": [ "en", "en-419" ],
212 tags: [ 'sfw-only', 'bitcoin-only', 'anime' ], 212 "tags": [ "sfw-only", "bitcoin-only", "anime" ],
213 posting_policy: 'https://example.com/posting-policy.html', 213 "posting_policy": "https://example.com/posting-policy.html",
214... 214...
215} 215}
216``` 216```
@@ -220,7 +220,7 @@ To support this goal, relays MAY specify some of the following values.
220 the major languages spoken on the relay. 220 the major languages spoken on the relay.
221 221
222- `tags` is a list of limitations on the topics to be discussed. 222- `tags` is a list of limitations on the topics to be discussed.
223 For example `sfw-only` indicates hat only "Safe For Work" content 223 For example `sfw-only` indicates that only "Safe For Work" content
224 is encouraged on this relay. This relies on assumptions of what the 224 is encouraged on this relay. This relies on assumptions of what the
225 "work" "community" feels "safe" talking about. In time, a common 225 "work" "community" feels "safe" talking about. In time, a common
226 set of tags may emerge that allow users to find relays that suit 226 set of tags may emerge that allow users to find relays that suit
@@ -245,11 +245,11 @@ Relays that require payments may want to expose their fee schedules.
245```json 245```json
246{ 246{
247... 247...
248 payments_url: "https://my-relay/payments", 248 "payments_url": "https://my-relay/payments",
249 fees: { 249 "fees": {
250 "admission": [{ amount: 1000000, unit: 'msats' }], 250 "admission": [{ "amount": 1000000, "unit": "msats" }],
251 "subscription": [{ amount: 5000000, unit: 'msats', period: 2592000 }], 251 "subscription": [{ "amount": 5000000, "unit": "msats", "period": 2592000 }],
252 "publication": [{ kinds: [4], amount: 100, unit: 'msats' }], 252 "publication": [{ "kinds": [4], "amount": 100, "unit": "msats" }],
253 }, 253 },
254... 254...
255} 255}
diff --git a/15.md b/15.md
index 617c011..f8df328 100644
--- a/15.md
+++ b/15.md
@@ -38,8 +38,8 @@ A merchant can publish these events:
38| `0 ` | `set_meta` | The merchant description (similar with any `nostr` public key). | [NIP01 ](https://github.com/nostr-protocol/nips/blob/master/01.md) | 38| `0 ` | `set_meta` | The merchant description (similar with any `nostr` public key). | [NIP01 ](https://github.com/nostr-protocol/nips/blob/master/01.md) |
39| `30017` | `set_stall` | Create or update a stall. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) | 39| `30017` | `set_stall` | Create or update a stall. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) |
40| `30018` | `set_product` | Create or update a product. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) | 40| `30018` | `set_product` | Create or update a product. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) |
41| `4 ` | `direct_message` | Communicate with the customer. The messages can be plain-text or JSON. | [NIP09](https://github.com/nostr-protocol/nips/blob/master/09.md) | 41| `4 ` | `direct_message` | Communicate with the customer. The messages can be plain-text or JSON. | [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md) |
42| `5 ` | `delete` | Delete a product or a stall. | [NIP05](https://github.com/nostr-protocol/nips/blob/master/05.md) | 42| `5 ` | `delete` | Delete a product or a stall. | [NIP09](https://github.com/nostr-protocol/nips/blob/master/09.md) |
43 43
44### Event `30017`: Create or update a stall. 44### Event `30017`: Create or update a stall.
45 45
diff --git a/16.md b/16.md
index 4d9481d..8ef4af4 100644
--- a/16.md
+++ b/16.md
@@ -20,6 +20,8 @@ Upon a replaceable event with a newer timestamp than the currently known latest
20effectively replacing what gets returned when querying for 20effectively replacing what gets returned when querying for
21`author:kind` tuples. 21`author:kind` tuples.
22 22
23If two events have the same timestamp, the event with the lowest id (first in lexical order) SHOULD be retained, and the other discarded.
24
23Ephemeral Events 25Ephemeral Events
24---------------- 26----------------
25An *ephemeral event* is defined as an event with a kind `20000 <= n < 30000`. 27An *ephemeral event* is defined as an event with a kind `20000 <= n < 30000`.
diff --git a/23.md b/23.md
index 151a31b..4291228 100644
--- a/23.md
+++ b/23.md
@@ -6,7 +6,7 @@ Long-form Content
6 6
7`draft` `optional` `author:fiatjaf` 7`draft` `optional` `author:fiatjaf`
8 8
9This NIP defines `kind:30023` (a parameterized replaceable event according to [NIP-33](33.md)) for long-form text content, generally referred to as "articles" or "blog posts". 9This NIP defines `kind:30023` (a parameterized replaceable event according to [NIP-33](33.md)) for long-form text content, generally referred to as "articles" or "blog posts". `kind:30024` has the same structure as `kind:30023` and is used to save long form drafts.
10 10
11"Social" clients that deal primarily with `kind:1` notes should not be expected to implement this NIP. 11"Social" clients that deal primarily with `kind:1` notes should not be expected to implement this NIP.
12 12
diff --git a/28.md b/28.md
index 169ae4f..62ab398 100644
--- a/28.md
+++ b/28.md
@@ -37,7 +37,7 @@ In the channel creation `content` field, Client SHOULD include basic channel met
37 37
38Update a channel's public metadata. 38Update a channel's public metadata.
39 39
40Clients and relays SHOULD handle kind 41 events similar to kind 0 `metadata` events. 40Clients and relays SHOULD handle kind 41 events similar to kind 33 replaceable events, where the information is used to update the metadata, without modifying the event id for the channel. Only the most recent kind 41 is needed to be stored.
41 41
42Clients SHOULD ignore kind 41s from pubkeys other than the kind 40 pubkey. 42Clients SHOULD ignore kind 41s from pubkeys other than the kind 40 pubkey.
43 43
diff --git a/30.md b/30.md
new file mode 100644
index 0000000..ffc5aeb
--- /dev/null
+++ b/30.md
@@ -0,0 +1,56 @@
1NIP-30
2======
3
4Custom Emoji
5------------
6
7`draft` `optional` `author:alexgleason`
8
9Custom emoji may be added to **kind 0** and **kind 1** events by including one or more `"emoji"` tags, in the form:
10
11```
12["emoji", <shortcode>, <image-url>]
13```
14
15Where:
16
17- `<shortcode>` is a name given for the emoji, which MUST be comprised of only alphanumeric characters and underscores.
18- `<image-url>` is a URL to the corresponding image file of the emoji.
19
20For each emoji tag, clients should parse emoji shortcodes (aka "emojify") like `:shortcode:` in the event to display custom emoji.
21
22Clients may allow users to add custom emoji to an event by including `:shortcode:` identifier in the event, and adding the relevant `"emoji"` tags.
23
24### Kind 0 events
25
26In kind 0 events, the `name` and `about` fields should be emojified.
27
28```json
29{
30 "kind": 0,
31 "content": "{\"name\":\"Alex Gleason :soapbox:\"}",
32 "tags": [
33 ["emoji", "soapbox", "https://gleasonator.com/emoji/Gleasonator/soapbox.png"]
34 ],
35 "pubkey": "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6",
36 "created_at": 1682790000
37}
38```
39
40### Kind 1 events
41
42In kind 1 events, the `content` should be emojified.
43
44```json
45{
46 "kind": 1,
47 "content": "Hello :gleasonator: 😂 :ablobcatrainbow: :disputed: yolo",
48 "tags": [
49 ["emoji", "ablobcatrainbow", "https://gleasonator.com/emoji/blobcat/ablobcatrainbow.png"],
50 ["emoji", "disputed", "https://gleasonator.com/emoji/Fun/disputed.png"],
51 ["emoji", "gleasonator", "https://gleasonator.com/emoji/Gleasonator/gleasonator.png"]
52 ],
53 "pubkey": "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6",
54 "created_at": 1682630000
55}
56```
diff --git a/33.md b/33.md
index 10681fa..5128bec 100644
--- a/33.md
+++ b/33.md
@@ -10,7 +10,7 @@ This NIP adds a new event range that allows for replacement of events that have
10 10
11Implementation 11Implementation
12-------------- 12--------------
13The value of a tag is defined as the first parameter of a tag after the tag name. 13The value of a tag can be any string and is defined as the first parameter of a tag after the tag name.
14 14
15A *parameterized replaceable event* is defined as an event with a kind `30000 <= n < 40000`. 15A *parameterized replaceable event* is defined as an event with a kind `30000 <= n < 40000`.
16Upon a parameterized replaceable event with a newer timestamp than the currently known latest 16Upon a parameterized replaceable event with a newer timestamp than the currently known latest
@@ -18,6 +18,8 @@ replaceable event with the same kind, author and first `d` tag value being recei
18SHOULD be discarded, effectively replacing what gets returned when querying for 18SHOULD be discarded, effectively replacing what gets returned when querying for
19`author:kind:d-tag` tuples. 19`author:kind:d-tag` tuples.
20 20
21If two events have the same timestamp, the event with the lowest id (first in lexical order) SHOULD be retained, and the other discarded.
22
21A missing or a `d` tag with no value should be interpreted equivalent to a `d` tag with the 23A missing or a `d` tag with no value should be interpreted equivalent to a `d` tag with the
22value as an empty string. Events from the same author with any of the following `tags` 24value as an empty string. Events from the same author with any of the following `tags`
23replace each other: 25replace each other:
diff --git a/45.md b/45.md
index 87e8000..a525391 100644
--- a/45.md
+++ b/45.md
@@ -6,21 +6,21 @@ Event Counts
6 6
7`draft` `optional` `author:staab` 7`draft` `optional` `author:staab`
8 8
9Relays may support the `COUNT` verb, which provides a mechanism for obtaining event counts. 9Relays may support the verb `COUNT`, which provides a mechanism for obtaining event counts.
10 10
11## Motivation 11## Motivation
12 12
13Some queries a client may want to execute against connected relays are prohibitively expensive, for example, in order to retrieve follower counts for a given pubkey, a client must query all kind-3 events referring to a given pubkey and count them. The result may be cached, either by a client or by a separate indexing server as an alternative, but both options erode the decentralization of the network by creating a second-layer protocol on top of Nostr. 13Some queries a client may want to execute against connected relays are prohibitively expensive, for example, in order to retrieve follower counts for a given pubkey, a client must query all kind-3 events referring to a given pubkey only to count them. The result may be cached, either by a client or by a separate indexing server as an alternative, but both options erode the decentralization of the network by creating a second-layer protocol on top of Nostr.
14 14
15## Filters and return values 15## Filters and return values
16 16
17This NIP defines a verb called `COUNT`, which accepts a subscription id and filters as specified in [NIP 01](01.md). 17This NIP defines the verb `COUNT`, which accepts a subscription id and filters as specified in [NIP 01](01.md) for the verb `REQ`. Multiple filters are OR'd together and aggregated into a single count result.
18 18
19``` 19```
20["COUNT", <subscription_id>, <filters JSON>...] 20["COUNT", <subscription_id>, <filters JSON>...]
21``` 21```
22 22
23Counts are returned using a `COUNT` response in the form `{count: <integer>}`. Relays may use probabilistic counts to reduce compute requirements. 23Counts are returned using a `COUNT` response in the form `{"count": <integer>}`. Relays may use probabilistic counts to reduce compute requirements.
24 24
25``` 25```
26["COUNT", <subscription_id>, {"count": <integer>}] 26["COUNT", <subscription_id>, {"count": <integer>}]
diff --git a/47.md b/47.md
index aa58b5c..c884b97 100644
--- a/47.md
+++ b/47.md
@@ -12,8 +12,18 @@ This NIP describes a way for clients to access a remote Lightning wallet through
12 12
13## Terms 13## Terms
14 14
15* **client**: Nostr app on any platform that wants to pay Lightning invoices 15* **client**: Nostr app on any platform that wants to pay Lightning invoices.
16* **wallet service**: Nostr app that typically runs on an always-on computer (eg. in the cloud or on a Raspberry Pi). 16* **user**: The person using the **client**, and want's to connect their wallet app to their **client**.
17* **wallet service**: Nostr app that typically runs on an always-on computer (eg. in the cloud or on a Raspberry Pi). This app has access to the APIs of the wallets it serves.
18
19## Theory of Operation
20 1. **Users** who which to use this NIP to send lightning payments to other nostr users must first acquire a special "connection" URI from their NIP-47 compliant wallet application. The wallet application may provide this URI using a QR screen, or a pasteable string, or some other means.
21
22 2. The **user** should then copy this URI into their **client(s)** by pasting, or scanning the QR, etc. The **client(s)** should save this URI and use it later whenever the **user** makes a payment. The **client** should then request an `info` (13194) event from the relay(s) specified in the URI. The **wallet service** will have sent that event to those relays earlier, and the relays will hold it as a replaceable event.
23
24 3. When the **user** initiates a payment their nostr **client** create a `pay_invoice` request, encrypts it using a token from the URI, and sends it (kind 23194) to the relay(s) specified in the connection URI. The **wallet service** will be listening on those relays and will decrypt the request and then contact the **user's** wallet application to send the payment. The **wallet service** will know how to talk to the wallet application because the connection URI specified relay(s) that have access to the wallet app API.
25
26 4. Once the payment is complete the **wallet service** will send an encrypted `response` (kind 23195) to the **user** over the relay(s) in the URI.
17 27
18## Events 28## Events
19 29
@@ -24,7 +34,8 @@ There are three event kinds:
24 34
25The info event should be a replaceable event that is published by the **wallet service** on the relay to indicate which commands it supports. The content should be 35The info event should be a replaceable event that is published by the **wallet service** on the relay to indicate which commands it supports. The content should be
26a plaintext string with the supported commands, space-seperated, eg. `pay_invoice get_balance`. Only the `pay_invoice` command is described in this NIP, but other commands might be defined in different NIPs. 36a plaintext string with the supported commands, space-seperated, eg. `pay_invoice get_balance`. Only the `pay_invoice` command is described in this NIP, but other commands might be defined in different NIPs.
27Both the request and response events SHOULD contain one `p` tag, containing the public key of the **wallet service** if this is a request, and the public key of the **client** if this is a response. The response event SHOULD contain an `e` tag with the id of the request event it is responding to. 37
38Both the request and response events SHOULD contain one `p` tag, containing the public key of the **wallet service** if this is a request, and the public key of the **user** if this is a response. The response event SHOULD contain an `e` tag with the id of the request event it is responding to.
28 39
29The content of requests and responses is encrypted with [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md), and is a JSON-RPCish object with a semi-fixed structure: 40The content of requests and responses is encrypted with [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md), and is a JSON-RPCish object with a semi-fixed structure:
30 41
@@ -77,6 +88,7 @@ The **wallet service** generates this connection URI with protocol `nostr+wallet
77 - The user can have different keys for different applications. Keys can be revoked and created at will and have arbitrary constraints (eg. budgets). 88 - The user can have different keys for different applications. Keys can be revoked and created at will and have arbitrary constraints (eg. budgets).
78 - The key is harder to leak since it is not shown to the user and backed up. 89 - The key is harder to leak since it is not shown to the user and backed up.
79 - It improves privacy because the user's main key would not be linked to their payments. 90 - It improves privacy because the user's main key would not be linked to their payments.
91- `lud16` Recommended. A lightning address that clients can use to automatically setup the `lud16` field on the user's profile if they have none configured.
80 92
81The **client** should then store this connection and use it when the user wants to perform actions like paying an invoice. Due to this NIP using ephemeral events, it is recommended to pick relays that do not close connections on inactivity to not drop events. 93The **client** should then store this connection and use it when the user wants to perform actions like paying an invoice. Due to this NIP using ephemeral events, it is recommended to pick relays that do not close connections on inactivity to not drop events.
82 94
@@ -117,7 +129,7 @@ Errors:
117## Example pay invoice flow 129## Example pay invoice flow
118 130
1190. The user scans the QR code generated by the **wallet service** with their **client** application, they follow a `nostr+walletconnect:` deeplink or configure the connection details manually. 1310. The user scans the QR code generated by the **wallet service** with their **client** application, they follow a `nostr+walletconnect:` deeplink or configure the connection details manually.
1201. **client** sends an event to with **wallet service** service with kind `23194`. The content is a `pay_invoice` request. The private key is the secret from the connection string above. 1321. **client** sends an event to the **wallet service** service with kind `23194`. The content is a `pay_invoice` request. The private key is the secret from the connection string above.
1212. **wallet service** verifies that the author's key is authorized to perform the payment, decrypts the payload and sends the payment. 1332. **wallet service** verifies that the author's key is authorized to perform the payment, decrypts the payload and sends the payment.
1223. **wallet service** responds to the event by sending an event with kind `23195` and content being a response either containing an error message or a preimage. 1343. **wallet service** responds to the event by sending an event with kind `23195` and content being a response either containing an error message or a preimage.
123 135
diff --git a/57.md b/57.md
index 17042ea..438a0f8 100644
--- a/57.md
+++ b/57.md
@@ -6,21 +6,21 @@ Lightning Zaps
6 6
7`draft` `optional` `author:jb55` `author:kieran` 7`draft` `optional` `author:jb55` `author:kieran`
8 8
9This NIP defines two new event types for recording lightning payments between users. `9734` is a `zap request`, representing a payer's request to a recipient's lightning wallet for an invoice. `9735` is a `zap receipt`, representing the confirmation by the recipient's lightning wallet that the invoice issued in response to a zap request has been paid. 9This NIP defines two new event types for recording lightning payments between users. `9734` is a `zap request`, representing a payer's request to a recipient's lightning wallet for an invoice. `9735` is a `zap receipt`, representing the confirmation by the recipient's lightning wallet that the invoice issued in response to a `zap request` has been paid.
10 10
11Having lightning receipts on nostr allows clients to display lightning payments from entities on the network. These can be used for fun or for spam deterrence. 11Having lightning receipts on nostr allows clients to display lightning payments from entities on the network. These can be used for fun or for spam deterrence.
12 12
13## Protocol flow 13## Protocol flow
14 14
151. Client calculates a recipient's lnurl pay request url from the `zap` tag on the event being zapped (see Appendix G), or by decoding their lud06 or lud16 field on their profile according to the [lnurl specifications](https://github.com/lnurl/luds). The client MUST send a GET request to this url and parse the response. If `allowsNostr` exists and it is `true`, and if `nostrPubkey` exists and is a valid BIP 340 public key in hex, the client should associate this information with the user, along with the response's `callback`, `minSendable`, and `maxSendable` values. 151. Client calculates a recipient's lnurl pay request url from the `zap` tag on the event being zapped (see Appendix G), or by decoding their lud06 or lud16 field on their profile according to the [lnurl specifications](https://github.com/lnurl/luds). The client MUST send a GET request to this url and parse the response. If `allowsNostr` exists and it is `true`, and if `nostrPubkey` exists and is a valid BIP 340 public key in hex, the client should associate this information with the user, along with the response's `callback`, `minSendable`, and `maxSendable` values.
162. Clients may choose to display a lightning zap button on each post or on a user's profile. If the user's lnurl pay request endpoint supports nostr, the client SHOULD use this NIP to request a zap receipt rather than a normal lnurl invoice. 162. Clients may choose to display a lightning zap button on each post or on a user's profile. If the user's lnurl pay request endpoint supports nostr, the client SHOULD use this NIP to request a `zap receipt` rather than a normal lnurl invoice.
173. When a user (the "sender") indicates they want to send a zap to another user (the "recipient"), the client should create a `zap request` event as described in Appendix A of this NIP and sign it. 173. When a user (the "sender") indicates they want to send a zap to another user (the "recipient"), the client should create a `zap request` event as described in Appendix A of this NIP and sign it.
184. Instead of publishing the `zap request`, the `9734` event should instead be sent to the `callback` url received from the lnurl pay endpoint for the recipient using a GET request. See Appendix B for details and an example. 184. Instead of publishing the `zap request`, the `9734` event should instead be sent to the `callback` url received from the lnurl pay endpoint for the recipient using a GET request. See Appendix B for details and an example.
195. The recipient's lnurl server will receive this request and validate it. See Appendix C for details on how to properly configure an lnurl server to support zaps, and Appendix D for details on how to validate the `nostr` query parameter. 195. The recipient's lnurl server will receive this `zap request` and validate it. See Appendix C for details on how to properly configure an lnurl server to support zaps, and Appendix D for details on how to validate the `nostr` query parameter.
206. If the request is valid, the server should fetch a description hash invoice where the description is this note and this note only. No additional lnurl metadata is included in the description. This will be returned in the response according to [LUD06](https://github.com/lnurl/luds/blob/luds/06.md). 206. If the `zap request` is valid, the server should fetch a description hash invoice where the description is this `zap request` note and this note only. No additional lnurl metadata is included in the description. This will be returned in the response according to [LUD06](https://github.com/lnurl/luds/blob/luds/06.md).
217. On receiving the invoice, the client MAY pay it or pass it to an app that can pay the invoice. 217. On receiving the invoice, the client MAY pay it or pass it to an app that can pay the invoice.
228. Once the invoice is paid, the recipient's lnurl server MUST generate a `zap receipt` as described in Appendix E, and publish it to the `relays` specified in the `zap request`. 228. Once the invoice is paid, the recipient's lnurl server MUST generate a `zap receipt` as described in Appendix E, and publish it to the `relays` specified in the `zap request`.
239. Clients MAY fetch zap notes on posts and profiles, but MUST authorize their validity as described in Appendix F. If the zap request note contains a non-empty `content`, it may display a zap comment. Generally clients should show users the `zap request` note, and use the `zap note` to show "zap authorized by ..." but this is optional. 239. Clients MAY fetch `zap receipt`s on posts and profiles, but MUST authorize their validity as described in Appendix F. If the `zap request` note contains a non-empty `content`, it may display a zap comment. Generally clients should show users the `zap request` note, and use the `zap receipt` to show "zap authorized by ..." but this is optional.
24 24
25## Reference and examples 25## Reference and examples
26 26
@@ -60,10 +60,10 @@ Example:
60 60
61### Appendix B: Zap Request HTTP Request 61### Appendix B: Zap Request HTTP Request
62 62
63A signed zap request event is not published, but is instead sent using a HTTP GET request to the recipient's `callback` url, which was provided by the recipient's lnurl pay endpoint. This request should have the following query parameters defined: 63A signed `zap request` event is not published, but is instead sent using a HTTP GET request to the recipient's `callback` url, which was provided by the recipient's lnurl pay endpoint. This request should have the following query parameters defined:
64 64
65- `amount` is the amount in _millisats_ the sender intends to pay 65- `amount` is the amount in _millisats_ the sender intends to pay
66- `nostr` is the `9734` zap request event, JSON encoded then URI encoded 66- `nostr` is the `9734` `zap request` event, JSON encoded then URI encoded
67- `lnurl` is the lnurl pay url of the recipient, encoded using bech32 with the prefix `lnurl` 67- `lnurl` is the lnurl pay url of the recipient, encoded using bech32 with the prefix `lnurl`
68 68
69This request should return a JSON response with a `pr` key, which is the invoice the sender must pay to finalize his zap. Here is an example flow: 69This request should return a JSON response with a `pr` key, which is the invoice the sender must pay to finalize his zap. Here is an example flow:
@@ -97,18 +97,18 @@ const {pr: invoice} = await fetchJson(`${callback}?amount=${amount}&nostr=${even
97 97
98The lnurl server will need some additional pieces of information so that clients can know that zap invoices are supported: 98The lnurl server will need some additional pieces of information so that clients can know that zap invoices are supported:
99 99
1001. Add a `nostrPubkey` to the lnurl-pay static endpoint `/.well-known/lnurlp/<user>`, where `nostrPubkey` is the nostr pubkey your server will use to sign `zap receipt` events. Clients will use this to validate zap receipts. 1001. Add a `nostrPubkey` to the lnurl-pay static endpoint `/.well-known/lnurlp/<user>`, where `nostrPubkey` is the nostr pubkey your server will use to sign `zap receipt` events. Clients will use this to validate `zap receipt`s.
1012. Add an `allowsNostr` field and set it to true. 1012. Add an `allowsNostr` field and set it to true.
102 102
103### Appendix D: LNURL Server Zap Request Validation 103### Appendix D: LNURL Server Zap Request Validation
104 104
105When a client sends a zap request event to a server's lnurl-pay callback URL, there will be a `nostr` query parameter where the contents of the event are URI- and JSON-encoded. If present, the zap request event must be validated in the following ways: 105When a client sends a `zap request` event to a server's lnurl-pay callback URL, there will be a `nostr` query parameter whose value is that event which is URI- and JSON-encoded. If present, the `zap request` event must be validated in the following ways:
106 106
1071. It MUST have a valid nostr signature 1071. It MUST have a valid nostr signature
1082. It MUST have tags 1082. It MUST have tags
1093. It MUST have only one `p` tag 1093. It MUST have only one `p` tag
1104. It MUST have 0 or 1 `e` tags 1104. It MUST have 0 or 1 `e` tags
1115. There should be a `relays` tag with the relays to send the `zap` note to. 1115. There should be a `relays` tag with the relays to send the `zap receipt` to.
1126. If there is an `amount` tag, it MUST be equal to the `amount` query parameter. 1126. If there is an `amount` tag, it MUST be equal to the `amount` query parameter.
1137. If there is an `a` tag, it MUST be a valid NIP-33 event coordinate 1137. If there is an `a` tag, it MUST be a valid NIP-33 event coordinate
114 114
@@ -116,29 +116,29 @@ The event MUST then be stored for use later, when the invoice is paid.
116 116
117### Appendix E: Zap Receipt Event 117### Appendix E: Zap Receipt Event
118 118
119A `zap receipt` is created by a lightning node when an invoice generated by a `zap request` is paid. Zap receipts are only created when the invoice description (committed to the description hash) contains a zap request note. 119A `zap receipt` is created by a lightning node when an invoice generated by a `zap request` is paid. `Zap receipt`s are only created when the invoice description (committed to the description hash) contains a `zap request` note.
120 120
121When receiving a payment, the following steps are executed: 121When receiving a payment, the following steps are executed:
122 122
1231. Get the description for the invoice. This needs to be saved somewhere during the generation of the description hash invoice. It is saved automatically for you with CLN, which is the reference implementation used here. 1231. Get the description for the invoice. This needs to be saved somewhere during the generation of the description hash invoice. It is saved automatically for you with CLN, which is the reference implementation used here.
1242. Parse the bolt11 description as a JSON nostr event. This SHOULD be validated based on the requirements in Appendix D, either when it is received, or before the invoice is paid. 1242. Parse the bolt11 description as a JSON nostr event. This SHOULD be validated based on the requirements in Appendix D, either when it is received, or before the invoice is paid.
1253. Create a nostr event of kind `9735` as described below, and publish it to the `relays` declared in the zap request. 1253. Create a nostr event of kind `9735` as described below, and publish it to the `relays` declared in the `zap request`.
126 126
127The following should be true of the zap receipt event: 127The following should be true of the `zap receipt` event:
128 128
129- The content SHOULD be empty. 129- The content SHOULD be empty.
130- The `created_at` date SHOULD be set to the invoice `paid_at` date for idempotency. 130- The `created_at` date SHOULD be set to the invoice `paid_at` date for idempotency.
131- `tags` MUST include the `p` tag AND optional `e` tag from the zap request. 131- `tags` MUST include the `p` tag AND optional `e` tag from the `zap request`.
132- The zap receipt MUST have a `bolt11` tag containing the description hash bolt11 invoice. 132- The `zap receipt` MUST have a `bolt11` tag containing the description hash bolt11 invoice.
133- The zap receipt MUST contain a `description` tag which is the JSON-encoded invoice description. 133- The `zap receipt` MUST contain a `description` tag which is the JSON-encoded invoice description.
134- `SHA256(description)` MUST match the description hash in the bolt11 invoice. 134- `SHA256(description)` MUST match the description hash in the bolt11 invoice.
135- The zap receipt MAY contain a `preimage` tag to match against the payment hash of the bolt11 invoice. This isn't really a payment proof, there is no real way to prove that the invoice is real or has been paid. You are trusting the author of the zap receipt for the legitimacy of the payment. 135- The `zap receipt` MAY contain a `preimage` tag to match against the payment hash of the bolt11 invoice. This isn't really a payment proof, there is no real way to prove that the invoice is real or has been paid. You are trusting the author of the `zap receipt` for the legitimacy of the payment.
136 136
137The zap receipt is not a proof of payment, all it proves is that some nostr user fetched an invoice. The existence of the zap receipt implies the invoice as paid, but it could be a lie given a rogue implementation. 137The `zap receipt` is not a proof of payment, all it proves is that some nostr user fetched an invoice. The existence of the `zap receipt` implies the invoice as paid, but it could be a lie given a rogue implementation.
138 138
139A reference implementation for a zap-enabled lnurl server can be found [here](https://github.com/jb55/cln-nostr-zapper). 139A reference implementation for a zap-enabled lnurl server can be found [here](https://github.com/jb55/cln-nostr-zapper).
140 140
141Example zap receipt: 141Example `zap receipt`:
142 142
143```json 143```json
144{ 144{
@@ -160,7 +160,7 @@ Example zap receipt:
160 160
161### Appendix F: Validating Zap Receipts 161### Appendix F: Validating Zap Receipts
162 162
163A client can retrieve `zap receipts` on events and pubkeys using a NIP-01 filter, for example `{"kinds": [9735], "#e": [...]}`. Zaps MUST be validated using the following steps: 163A client can retrieve `zap receipt`s on events and pubkeys using a NIP-01 filter, for example `{"kinds": [9735], "#e": [...]}`. Zaps MUST be validated using the following steps:
164 164
165- The `zap receipt` event's `pubkey` MUST be the same as the recipient's lnurl provider's `nostrPubkey` (retrieved in step 1 of the protocol flow). 165- The `zap receipt` event's `pubkey` MUST be the same as the recipient's lnurl provider's `nostrPubkey` (retrieved in step 1 of the protocol flow).
166- The `invoiceAmount` contained in the `bolt11` tag of the `zap receipt` MUST equal the `amount` tag of the `zap request` (if present). 166- The `invoiceAmount` contained in the `bolt11` tag of the `zap receipt` MUST equal the `amount` tag of the `zap request` (if present).
@@ -168,7 +168,7 @@ A client can retrieve `zap receipts` on events and pubkeys using a NIP-01 filter
168 168
169### Appendix G: `zap` tag on zapped event 169### Appendix G: `zap` tag on zapped event
170 170
171When an event includes a `zap` tag, clients SHOULD calculate the lnurl pay request based on it's value instead of the profile's field. An optional third argument on the tag specifies the type of value, either `lud06` or `lud16`. 171When an event includes a `zap` tag, clients SHOULD calculate the lnurl pay request based on its value instead of the profile's field. An optional third argument on the tag specifies the type of value, either `lud06` or `lud16`.
172 172
173```json 173```json
174{ 174{
@@ -180,4 +180,4 @@ When an event includes a `zap` tag, clients SHOULD calculate the lnurl pay reque
180 180
181## Future Work 181## Future Work
182 182
183Zaps can be extended to be more private by encrypting zap request notes to the target user, but for simplicity it has been left out of this initial draft. 183Zaps can be extended to be more private by encrypting `zap request` notes to the target user, but for simplicity it has been left out of this initial draft.
diff --git a/89.md b/89.md
new file mode 100644
index 0000000..5eee3b8
--- /dev/null
+++ b/89.md
@@ -0,0 +1,116 @@
1NIP-89
2======
3
4Recommended Application Handlers
5--------------------------------
6
7`draft` `optional` `author:pablof7z`
8
9This NIP describes `kind:31989` and `kind:31990`: a way to discover applications that can handle unknown event-kinds.
10
11## Rationale
12Nostr's discoverability and transparent event interaction is one of its most interesting/novel mechanics.
13This NIP provides a simple way for clients to discover applications that handle events of a specific kind to ensure smooth cross-client and cross-kind interactions.
14
15### Parties involved
16There are three actors to this workflow:
17
18* application that handles a specific event kind (note that an application doesn't necessarily need to be a distinct entity and it could just be the same pubkey as user A)
19 * Publishes `kind:31990`, detailing how apps should redirect to it
20* user A, who recommends an app that handles a specific event kind
21 * Publishes `kind:31989`
22* user B, who seeks a recommendation for an app that handles a specific event kind
23 * Queries for `kind:31989` and, based on results, queries for `kind:31990`
24
25# Events
26
27## Recommendation event
28```json
29{
30 "kind": 31989,
31 "pubkey": <recommender-user-pubkey>,
32 "tags": [
33 [ "d", <supported-event-kind> ],
34 [ "a", "31990:app1-pubkey:<d-identifier>", "wss://relay1", "ios" ],
35 [ "a", "31990:app2-pubkey:<d-identifier>", "wss://relay2", "web" ]
36 ]
37}
38```
39
40The `d` tag in `kind:31989` is the supported event kind this event is recommending.
41
42Multiple `a` tags can appear on the same `kind:31989`.
43
44The second value of the tag SHOULD be a relay hint.
45The third value of the tag SHOULD be the platform where this recommendation might apply.
46
47## Handler information
48```json
49{
50 "kind": 31990,
51 "pubkey": <pubkey>,
52 "content": "<optional-kind:0-style-metadata>",
53 "tags": [
54 [ "d", <random-id> ],
55 [ "k", <supported-event-kind> ],
56 [ "web", "https://..../a/<bech32>", "nevent" ],
57 [ "web", "https://..../p/<bech32>", "nprofile" ],
58 [ "web", "https://..../e/<bech32>" ],
59 [ "ios", ".../<bech32>" ]
60 ]
61}
62```
63
64* `content` is an optional `set_metadata`-like stringified JSON object, as described in NIP-01. This content is useful when the pubkey creating the `kind:31990` is not an application. If `content` is empty, the `kind:0` of the pubkey should be used to display application information (e.g. name, picture, web, LUD16, etc.)
65
66* `k` tags' value is the event kind that is supported by this `kind:31990`.
67Using a `k` tag(s) (instead of having the kind onf the NIP-33 `d` tag) provides:
68 * Multiple `k` tags can exist in the same event if the application supports more than one event kind and their handler URLs are the same.
69 * The same pubkey can have multiple events with different apps that handle the same event kind.
70
71* `bech32` in a URL MUST be replaced by clients with the NIP-19-encoded entity that should be loaded by the application.
72
73Multiple tags might be registered by the app, following NIP-19 nomenclature as the second value of the array.
74
75A tag without a second value in the array SHOULD be considered a generic handler for any NIP-19 entity that is not handled by a different tag.
76
77# User flow
78A user A who uses a non-`kind:1`-centric nostr app could choose to announce/recommend a certain kind-handler application.
79
80When user B sees an unknown event kind, e.g. in a social-media centric nostr client, the client would allow user B to interact with the unknown-kind event (e.g. tapping on it).
81
82The client MIGHT query for the user's and the user's follows handler.
83
84# Example
85
86## User A recommends a `kind:31337`-handler
87User A might be a user of Zapstr, a `kind:31337`-centric client (tracks). Using Zapstr, user A publishes an event recommending Zapstr as a `kind:31337`-handler.
88
89```json
90{
91 "kind": 31989,
92 "tags": [
93 [ "d", "31337" ],
94 [ "a", "31990:1743058db7078661b94aaf4286429d97ee5257d14a86d6bfa54cb0482b876fb0:abcd", <relay-url>, "web" ]
95 ]
96}
97```
98
99## User B interacts with a `kind:31337`-handler
100User B might see in their timeline an event referring to a `kind:31337` event
101(e.g. a `kind:1` tagging a `kind:31337`).
102
103User B's client, not knowing how to handle a `kind:31337` might display the event
104using its `alt` tag (as described in NIP-31). When the user clicks on the event,
105the application queries for a handler for this `kind`:
106
107`["REQ", <id>, '[{ "kinds": [31989], "#d": ["31337"], 'authors': [<user>, <users-contact-list>] }]']`
108
109User B, who follows User A, sees that `kind:31989` event and fetches the `a`-tagged event for the app and handler information.
110
111User B's client sees the application's `kind:31990` which includes the information to redirect the user to the relevant URL with the desired entity replaced in the URL.
112
113## Alternative query bypassing `kind:31989`
114Alternatively, users might choose to query directly for `kind:31990` for an event kind. Clients SHOULD be careful doing this and use spam-prevention mechanisms to avoid directing users to malicious handlers.
115
116`["REQ", <id>, '[{ "kinds": [31990], "#k": [<desired-event-kind>], 'authors': [...] }]']` \ No newline at end of file
diff --git a/README.md b/README.md
index f3ae501..a940ba6 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
1# NIPs 1# NIPs
2 2
3NIPs stand for **Nostr Implementation Possibilities**. 3NIPs stand for **Nostr Implementation Possibilities**.
4They exist to document what may be implemented by [Nostr](https://github.com/fiatjaf/nostr)-compatible _relay_ and _client_ software. 4They exist to document what may be implemented by [Nostr](https://github.com/nostr-protocol/nostr)-compatible _relay_ and _client_ software.
5 5
6--- 6---
7 7
@@ -45,6 +45,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/fia
45- [NIP-26: Delegated Event Signing](26.md) 45- [NIP-26: Delegated Event Signing](26.md)
46- [NIP-27: Text Note References](27.md) 46- [NIP-27: Text Note References](27.md)
47- [NIP-28: Public Chat](28.md) 47- [NIP-28: Public Chat](28.md)
48- [NIP-30: Custom Emoji](30.md)
48- [NIP-31: Dealing with Unknown Events](31.md) 49- [NIP-31: Dealing with Unknown Events](31.md)
49- [NIP-33: Parameterized Replaceable Events](33.md) 50- [NIP-33: Parameterized Replaceable Events](33.md)
50- [NIP-36: Sensitive Content](36.md) 51- [NIP-36: Sensitive Content](36.md)
@@ -61,6 +62,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/fia
61- [NIP-58: Badges](58.md) 62- [NIP-58: Badges](58.md)
62- [NIP-65: Relay List Metadata](65.md) 63- [NIP-65: Relay List Metadata](65.md)
63- [NIP-78: Application-specific data](78.md) 64- [NIP-78: Application-specific data](78.md)
65- [NIP-89: Recommended Application Handlers](89.md)
64- [NIP-94: File Metadata](94.md) 66- [NIP-94: File Metadata](94.md)
65 67
66## Event Kinds 68## Event Kinds
@@ -101,6 +103,8 @@ They exist to document what may be implemented by [Nostr](https://github.com/fia
101| `30018` | Create or update a product | [15](15.md) | 103| `30018` | Create or update a product | [15](15.md) |
102| `30023` | Long-form Content | [23](23.md) | 104| `30023` | Long-form Content | [23](23.md) |
103| `30078` | Application-specific Data | [78](78.md) | 105| `30078` | Application-specific Data | [78](78.md) |
106| `31989` | Handler recommendation | [89](89.md) |
107| `31990` | Handler information | [89](89.md) |
104 108
105### Event Kind Ranges 109### Event Kind Ranges
106 110
@@ -143,6 +147,7 @@ When experimenting with kinds, keep in mind the classification introduced by [NI
143| name | value | other parameters | NIP | 147| name | value | other parameters | NIP |
144| ----------------- | ------------------------------------ | -------------------- | ------------------------ | 148| ----------------- | ------------------------------------ | -------------------- | ------------------------ |
145| `a` | coordinates to an event | relay URL | [33](33.md), [23](23.md) | 149| `a` | coordinates to an event | relay URL | [33](33.md), [23](23.md) |
150| `alt` | Alt tag | -- | [31](31.md) |
146| `d` | identifier | -- | [33](33.md) | 151| `d` | identifier | -- | [33](33.md) |
147| `e` | event id (hex) | relay URL, marker | [1](01.md), [10](10.md) | 152| `e` | event id (hex) | relay URL, marker | [1](01.md), [10](10.md) |
148| `g` | geohash | -- | [12](12.md) | 153| `g` | geohash | -- | [12](12.md) |
@@ -157,6 +162,7 @@ When experimenting with kinds, keep in mind the classification introduced by [NI
157| `delegation` | pubkey, conditions, delegation token | -- | [26](26.md) | 162| `delegation` | pubkey, conditions, delegation token | -- | [26](26.md) |
158| `description` | badge description | -- | [58](58.md) | 163| `description` | badge description | -- | [58](58.md) |
159| `description` | invoice description | -- | [57](57.md) | 164| `description` | invoice description | -- | [57](57.md) |
165| `emoji` | shortcode | image URL | [30](30.md) |
160| `expiration` | unix timestamp (string) | -- | [40](40.md) | 166| `expiration` | unix timestamp (string) | -- | [40](40.md) |
161| `image` | image URL | dimensions in pixels | [23](23.md), [58](58.md) | 167| `image` | image URL | dimensions in pixels | [23](23.md), [58](58.md) |
162| `lnurl` | `bech32` encoded `lnurl` | -- | [57](57.md) | 168| `lnurl` | `bech32` encoded `lnurl` | -- | [57](57.md) |