upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--05.md6
-rw-r--r--10.md57
-rw-r--r--11.md30
-rw-r--r--17.md2
-rw-r--r--22.md184
-rw-r--r--24.md2
-rw-r--r--29.md168
-rw-r--r--46.md214
-rw-r--r--47.md130
-rw-r--r--51.md2
-rw-r--r--53.md2
-rw-r--r--55.md29
-rw-r--r--57.md2
-rw-r--r--59.md2
-rw-r--r--60.md2
-rw-r--r--69.md86
-rw-r--r--7D.md34
-rw-r--r--86.md90
-rw-r--r--96.md4
-rw-r--r--C7.md29
-rw-r--r--README.md37
21 files changed, 861 insertions, 251 deletions
diff --git a/05.md b/05.md
index ca6da7b..7e823fb 100644
--- a/05.md
+++ b/05.md
@@ -10,7 +10,7 @@ On events of kind `0` (`user metadata`) one can specify the key `"nip05"` with a
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
13The result should be a JSON document object with a key `"names"` that should then be a mapping of names to hex formatted public keys. If the public key for the given `<name>` matches the `pubkey` from the `user's metadata` event, the client then concludes that the given pubkey can indeed be referenced by its identifier. 13The result should be a JSON document object with a key `"names"` that should then be a mapping of names to hex formatted public keys. If the public key for the given `<name>` matches the `pubkey` from the `user metadata` event, the client then concludes that the given pubkey can indeed be referenced by its identifier.
14 14
15### Example 15### Example
16 16
@@ -33,7 +33,7 @@ It will make a GET request to `https://example.com/.well-known/nostr.json?name=b
33 "bob": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9" 33 "bob": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9"
34 } 34 }
35} 35}
36```` 36```
37 37
38or with the **recommended** `"relays"` attribute: 38or with the **recommended** `"relays"` attribute:
39 39
@@ -46,7 +46,7 @@ or with the **recommended** `"relays"` attribute:
46 "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9": [ "wss://relay.example.com", "wss://relay2.example.com" ] 46 "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9": [ "wss://relay.example.com", "wss://relay2.example.com" ]
47 } 47 }
48} 48}
49```` 49```
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
diff --git a/10.md b/10.md
index 94db4fa..c1d5068 100644
--- a/10.md
+++ b/10.md
@@ -10,33 +10,6 @@ On "e" and "p" tags in Text Events (kind 1)
10## Abstract 10## Abstract
11This NIP describes how to use "e" and "p" tags in text events, especially those that are replies to other text events. It helps clients thread the replies into a tree rooted at the original event. 11This NIP describes how to use "e" and "p" tags in text events, especially those that are replies to other text events. It helps clients thread the replies into a tree rooted at the original event.
12 12
13## Positional "e" tags (DEPRECATED)
14>This scheme is in common use; but should be considered deprecated.
15
16`["e", <event-id>, <relay-url>]` as per NIP-01.
17
18Where:
19
20 * `<event-id>` is the id of the event being referenced.
21 * `<relay-url>` is the URL of a recommended relay associated with the reference. Many clients treat this field as optional.
22
23**The positions of the "e" tags within the event denote specific meanings as follows**:
24
25 * No "e" tag: <br>
26 This event is not a reply to, nor does it refer to, any other event.
27
28 * One "e" tag: <br>
29 `["e", <id>]`: The id of the event to which this event is a reply.
30
31 * Two "e" tags: `["e", <root-id>]`, `["e", <reply-id>]` <br>
32 `<root-id>` is the id of the event at the root of the reply chain. `<reply-id>` is the id of the article to which this event is a reply.
33
34 * Many "e" tags: `["e", <root-id>]` `["e", <mention-id>]`, ..., `["e", <reply-id>]`<br>
35There may be any number of `<mention-ids>`. These are the ids of events which may, or may not be in the reply chain.
36They are citing from this event. `root-id` and `reply-id` are as above.
37
38>This scheme is deprecated because it creates ambiguities that are difficult, or impossible to resolve when an event references another but is not a reply.
39
40## Marked "e" tags (PREFERRED) 13## Marked "e" tags (PREFERRED)
41`["e", <event-id>, <relay-url>, <marker>, <pubkey>]` 14`["e", <event-id>, <relay-url>, <marker>, <pubkey>]`
42 15
@@ -62,3 +35,33 @@ When replying to a text event E the reply event's "p" tags should contain all of
62 35
63Example: Given a text event authored by `a1` with "p" tags [`p1`, `p2`, `p3`] then the "p" tags of the reply should be [`a1`, `p1`, `p2`, `p3`] 36Example: Given a text event authored by `a1` with "p" tags [`p1`, `p2`, `p3`] then the "p" tags of the reply should be [`a1`, `p1`, `p2`, `p3`]
64in no particular order. 37in no particular order.
38
39## Deprecated Positional "e" tags
40
41This scheme is not in common use anymore and is here just to keep backward compatibility with older events on the network.
42
43Positional `e` tags are deprecated because they create ambiguities that are difficult, or impossible to resolve when an event references another but is not a reply.
44
45They use simple `e` tags without any marker.
46
47`["e", <event-id>, <relay-url>]` as per NIP-01.
48
49Where:
50
51 * `<event-id>` is the id of the event being referenced.
52 * `<relay-url>` is the URL of a recommended relay associated with the reference. Many clients treat this field as optional.
53
54**The positions of the "e" tags within the event denote specific meanings as follows**:
55
56 * No "e" tag: <br>
57 This event is not a reply to, nor does it refer to, any other event.
58
59 * One "e" tag: <br>
60 `["e", <id>]`: The id of the event to which this event is a reply.
61
62 * Two "e" tags: `["e", <root-id>]`, `["e", <reply-id>]` <br>
63 `<root-id>` is the id of the event at the root of the reply chain. `<reply-id>` is the id of the article to which this event is a reply.
64
65 * Many "e" tags: `["e", <root-id>]` `["e", <mention-id>]`, ..., `["e", <reply-id>]`<br>
66There may be any number of `<mention-ids>`. These are the ids of events which may, or may not be in the reply chain.
67They are citing from this event. `root-id` and `reply-id` are as above. \ No newline at end of file
diff --git a/11.md b/11.md
index 0ca5870..8af4f31 100644
--- a/11.md
+++ b/11.md
@@ -14,6 +14,8 @@ When a relay receives an HTTP(s) request with an `Accept` header of `application
14{ 14{
15 "name": <string identifying relay>, 15 "name": <string identifying relay>,
16 "description": <string with detailed information>, 16 "description": <string with detailed information>,
17 "banner": <a link to an image (e.g. in .jpg, or .png format)>,
18 "icon": <a link to an icon (e.g. in .jpg, or .png format>,
17 "pubkey": <administrative contact pubkey>, 19 "pubkey": <administrative contact pubkey>,
18 "contact": <administrative alternate contact>, 20 "contact": <administrative alternate contact>,
19 "supported_nips": <a list of NIP numbers supported by the relay>, 21 "supported_nips": <a list of NIP numbers supported by the relay>,
@@ -35,6 +37,21 @@ A relay may select a `name` for use in client software. This is a string, and S
35 37
36Detailed plain-text information about the relay may be contained in the `description` string. It is recommended that this contain no markup, formatting or line breaks for word wrapping, and simply use double newline characters to separate paragraphs. There are no limitations on length. 38Detailed plain-text information about the relay may be contained in the `description` string. It is recommended that this contain no markup, formatting or line breaks for word wrapping, and simply use double newline characters to separate paragraphs. There are no limitations on length.
37 39
40### Banner
41
42To make nostr relay management more user friendly, an effort should be made by relay owners to communicate with non-dev non-technical nostr end users. A banner is a visual representation of the relay. It should aim to visually communicate the brand of the relay, complementing the text `Description`. [Here is an example banner](https://image.nostr.build/232ddf6846e8aea5a61abcd70f9222ab521f711aa545b7ab02e430248fa3a249.png) mockup as visualized in Damus iOS relay view of the Damus relay.
43
44### Icon
45
46Icon is a compact visual representation of the relay for use in UI with limited real estate such as a nostr user's relay list view. Below is an example URL pointing to an image to be used as an icon for the relay. Recommended to be squared in shape.
47
48```jsonc
49{
50 "icon": "https://nostr.build/i/53866b44135a27d624e99c6165cabd76ac8f72797209700acb189fce75021f47.jpg",
51 // other fields...
52}
53```
54
38### Pubkey 55### Pubkey
39 56
40An administrative contact may be listed with a `pubkey`, in the same format as Nostr events (32-byte hex for a `secp256k1` public key). If a contact is listed, this provides clients with a recommended address to send encrypted direct messages (See [NIP-17](17.md)) to a system administrator. Expected uses of this address are to report abuse or illegal content, file bug reports, or request other technical assistance. 57An administrative contact may be listed with a `pubkey`, in the same format as Nostr events (32-byte hex for a `secp256k1` public key). If a contact is listed, this provides clients with a recommended address to send encrypted direct messages (See [NIP-17](17.md)) to a system administrator. Expected uses of this address are to report abuse or illegal content, file bug reports, or request other technical assistance.
@@ -245,7 +262,7 @@ processed by appropriate client software.
245 262
246Relays that require payments may want to expose their fee schedules. 263Relays that require payments may want to expose their fee schedules.
247 264
248```json 265```jsonc
249{ 266{
250 "payments_url": "https://my-relay/payments", 267 "payments_url": "https://my-relay/payments",
251 "fees": { 268 "fees": {
@@ -253,17 +270,6 @@ Relays that require payments may want to expose their fee schedules.
253 "subscription": [{ "amount": 5000000, "unit": "msats", "period": 2592000 }], 270 "subscription": [{ "amount": 5000000, "unit": "msats", "period": 2592000 }],
254 "publication": [{ "kinds": [4], "amount": 100, "unit": "msats" }], 271 "publication": [{ "kinds": [4], "amount": 100, "unit": "msats" }],
255 }, 272 },
256 ...
257}
258```
259
260### Icon
261
262A URL pointing to an image to be used as an icon for the relay. Recommended to be squared in shape.
263
264```jsonc
265{
266 "icon": "https://nostr.build/i/53866b44135a27d624e99c6165cabd76ac8f72797209700acb189fce75021f47.jpg",
267 // other fields... 273 // other fields...
268} 274}
269``` 275```
diff --git a/17.md b/17.md
index 4b96bce..72f40c4 100644
--- a/17.md
+++ b/17.md
@@ -47,7 +47,7 @@ An optional `subject` tag defines the current name/topic of the conversation. An
47 47
48Following [NIP-59](59.md), the **unsigned** `kind:14` chat message must be sealed (`kind:13`) and then gift-wrapped (`kind:1059`) to each receiver and the sender individually. 48Following [NIP-59](59.md), the **unsigned** `kind:14` chat message must be sealed (`kind:13`) and then gift-wrapped (`kind:1059`) to each receiver and the sender individually.
49 49
50```js 50```jsonc
51{ 51{
52 "id": "<usual hash>", 52 "id": "<usual hash>",
53  "pubkey": randomPublicKey, 53  "pubkey": randomPublicKey,
diff --git a/22.md b/22.md
new file mode 100644
index 0000000..f11925f
--- /dev/null
+++ b/22.md
@@ -0,0 +1,184 @@
1NIP-22
2======
3
4Comment
5-------
6
7`draft` `optional`
8
9A comment is a threading note always scoped to a root event or an `I`-tag.
10
11It uses `kind:1111` with plaintext `.content` (no HTML, Markdown, or other formatting).
12
13Comments MUST point to the root scope using uppercase tag names (e.g. `K`, `E`, `A` or `I`)
14and MUST point to the parent item with lowercase ones (e.g. `k`, `e`, `a` or `i`).
15
16```jsonc
17{
18 kind: 1111,
19 content: '<comment>',
20 tags: [
21 // root scope: event addresses, event ids, or I-tags.
22 ["<A, E, I>", "<address, id or I-value>", "<relay or web page hint>", "<root event's pubkey, if an E tag>"],
23 // the root item kind
24 ["K", "<root kind>"],
25
26 // parent item: event addresses, event ids, or i-tags.
27 ["<a, e, i>", "<address, id or i-value>", "<relay or web page hint>", "<parent event's pubkey, if an e tag>"],
28 // parent item kind
29 ["k", "<parent comment kind>"]
30 ]
31 // other fields
32}
33```
34
35Tags `K` and `k` MUST be present to define the event kind of the root and the parent items.
36
37`I` and `i` tags create scopes for hashtags, geohashes, URLs, and other external identifiers.
38
39The possible values for `i` tags – and `k` tags, when related to an extenal identity – are listed on [NIP-73](73.md).
40Their uppercase versions use the same type of values but relate to the root item instead of the parent one.
41
42`q` tags MAY be used when citing events in the `.content` with [NIP-21](21.md).
43
44```json
45["q", "<event-id> or <event-address>", "<relay-url>", "<pubkey-if-a-regular-event>"]
46```
47
48`p` tags SHOULD be used when mentioning pubkeys in the `.content` with [NIP-21](21.md).
49If the parent item is an event, a `p` tag set to the parent event's author SHOULD be added.
50
51```json
52["p", "<pubkey>", "<relay-url>"]
53```
54
55## Examples
56
57A comment on a blog post looks like this:
58
59```jsonc
60{
61 kind: 1111,
62 content: 'Great blog post!',
63 tags: [
64 // top-level comments scope to event addresses or ids
65 ["A", "30023:3c9849383bdea883b0bd16fece1ed36d37e37cdde3ce43b17ea4e9192ec11289:f9347ca7", "wss://example.relay"],
66 // the root kind
67 ["K", "30023"],
68
69 // the parent event address (same as root for top-level comments)
70 ["a", "30023:3c9849383bdea883b0bd16fece1ed36d37e37cdde3ce43b17ea4e9192ec11289:f9347ca7", "wss://example.relay"],
71 // when the parent event is replaceable or addressable, also include an `e` tag referencing its id
72 ["e", "5b4fc7fed15672fefe65d2426f67197b71ccc82aa0cc8a9e94f683eb78e07651", "wss://example.relay"],
73 // the parent event kind
74 ["k", "30023"]
75 ]
76 // other fields
77}
78```
79
80A comment on a [NIP-94](94.md) file looks like this:
81
82```jsonc
83{
84 kind: 1111,
85 content: 'Great file!',
86 tags: [
87 // top-level comments have the same scope and reply to addresses or ids
88 ["E", "768ac8720cdeb59227cf95e98b66560ef03d8bc9a90d721779e76e68fb42f5e6", "wss://example.relay", "3721e07b079525289877c366ccab47112bdff3d1b44758ca333feb2dbbbbe5bb"],
89 // the root kind
90 ["K", "1063"],
91
92 // the parent event id (same as root for top-level comments)
93 ["e", "768ac8720cdeb59227cf95e98b66560ef03d8bc9a90d721779e76e68fb42f5e6", "wss://example.relay", "3721e07b079525289877c366ccab47112bdff3d1b44758ca333feb2dbbbbe5bb"],
94 // the parent kind
95 ["k", "1063"]
96 ]
97 // other fields
98}
99```
100
101A reply to a comment looks like this:
102
103```jsonc
104{
105 kind: 1111,
106 content: 'This is a reply to "Great file!"',
107 tags: [
108 // nip-94 file event id
109 ["E", "768ac8720cdeb59227cf95e98b66560ef03d8bc9a90d721779e76e68fb42f5e6", "wss://example.relay", "fd913cd6fa9edb8405750cd02a8bbe16e158b8676c0e69fdc27436cc4a54cc9a"],
110 // the root kind
111 ["K", "1063"],
112
113 // the parent event
114 ["e", "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "wss://example.relay", "93ef2ebaaf9554661f33e79949007900bbc535d239a4c801c33a4d67d3e7f546"],
115 // the parent kind
116 ["k", "1111"]
117 ]
118 // other fields
119}
120```
121
122A comment on a website's url looks like this:
123
124```jsonc
125{
126 kind: 1111,
127 content: 'Nice article!',
128 tags: [
129 // referencing the root url
130 ["I", "https://abc.com/articles/1"],
131 // the root "kind": for an url, the kind is its domain
132 ["K", "https://abc.com"],
133
134 // the parent reference (same as root for top-level comments)
135 ["i", "https://abc.com/articles/1"],
136 // the parent "kind": for an url, the kind is its domain
137 ["k", "https://abc.com"]
138 ]
139 // other fields
140}
141```
142
143A podcast comment example:
144
145```jsonc
146{
147 id: "80c48d992a38f9c445b943a9c9f1010b396676013443765750431a9004bdac05",
148 pubkey: "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111",
149 kind: 1111,
150 content: "This was a great episode!",
151 tags: [
152 // podcast episode reference
153 ["I", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f", "https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"],
154 // podcast episode type
155 ["K", "podcast:item:guid"],
156
157 // same value as "I" tag above, because it is a top-level comment (not a reply to a comment)
158 ["i", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f", "https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"],
159 ["k", "podcast:item:guid"]
160 ]
161 // other fields
162}
163```
164
165A reply to a podcast comment:
166
167```jsonc
168{
169 kind: 1111,
170 content: "I'm replying to the above comment.",
171 tags: [
172 // podcast episode reference
173 ["I", "podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f", "https://fountain.fm/episode/z1y9TMQRuqXl2awyrQxg"],
174 // podcast episode type
175 ["K", "podcast:item:guid"],
176
177 // this is a reference to the above comment
178 ["e", "80c48d992a38f9c445b943a9c9f1010b396676013443765750431a9004bdac05", "wss://example.relay", "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111"],
179 // the parent comment kind
180 ["k", "1111"]
181 ]
182 // other fields
183}
184```
diff --git a/24.md b/24.md
index 1afa7c7..df3a932 100644
--- a/24.md
+++ b/24.md
@@ -6,7 +6,7 @@ Extra metadata fields and tags
6 6
7`draft` `optional` 7`draft` `optional`
8 8
9This NIP defines extra optional fields added to events. 9This NIP keeps track of extra optional fields that can added to events which are not defined anywhere else but have become _de facto_ standards and other minor implementation possibilities that do not deserve their own NIP and do not have a place in other NIPs.
10 10
11kind 0 11kind 0
12====== 12======
diff --git a/29.md b/29.md
index 6232f8b..331047f 100644
--- a/29.md
+++ b/29.md
@@ -22,9 +22,15 @@ Relays are supposed to generate the events that describe group metadata and grou
22 22
23A group may be identified by a string in the format `<host>'<group-id>`. For example, a group with _id_ `abcdef` hosted at the relay `wss://groups.nostr.com` would be identified by the string `groups.nostr.com'abcdef`. 23A group may be identified by a string in the format `<host>'<group-id>`. For example, a group with _id_ `abcdef` hosted at the relay `wss://groups.nostr.com` would be identified by the string `groups.nostr.com'abcdef`.
24 24
25Group identifiers must be strings restricted to the characters `a-z0-9-_`, and SHOULD be random in order to avoid name collisions.
26
27When encountering just the `<host>` without the `'<group-id>`, clients MAY infer `_` as the group id, which is a special top-level group dedicated to relay-local discussions.
28
25## The `h` tag 29## The `h` tag
26 30
27Events sent by users to groups (chat messages, text notes, moderation events etc) must have an `h` tag with the value set to the group _id_. 31Events sent by users to groups (chat messages, text notes, moderation events etc) MUST have an `h` tag with the value set to the group _id_.
32
33`h` tags MAY include the group's name as the second argument. This allows `unmanaged` groups to be assigned human-readable names without relay support.
28 34
29## Timeline references 35## Timeline references
30 36
@@ -36,48 +42,33 @@ This is a hack to prevent messages from being broadcasted to external relays tha
36 42
37Relays should prevent late publication (messages published now with a timestamp from days or even hours ago) unless they are open to receive a group forked or moved from another relay. 43Relays should prevent late publication (messages published now with a timestamp from days or even hours ago) unless they are open to receive a group forked or moved from another relay.
38 44
39## Event definitions 45## Group management
40 46
41- *text root note* (`kind:11`) 47Groups can have any number of users with elevated access. These users are identified by role labels which are arbitrarily defined by the relays (see also the description of `kind:39003`). What each role is capable of not defined in this NIP either, it's a relay policy that can vary. Roles can be assigned by other users (as long as they have the capability to add roles) by publishing a `kind:9000` event with that user's pubkey in a `p` tag and the roles afterwards (even if the user is already a group member a `kind:9000` can be issued and the user roles must just be updated).
42 48
43This is the basic unit of a "microblog" root text note sent to a group. 49The roles supported by the group as to having some special privilege assigned to them should be accessible on the event `kind:39003`, but the relay may also accept other role names, arbitrarily defined by clients, and just not do anything with them.
44 50
45```jsonc 51Users with any roles that have any privilege can be considered _admins_ in a broad sense and be returned in the `kind:39001` event for a group.
46 "kind": 11,
47 "content": "hello my friends lovers of pizza",
48 "tags": [
49 ["h", "<group-id>"],
50 ["previous", "<event-id-first-chars>", "<event-id-first-chars>", /*...*/]
51 ]
52 // other fields...
53```
54 52
55- *threaded text reply* (`kind:12`) 53## Unmanaged groups
56 54
57This is the basic unit of a "microblog" reply note sent to a group. It's the same as `kind:11`, except for the fact that it must be used whenever it's in reply to some other note (either in reply to a `kind:11` or a `kind:12`). `kind:12` events SHOULD use NIP-10 markers, leaving an empty relay url: 55Unmanaged groups are impromptu groups that can be used in any public relay unaware of NIP-29 specifics. They piggyback on relays' natural white/blacklists (or lack of) but aside from that are not actively managed and won't have any admins, group state or metadata events.
58 56
59* `["e", "<kind-11-root-id>", "", "root"]` 57In `unmanaged` groups, everybody is considered to be a member.
60* `["e", "<kind-12-event-id>", "", "reply"]`
61 58
62- *chat message* (`kind:9`) 59Unmanaged groups can transition to managed groups, in that case the relay master key just has to publish moderation events setting the state of all groups and start enforcing the rules they choose to.
63 60
64This is the basic unit of a _chat message_ sent to a group. 61## Event definitions
65 62
66```jsonc 63These are the events expected to be found in NIP-29 groups.
67 "kind": 9,
68 "content": "hello my friends lovers of pizza",
69 "tags": [
70 ["h", "<group-id>"],
71 ["previous", "<event-id-first-chars>", "<event-id-first-chars>", /*...*/]
72 ]
73 // other fields...
74```
75 64
76- *chat message threaded reply* (`kind:10`) 65### Normal user-created events
77 66
78Similar to `kind:12`, this is the basic unit of a chat message sent to a group. This is intended for in-chat threads that may be hidden by default. Not all in-chat replies MUST use `kind:10`, only when the intention is to create a hidden thread that isn't part of the normal flow of the chat (although clients are free to display those by default too). 67Groups may accept any event kind, including chats, threads, long-form articles, calendar, livestreams, market announcements and so on. These should be as defined in their respective NIPs, with the addition of the `h` tag.
79 68
80`kind:10` SHOULD use NIP-10 markers, just like `kind:12`. 69### User-related group management events
70
71These are events that can be sent by users to manage their situation in a group, they also require the `h` tag.
81 72
82- *join request* (`kind:9021`) 73- *join request* (`kind:9021`)
83 74
@@ -88,11 +79,14 @@ Any user can send one of these events to the relay in order to be automatically
88 "kind": 9021, 79 "kind": 9021,
89 "content": "optional reason", 80 "content": "optional reason",
90 "tags": [ 81 "tags": [
91 ["h", "<group-id>"] 82 ["h", "<group-id>"],
83 ["code", "<optional-invite-code>"]
92 ] 84 ]
93} 85}
94``` 86```
95 87
88The optional `code` tag may be used by the relay to preauthorize acceptances in `closed` groups, together with the `kind:9009` `create-invite` moderation event.
89
96- *leave request* (`kind:9022`) 90- *leave request* (`kind:9022`)
97 91
98Any user can send one of these events to the relay in order to be automatically removed from the group. The relay will automatically issue a `kind:9001` in response removing this user. 92Any user can send one of these events to the relay in order to be automatically removed from the group. The relay will automatically issue a `kind:9001` in response removing this user.
@@ -107,11 +101,15 @@ Any user can send one of these events to the relay in order to be automatically
107} 101}
108``` 102```
109 103
104### Group state -- or moderation
105
106These are events expected to be sent by the relay master key or by group admins -- and relays should reject them if they don't come from an authorized admin. They also require the `h` tag.
107
110- *moderation events* (`kinds:9000-9020`) (optional) 108- *moderation events* (`kinds:9000-9020`) (optional)
111 109
112Clients can send these events to a relay in order to accomplish a moderation action. Relays must check if the pubkey sending the event is capable of performing the given action. The relay may discard the event after taking action or keep it as a moderation log. 110Clients can send these events to a relay in order to accomplish a moderation action. Relays must check if the pubkey sending the event is capable of performing the given action based on its role and the relay's internal policy (see also the description of `kind:39003`).
113 111
114```json 112```jsonc
115{ 113{
116 "kind": 90xx, 114 "kind": 90xx,
117 "content": "optional reason", 115 "content": "optional reason",
@@ -124,17 +122,21 @@ Clients can send these events to a relay in order to accomplish a moderation act
124 122
125Each moderation action uses a different kind and requires different arguments, which are given as tags. These are defined in the following table: 123Each moderation action uses a different kind and requires different arguments, which are given as tags. These are defined in the following table:
126 124
127| kind | name | tags | 125| kind | name | tags |
128| --- | --- | --- | 126| --- | --- | --- |
129| 9000 | `add-user` | `p` (pubkey hex) | 127| 9000 | `put-user` | `p` with pubkey hex and optional roles |
130| 9001 | `remove-user` | `p` (pubkey hex) | 128| 9001 | `remove-user` | `p` with pubkey hex |
131| 9002 | `edit-metadata` | `name`, `about`, `picture` (string) | 129| 9002 | `edit-metadata` | fields from `kind:39000` to be modified |
132| 9003 | `add-permission` | `p` (pubkey), `permission` (name) | 130| 9005 | `delete-event` | `e` with event id hex |
133| 9004 | `remove-permission` | `p` (pubkey), `permission` (name) | 131| 9007 | `create-group` | |
134| 9005 | `delete-event` | `e` (id hex) | 132| 9008 | `delete-group` | |
135| 9006 | `edit-group-status` | `public` or `private`, `open` or `closed` | 133| 9009 | `create-invite` | |
136| 9007 | `create-group` | | 134
137| 9008 | `delete-group` | | 135It's expected that the group state (of who is an allowed member or not, who is an admin and with which permission or not, what are the group name and picture etc) can be fully reconstructed from the canonical sequence of these events.
136
137### Group metadata events
138
139These events contain the group id in a `d` tag instead of the `h` tag. They MUST be created by the relay master key only and a single instance of each (or none) should exist at all times for each group. They are merely informative but should reflect the latest group state (as it was changed by moderation events over time).
138 140
139- *group metadata* (`kind:39000`) (optional) 141- *group metadata* (`kind:39000`) (optional)
140 142
@@ -142,6 +144,8 @@ This event defines the metadata for the group -- basically how clients should di
142 144
143If the group is forked and hosted in multiple relays, there will be multiple versions of this event in each different relay and so on. 145If the group is forked and hosted in multiple relays, there will be multiple versions of this event in each different relay and so on.
144 146
147When this event is not found, clients may still connect to the group, but treat it as having a different status, `unmanaged`,
148
145```jsonc 149```jsonc
146{ 150{
147 "kind": 39000, 151 "kind": 39000,
@@ -162,41 +166,29 @@ If the group is forked and hosted in multiple relays, there will be multiple ver
162 166
163- *group admins* (`kind:39001`) (optional) 167- *group admins* (`kind:39001`) (optional)
164 168
165Similar to the group metadata, this event is supposed to be generated by relays that host the group. 169Each admin is listed along with one or more roles. These roles SHOULD have a correspondence with the roles supported by the relay, as advertised by the `kind:39003` event.
166 170
167Each admin gets a label that is only used for display purposes, and a list of permissions it has are listed afterwards. These permissions can inform client building UI, but ultimately are evaluated by the relay in order to become effective. 171```jsonc
168
169The list of capabilities, as defined by this NIP, for now, is the following:
170
171- `add-user`
172- `edit-metadata`
173- `delete-event`
174- `remove-user`
175- `add-permission`
176- `remove-permission`
177- `edit-group-status`
178- `delete-group`
179
180```json
181{ 172{
182 "kind": 39001, 173 "kind": 39001,
183 "content": "list of admins for the pizza lovers group", 174 "content": "list of admins for the pizza lovers group",
184 "tags": [ 175 "tags": [
185 ["d", "<group-id>"], 176 ["d", "<group-id>"],
186 ["p", "<pubkey1-as-hex>", "ceo", "add-user", "edit-metadata", "delete-event", "remove-user"], 177 ["p", "<pubkey1-as-hex>", "ceo"],
187 ["p", "<pubkey2-as-hex>", "secretary", "add-user", "delete-event"] 178 ["p", "<pubkey2-as-hex>", "secretary", "gardener"],
188 ] 179 // other pubkeys...
180 ],
189 // other fields... 181 // other fields...
190} 182}
191``` 183```
192 184
193- *group members* (`kind:39002`) (optional) 185- *group members* (`kind:39002`) (optional)
194 186
195Similar to *group admins*, this event is supposed to be generated by relays that host the group. 187It's a list of pubkeys that are members of the group. Relays might choose to not to publish this information, to restrict what pubkeys can fetch it or to only display a subset of the members in it.
196 188
197It's a NIP-51-like list of pubkeys that are members of the group. Relays might choose to not to publish this information or to restrict what pubkeys can fetch it. 189Clients should not assume this will always be present or that it will contain a full list of members.
198 190
199```json 191```jsonc
200{ 192{
201 "kind": 39002, 193 "kind": 39002,
202 "content": "list of members for the pizza lovers group", 194 "content": "list of members for the pizza lovers group",
@@ -205,10 +197,48 @@ It's a NIP-51-like list of pubkeys that are members of the group. Relays might c
205 ["p", "<admin1>"], 197 ["p", "<admin1>"],
206 ["p", "<member-pubkey1>"], 198 ["p", "<member-pubkey1>"],
207 ["p", "<member-pubkey2>"], 199 ["p", "<member-pubkey2>"],
208 ] 200 // other pubkeys...
201 ],
202 // other fields...
209} 203}
210``` 204```
211 205
212## Storing the list of groups a user belongs to 206- *group roles* (`kind:39003`) (optional)
207
208This is an event that MAY be published by the relay informing users and clients about what are the roles supported by this relay according to its internal logic.
209
210For example, a relay may choose to support the roles `"admin"` and `"moderator"`, in which the `"admin"` will be allowed to edit the group metadata, delete messages and remove users from the group, while the `"moderator"` can only delete messages (or the relay may choose to call these roles `"ceo"` and `"secretary"` instead, the exact role name is not relevant).
211
212The process through which the relay decides what roles to support and how to handle moderation events internally based on them is specific to each relay and not specified here.
213
214```jsonc
215{
216 "kind": 39003,
217 "content": "list of roles supported by this group",
218 "tags": [
219 ["d", "<group-id>"],
220 ["role", "<role-name>", "<optional-description>"],
221 ["role", "<role-name>", "<optional-description>"],
222 // other roles...
223 ],
224 // other fields...
225}
226```
227
228## Implementation quirks
229
230### Checking your own membership in a group
231
232The latest of either `kind:9000` or `kind:9001` events present in a group should tell a user that they are currently members of the group or if they were removed. In case none of these exist the user is assumed to not be a member of the group -- unless the group is `unmanaged`, in which case the user is assumed to be a member.
233
234### Adding yourself to a group
235
236When a group is `open`, anyone can send a `kind:9021` event to it in order to be added, then expect a `kind:9000` event to be emitted confirming that the user was added. The same happens with `closed` groups, except in that case a user may only send a `kind:9021` if it has an invite code.
237
238### Storing your list of groups
239
240A definition for `kind:10009` was included in [NIP-51](51.md) that allows clients to store the list of groups a user wants to remember being in.
241
242### Using `unmanaged` relays
213 243
214A definition for kind `10009` was included in [NIP-51](51.md) that allows clients to store the list of groups a user wants to remember being in. 244To prevent event leakage, when using `unmanaged` relays, clients should include the [NIP-70](70.md) `-` tag, as just the `previous` tag won't be checked by other `unmanaged` relays.
diff --git a/46.md b/46.md
index e1675b0..824059c 100644
--- a/46.md
+++ b/46.md
@@ -4,6 +4,10 @@ NIP-46
4Nostr Remote Signing 4Nostr Remote Signing
5-------------------- 5--------------------
6 6
7## Changes
8
9`remote-signer-key` is introduced, passed in bunker url, clients must differentiate between `remote-signer-pubkey` and `user-pubkey`, must call `get_public_key` after connect, nip05 login is removed, create_account moved to another NIP.
10
7## Rationale 11## Rationale
8 12
9Private keys should be exposed to as few systems - apps, operating systems, devices - as possible as each system adds to the attack surface. 13Private keys should be exposed to as few systems - apps, operating systems, devices - as possible as each system adds to the attack surface.
@@ -12,102 +16,70 @@ This NIP describes a method for 2-way communication between a remote signer and
12 16
13## Terminology 17## Terminology
14 18
15- **Local keypair**: A local public and private key-pair used to encrypt content and communicate with the remote signer. Usually created by the client application. 19- **user**: A person that is trying to use Nostr.
16- **Remote user pubkey**: The public key that the user wants to sign as. The remote signer has control of the private key that matches this public key. 20- **client**: A user-facing application that _user_ is looking at and clicking buttons in. This application will send requests to _remote-signer_.
17- **Remote signer pubkey**: This is the public key of the remote signer itself. This is needed in both `create_account` command because you don't yet have a remote user pubkey. 21- **remote-signer**: A daemon or server running somewhere that will answer requests from _client_, also known as "bunker".
22- **client-keypair/pubkey**: The keys generated by _client_. Used to encrypt content and communicate with _remote-signer_.
23- **remote-signer-keypair/pubkey**: The keys used by _remote-signer_ to encrypt content and communicate with _client_. This keypair MAY be same as _user-keypair_, but not necessarily.
24- **user-keypair/pubkey**: The actual keys representing _user_ (that will be used to sign events in response to `sign_event` requests, for example). The _remote-signer_ generally has control over these keys.
18 25
19All pubkeys specified in this NIP are in hex format. 26All pubkeys specified in this NIP are in hex format.
20 27
21## Initiating a connection 28## Overview
22
23To initiate a connection between a client and a remote signer there are a few different options.
24
25### Direct connection initiated by remote signer
26
27This is most common in a situation where you have your own nsecbunker or other type of remote signer and want to connect through a client that supports remote signing.
28 29
29The remote signer would provide a connection token in the form: 301. _client_ generates `client-keypair`. This keypair doesn't need to be communicated to _user_ since it's largely disposable. _client_ might choose to store it locally and they should delete it on logout;
312. A connection is established (see below), _remote-signer_ learns `client-pubkey`, _client_ learns `remote-signer-pubkey`.
323. _client_ uses `client-keypair` to send requests to _remote-signer_ by `p`-tagging and encrypting to `remote-signer-pubkey`;
334. _remote-signer_ responds to _client_ by `p`-tagging and encrypting to the `client-pubkey`.
345. _client_ requests `get_public_key` to learn `user-pubkey`.
30 35
31``` 36## Initiating a connection
32bunker://<remote-user-pubkey>?relay=<wss://relay-to-connect-on>&relay=<wss://another-relay-to-connect-on>&secret=<optional-secret-value>
33```
34 37
35This token is pasted into the client by the user and the client then uses the details to connect to the remote signer via the specified relay(s). Optional secret can be used for single successfully established connection only, remote signer SHOULD ignore new attempts to establish connection with old optional secret. 38There are two ways to initiate a connection:
36 39
37### Direct connection initiated by the client 40### Direct connection initiated by _remote-signer_
38 41
39In this case, basically the opposite direction of the first case, the client provides a connection token (or encodes the token in a QR code) and the signer initiates a connection to the client via the specified relay(s). 42_remote-signer_ provides connection token in the form:
40 43
41``` 44```
42nostrconnect://<local-keypair-pubkey>?relay=<wss://relay-to-connect-on>&metadata=<json metadata in the form: {"name":"...", "url": "...", "description": "..."}> 45bunker://<remote-signer-pubkey>?relay=<wss://relay-to-connect-on>&relay=<wss://another-relay-to-connect-on>&secret=<optional-secret-value>
43``` 46```
44 47
45## The flow 48_user_ passes this token to _client_, which then sends `connect` request to _remote-signer_ via the specified relays. Optional secret can be used for single successfully established connection only, _remote-signer_ SHOULD ignore new attempts to establish connection with old secret.
46 49
471. Client creates a local keypair. This keypair doesn't need to be communicated to the user since it's largely disposable (i.e. the user doesn't need to see this pubkey). Clients might choose to store it locally and they should delete it when the user logs out. 50### Direct connection initiated by the _client_
482. Client gets the remote user pubkey (either via a `bunker://` connection string or a NIP-05 login-flow; shown below)
493. Clients use the local keypair to send requests to the remote signer by `p`-tagging and encrypting to the remote user pubkey.
504. The remote signer responds to the client by `p`-tagging and encrypting to the local keypair pubkey.
51 51
52### Example flow for signing an event 52_client_ provides a connection token using `nostrconnect://` as the protocol, and `client-pubkey` as the origin. Additional information should be passed as query parameters:
53 53
54- Remote user pubkey (e.g. signing as) `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52` 54- `relay` (required) - one or more relay urls on which the _client_ is listening for responses from the _remote-signer_.
55- Local pubkey is `eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86` 55- `secret` (required) - a short random string that the _remote-signer_ should return as the `result` field of its response.
56- `perms` (optional) - a comma-separated list of permissions the _client_ is requesting be approved by the _remote-signer_
57- `name` (optional) - the name of the _client_ application
58- `url` (optional) - the canonical url of the _client_ application
59- `image` (optional) - a small image representing the _client_ application
56 60
57#### Signature request 61Here's an example:
58 62
59```json
60{
61 "kind": 24133,
62 "pubkey": "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86",
63 "content": nip04({
64 "id": <random_string>,
65 "method": "sign_event",
66 "params": [json_stringified(<{
67 content: "Hello, I'm signing remotely",
68 kind: 1,
69 tags: [],
70 created_at: 1714078911
71 }>)]
72 }),
73 "tags": [["p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"]], // p-tags the remote user pubkey
74}
75``` 63```
76 64nostrconnect://83f3b2ae6aa368e8275397b9c26cf550101d63ebaab900d19dd4a4429f5ad8f5?relay=wss%3A%2F%2Frelay1.example.com&perms=nip44_encrypt%2Cnip44_decrypt%2Csign_event%3A13%2Csign_event%3A14%2Csign_event%3A1059&name=My+Client&secret=0s8j2djs&relay=wss%3A%2F%2Frelay2.example2.com
77#### Response event
78
79```json
80{
81 "kind": 24133,
82 "pubkey": "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52",
83 "content": nip04({
84 "id": <random_string>,
85 "result": json_stringified(<signed-event>)
86 }),
87 "tags": [["p", "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86"]], // p-tags the local keypair pubkey
88}
89``` 65```
90 66
91#### Diagram 67_user_ passes this token to _remote-signer_, which then sends `connect` *response* event to the `client-pubkey` via the specified relays. Client discovers `remote-signer-pubkey` from connect response author. `secret` value MUST be provided to avoid connection spoofing, _client_ MUST validate the `secret` returned by `connect` response.
92
93![signing-example](https://i.nostr.build/P3gW.png)
94 68
95## Request Events `kind: 24133` 69## Request Events `kind: 24133`
96 70
97```jsonc 71```jsonc
98{ 72{
99 "id": <id>,
100 "kind": 24133, 73 "kind": 24133,
101 "pubkey": <local_keypair_pubkey>, 74 "pubkey": <local_keypair_pubkey>,
102 "content": <nip04(<request>)>, 75 "content": <nip04(<request>)>,
103 "tags": [["p", <remote_user_pubkey>]], // NB: in the `create_account` event, the remote signer pubkey should be `p` tagged. 76 "tags": [["p", <remote-signer-pubkey>]],
104 "created_at": <unix timestamp in seconds>
105} 77}
106``` 78```
107 79
108The `content` field is a JSON-RPC-like message that is [NIP-04](04.md) encrypted and has the following structure: 80The `content` field is a JSON-RPC-like message that is [NIP-04](04.md) encrypted and has the following structure:
109 81
110```json 82```jsonc
111{ 83{
112 "id": <random_string>, 84 "id": <random_string>,
113 "method": <method_name>, 85 "method": <method_name>,
@@ -121,15 +93,15 @@ The `content` field is a JSON-RPC-like message that is [NIP-04](04.md) encrypted
121 93
122### Methods/Commands 94### Methods/Commands
123 95
124Each of the following are methods that the client sends to the remote signer. 96Each of the following are methods that the _client_ sends to the _remote-signer_.
125 97
126| Command | Params | Result | 98| Command | Params | Result |
127| ------------------------ | ------------------------------------------------- | ---------------------------------------------------------------------- | 99| ------------------------ | ------------------------------------------------- | ---------------------------------------------------------------------- |
128| `connect` | `[<remote_user_pubkey>, <optional_secret>, <optional_requested_permissions>]` | "ack" | 100| `connect` | `[<remote-signer-pubkey>, <optional_secret>, <optional_requested_permissions>]` | "ack" OR `<required-secret-value>` |
129| `sign_event` | `[<{kind, content, tags, created_at}>]` | `json_stringified(<signed_event>)` | 101| `sign_event` | `[<{kind, content, tags, created_at}>]` | `json_stringified(<signed_event>)` |
130| `ping` | `[]` | "pong" | 102| `ping` | `[]` | "pong" |
131| `get_relays` | `[]` | `json_stringified({<relay_url>: {read: <boolean>, write: <boolean>}})` | 103| `get_relays` | `[]` | `json_stringified({<relay_url>: {read: <boolean>, write: <boolean>}})` |
132| `get_public_key` | `[]` | `<hex-pubkey>` | 104| `get_public_key` | `[]` | `<user-pubkey>` |
133| `nip04_encrypt` | `[<third_party_pubkey>, <plaintext_to_encrypt>]` | `<nip04_ciphertext>` | 105| `nip04_encrypt` | `[<third_party_pubkey>, <plaintext_to_encrypt>]` | `<nip04_ciphertext>` |
134| `nip04_decrypt` | `[<third_party_pubkey>, <nip04_ciphertext_to_decrypt>]` | `<plaintext>` | 106| `nip04_decrypt` | `[<third_party_pubkey>, <nip04_ciphertext_to_decrypt>]` | `<plaintext>` |
135| `nip44_encrypt` | `[<third_party_pubkey>, <plaintext_to_encrypt>]` | `<nip44_ciphertext>` | 107| `nip44_encrypt` | `[<third_party_pubkey>, <plaintext_to_encrypt>]` | `<nip44_ciphertext>` |
@@ -137,7 +109,7 @@ Each of the following are methods that the client sends to the remote signer.
137 109
138### Requested permissions 110### Requested permissions
139 111
140The `connect` method may be provided with `optional_requested_permissions` for user convenience. The permissions are a comma-separated list of `method[:params]`, i.e. `nip04_encrypt,sign_event:4` meaning permissions to call `nip04_encrypt` and to call `sign_event` with `kind:4`. Optional parameter for `sign_event` is the kind number, parameters for other methods are to be defined later. 112The `connect` method may be provided with `optional_requested_permissions` for user convenience. The permissions are a comma-separated list of `method[:params]`, i.e. `nip04_encrypt,sign_event:4` meaning permissions to call `nip04_encrypt` and to call `sign_event` with `kind:4`. Optional parameter for `sign_event` is the kind number, parameters for other methods are to be defined later. Same permission format may be used for `perms` field of `metadata` in `nostrconnect://` string.
141 113
142## Response Events `kind:24133` 114## Response Events `kind:24133`
143 115
@@ -145,9 +117,9 @@ The `connect` method may be provided with `optional_requested_permissions` for u
145{ 117{
146 "id": <id>, 118 "id": <id>,
147 "kind": 24133, 119 "kind": 24133,
148 "pubkey": <remote_signer_pubkey>, 120 "pubkey": <remote-signer-pubkey>,
149 "content": <nip04(<response>)>, 121 "content": <nip04(<response>)>,
150 "tags": [["p", <local_keypair_pubkey>]], 122 "tags": [["p", <client-pubkey>]],
151 "created_at": <unix timestamp in seconds> 123 "created_at": <unix timestamp in seconds>
152} 124}
153``` 125```
@@ -166,66 +138,90 @@ The `content` field is a JSON-RPC-like message that is [NIP-04](04.md) encrypted
166- `results` is a string of the result of the call (this can be either a string or a JSON stringified object) 138- `results` is a string of the result of the call (this can be either a string or a JSON stringified object)
167- `error`, _optionally_, it is an error in string form, if any. Its presence indicates an error with the request. 139- `error`, _optionally_, it is an error in string form, if any. Its presence indicates an error with the request.
168 140
169### Auth Challenges 141## Example flow for signing an event
170 142
171An Auth Challenge is a response that a remote signer can send back when it needs the user to authenticate via other means. This is currently used in the OAuth-like flow enabled by signers like [Nsecbunker](https://github.com/kind-0/nsecbunkerd/). The response `content` object will take the following form: 143- `remote-signer-pubkey` is `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
144- `user-pubkey` is also `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
145- `client-pubkey` is `eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86`
172 146
173```json 147### Signature request
148
149```jsonc
174{ 150{
175 "id": <request_id>, 151 "kind": 24133,
176 "result": "auth_url", 152 "pubkey": "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86",
177 "error": <URL_to_display_to_end_user> 153 "content": nip04({
154 "id": <random_string>,
155 "method": "sign_event",
156 "params": [json_stringified(<{
157 content: "Hello, I'm signing remotely",
158 kind: 1,
159 tags: [],
160 created_at: 1714078911
161 }>)]
162 }),
163 "tags": [["p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"]], // p-tags the remote-signer-pubkey
178} 164}
179``` 165```
180 166
181Clients should display (in a popup or new tab) the URL from the `error` field and then subscribe/listen for another response from the remote signer (reusing the same request ID). This event will be sent once the user authenticates in the other window (or will never arrive if the user doesn't authenticate). It's also possible to add a `redirect_uri` url parameter to the auth_url, which is helpful in situations when a client cannot open a new window or tab to display the auth challenge. 167### Response event
182
183#### Example event signing request with auth challenge
184
185![signing-example-with-auth-challenge](https://i.nostr.build/W3aj.png)
186
187## Remote Signer Commands
188
189Remote signers might support additional commands when communicating directly with it. These commands follow the same flow as noted above, the only difference is that when the client sends a request event, the `p`-tag is the pubkey of the remote signer itself and the `content` payload is encrypted to the same remote signer pubkey.
190 168
191### Methods/Commands 169```jsonc
192 170{
193Each of the following are methods that the client sends to the remote signer. 171 "kind": 24133,
194 172 "pubkey": "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52",
195| Command | Params | Result | 173 "content": nip04({
196| ---------------- | ------------------------------------------ | ------------------------------------ | 174 "id": <random_string>,
197| `create_account` | `[<username>, <domain>, <optional_email>, <optional_requested_permissions>]` | `<newly_created_remote_user_pubkey>` | 175 "result": json_stringified(<signed-event>)
176 }),
177 "tags": [["p", "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86"]], // p-tags the client-pubkey
178}
179```
198 180
199## Appendix 181### Diagram
200 182
201### NIP-05 Login Flow 183![signing-example](https://i.nostr.build/P3gW.png)
202 184
203Clients might choose to present a more familiar login flow, so users can type a NIP-05 address instead of a `bunker://` string.
204 185
205When the user types a NIP-05 the client: 186## Auth Challenges
206 187
207- Queries the `/.well-known/nostr.json` file from the domain for the NIP-05 address provided to get the user's pubkey (this is the **remote user pubkey**) 188An Auth Challenge is a response that a _remote-signer_ can send back when it needs the _user_ to authenticate via other means. The response `content` object will take the following form:
208- In the same `/.well-known/nostr.json` file, queries for the `nip46` key to get the relays that the remote signer will be listening on.
209- Now the client has enough information to send commands to the remote signer on behalf of the user.
210 189
211### OAuth-like Flow 190```json
191{
192 "id": <request_id>,
193 "result": "auth_url",
194 "error": <URL_to_display_to_end_user>
195}
196```
212 197
213#### Remote signer discovery via NIP-89 198_client_ should display (in a popup or new tab) the URL from the `error` field and then subscribe/listen for another response from the _remote-signer_ (reusing the same request ID). This event will be sent once the user authenticates in the other window (or will never arrive if the user doesn't authenticate).
214 199
215In this last case, most often used to facilitate an OAuth-like signin flow, the client first looks for remote signers that have announced themselves via NIP-89 application handler events. 200### Example event signing request with auth challenge
216 201
217First the client will query for `kind: 31990` events that have a `k` tag of `24133`. 202![signing-example-with-auth-challenge](https://i.nostr.build/W3aj.png)
218 203
219These are generally shown to a user, and once the user selects which remote signer to use and provides the remote user pubkey they want to use (via npub, pubkey, or nip-05 value), the client can initiate a connection. Note that it's on the user to select the remote signer that is actually managing the remote key that they would like to use in this case. If the remote user pubkey is managed on another remote signer, the connection will fail. 204## Appendix
220 205
221In addition, it's important that clients validate that the pubkey of the announced remote signer matches the pubkey of the `_` entry in the `/.well-known/nostr.json` file of the remote signer's announced domain. 206### Announcing _remote-signer_ metadata
222 207
223Clients that allow users to create new accounts should also consider validating the availability of a given username in the namespace of remote signer's domain by checking the `/.well-known/nostr.json` file for existing usernames. Clients can then show users feedback in the UI before sending a `create_account` event to the remote signer and receiving an error in return. Ideally, remote signers would also respond with understandable error messages if a client tries to create an account with an existing username. 208_remote-signer_ MAY publish it's metadata by using [NIP-05](05.md) and [NIP-89](89.md). With NIP-05, a request to `<remote-signer>/.well-known/nostr.json?name=_` MAY return this:
209```jsonc
210{
211 "names":{
212 "_": <remote-signer-app-pubkey>,
213 },
214 "nip46": {
215 "relays": ["wss://relay1","wss://relay2"...],
216 "nostrconnect_url": "https://remote-signer-domain.com/<nostrconnect>"
217 }
218}
219```
224 220
225#### Example Oauth-like flow to create a new user account with Nsecbunker 221The `<remote-signer-app-pubkey>` MAY be used to verify the domain from _remote-signer_'s NIP-89 event (see below). `relays` SHOULD be used to construct a more precise `nostrconnect://` string for the specific `remote-signer`. `nostrconnect_url` template MAY be used to redirect users to _remote-signer_'s connection flow by replacing `<nostrconnect>` placeholder with an actual `nostrconnect://` string.
226 222
227Coming soon... 223### Remote signer discovery via NIP-89
228 224
229## References 225_remote-signer_ MAY publish a NIP-89 `kind: 31990` event with `k` tag of `24133`, which MAY also include one or more `relay` tags and MAY include `nostrconnect_url` tag. The semantics of `relay` and `nostrconnect_url` tags are the same as in the section above.
230 226
231- [NIP-04 - Encryption](04.md) 227_client_ MAY improve UX by discovering _remote-signers_ using their `kind: 31990` events. _client_ MAY then pre-generate `nostrconnect://` strings for the _remote-signers_, and SHOULD in that case verify that `kind: 31990` event's author is mentioned in signer's `nostr.json?name=_` file as `<remote-signer-app-pubkey>`.
diff --git a/47.md b/47.md
index a19230f..d1030a4 100644
--- a/47.md
+++ b/47.md
@@ -8,32 +8,42 @@ Nostr Wallet Connect
8 8
9## Rationale 9## Rationale
10 10
11This NIP describes a way for clients to access a remote Lightning wallet through a standardized protocol. Custodians may implement this, or the user may run a bridge that bridges their wallet/node and the Nostr Wallet Connect protocol. 11This NIP describes a way for clients to access a remote lightning wallet through a standardized protocol. Custodians may implement this, or the user may run a bridge that bridges their wallet/node and the Nostr Wallet Connect protocol.
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 interact with a lightning wallet.
16* **user**: The person using the **client**, and want's to connect their wallet app to their **client**. 16* **user**: The person using the **client**, and wants to connect their wallet 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. 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 18
19## Theory of Operation 19## Theory of Operation
20 1. **Users** who wish 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. 20 1. **Users** who wish to use this NIP to allow **client(s)** to interact with their wallet 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 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. 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** (or the **client** on the user's behalf) wants to interact with the wallet. 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 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. 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 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. 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.
27
28 5. The **wallet service** may send encrypted notifications (kind 23196) of wallet events (such as a received payment) to the **client**.
27 29
28## Events 30## Events
29 31
30There are three event kinds: 32There are four event kinds:
31- `NIP-47 info event`: 13194 33- `NIP-47 info event`: 13194
32- `NIP-47 request`: 23194 34- `NIP-47 request`: 23194
33- `NIP-47 response`: 23195 35- `NIP-47 response`: 23195
36- `NIP-47 notification event`: 23196
37
38### Info Event
39
40The info event should be a replaceable event that is published by the **wallet service** on the relay to indicate which capabilities it supports.
41
42The content should be a plaintext string with the supported capabilities space-separated, eg. `pay_invoice get_balance notifications`.
34 43
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 44If the **wallet service** supports notifications, the info event SHOULD contain a `notifications` tag with the supported notification types space-separated, eg. `payment_received payment_sent`.
36a plaintext string with the supported commands, space-separated, eg. `pay_invoice get_balance`. Only the `pay_invoice` command is described in this NIP, but other commands might be defined in different NIPs. 45
46### Request and Response Events
37 47
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. 48Both 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.
39Optionally, a request can have an `expiration` tag that has a unix timestamp in seconds. If the request is received after this timestamp, it should be ignored. 49Optionally, a request can have an `expiration` tag that has a unix timestamp in seconds. If the request is received after this timestamp, it should be ignored.
@@ -68,6 +78,22 @@ The `result_type` field MUST contain the name of the method that this event is r
68The `error` field MUST contain a `message` field with a human readable error message and a `code` field with the error code if the command was not successful. 78The `error` field MUST contain a `message` field with a human readable error message and a `code` field with the error code if the command was not successful.
69If the command was successful, the `error` field must be null. 79If the command was successful, the `error` field must be null.
70 80
81### Notification Events
82
83The notification event SHOULD contain one `p` tag, the public key of the **user**.
84
85The content of notifications 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:
86
87```jsonc
88{
89 "notification_type": "payment_received", //indicates the structure of the notification field
90 "notification": {
91 "payment_hash": "0123456789abcdef..." // notification-related data
92 }
93}
94```
95
96
71### Error codes 97### Error codes
72- `RATE_LIMITED`: The client is sending commands too fast. It should retry in a few seconds. 98- `RATE_LIMITED`: The client is sending commands too fast. It should retry in a few seconds.
73- `NOT_IMPLEMENTED`: The command is not known or is intentionally not implemented. 99- `NOT_IMPLEMENTED`: The command is not known or is intentionally not implemented.
@@ -120,7 +146,8 @@ Response:
120{ 146{
121 "result_type": "pay_invoice", 147 "result_type": "pay_invoice",
122 "result": { 148 "result": {
123 "preimage": "0123456789abcdef..." // preimage of the payment 149 "preimage": "0123456789abcdef...", // preimage of the payment
150 "fees_paid": 123, // value in msats, optional
124 } 151 }
125} 152}
126``` 153```
@@ -148,14 +175,15 @@ Request:
148Response: 175Response:
149 176
150For every invoice in the request, a separate response event is sent. To differentiate between the responses, each 177For every invoice in the request, a separate response event is sent. To differentiate between the responses, each
151response event contains an `d` tag with the id of the invoice it is responding to, if no id was given, then the 178response event contains a `d` tag with the id of the invoice it is responding to, if no id was given, then the
152payment hash of the invoice should be used. 179payment hash of the invoice should be used.
153 180
154```jsonc 181```jsonc
155{ 182{
156 "result_type": "multi_pay_invoice", 183 "result_type": "multi_pay_invoice",
157 "result": { 184 "result": {
158 "preimage": "0123456789abcdef..." // preimage of the payment 185 "preimage": "0123456789abcdef...", // preimage of the payment
186 "fees_paid": 123, // value in msats, optional
159 } 187 }
160} 188}
161``` 189```
@@ -189,6 +217,7 @@ Response:
189 "result_type": "pay_keysend", 217 "result_type": "pay_keysend",
190 "result": { 218 "result": {
191 "preimage": "0123456789abcdef...", // preimage of the payment 219 "preimage": "0123456789abcdef...", // preimage of the payment
220 "fees_paid": 123, // value in msats, optional
192 } 221 }
193} 222}
194``` 223```
@@ -225,7 +254,8 @@ pubkey should be used.
225{ 254{
226 "result_type": "multi_pay_keysend", 255 "result_type": "multi_pay_keysend",
227 "result": { 256 "result": {
228 "preimage": "0123456789abcdef..." // preimage of the payment 257 "preimage": "0123456789abcdef...", // preimage of the payment
258 "fees_paid": 123, // value in msats, optional
229 } 259 }
230} 260}
231``` 261```
@@ -394,6 +424,59 @@ Response:
394 "block_height": 1, 424 "block_height": 1,
395 "block_hash": "hex string", 425 "block_hash": "hex string",
396 "methods": ["pay_invoice", "get_balance", "make_invoice", "lookup_invoice", "list_transactions", "get_info"], // list of supported methods for this connection 426 "methods": ["pay_invoice", "get_balance", "make_invoice", "lookup_invoice", "list_transactions", "get_info"], // list of supported methods for this connection
427 "notifications": ["payment_received", "payment_sent"], // list of supported notifications for this connection, optional.
428 }
429}
430```
431
432## Notifications
433
434### `payment_received`
435
436Description: A payment was successfully received by the wallet.
437
438Notification:
439```jsonc
440{
441 "notification_type": "payment_received",
442 "notification": {
443 "type": "incoming",
444 "invoice": "string", // encoded invoice
445 "description": "string", // invoice's description, optional
446 "description_hash": "string", // invoice's description hash, optional
447 "preimage": "string", // payment's preimage
448 "payment_hash": "string", // Payment hash for the payment
449 "amount": 123, // value in msats
450 "fees_paid": 123, // value in msats
451 "created_at": unixtimestamp, // invoice/payment creation time
452 "expires_at": unixtimestamp, // invoice expiration time, optional if not applicable
453 "settled_at": unixtimestamp, // invoice/payment settlement time
454 "metadata": {} // generic metadata that can be used to add things like zap/boostagram details for a payer name/comment/etc.
455 }
456}
457```
458
459### `payment_sent`
460
461Description: A payment was successfully sent by the wallet.
462
463Notification:
464```jsonc
465{
466 "notification_type": "payment_sent",
467 "notification": {
468 "type": "outgoing",
469 "invoice": "string", // encoded invoice
470 "description": "string", // invoice's description, optional
471 "description_hash": "string", // invoice's description hash, optional
472 "preimage": "string", // payment's preimage
473 "payment_hash": "string", // Payment hash for the payment
474 "amount": 123, // value in msats
475 "fees_paid": 123, // value in msats
476 "created_at": unixtimestamp, // invoice/payment creation time
477 "expires_at": unixtimestamp, // invoice expiration time, optional if not applicable
478 "settled_at": unixtimestamp, // invoice/payment settlement time
479 "metadata": {} // generic metadata that can be used to add things like zap/boostagram details for a payer name/comment/etc.
397 } 480 }
398} 481}
399``` 482```
@@ -407,3 +490,24 @@ Response:
407 490
408## Using a dedicated relay 491## Using a dedicated relay
409This NIP does not specify any requirements on the type of relays used. However, if the user is using a custodial service it might make sense to use a relay that is hosted by the custodial service. The relay may then enforce authentication to prevent metadata leaks. Not depending on a 3rd party relay would also improve reliability in this case. 492This NIP does not specify any requirements on the type of relays used. However, if the user is using a custodial service it might make sense to use a relay that is hosted by the custodial service. The relay may then enforce authentication to prevent metadata leaks. Not depending on a 3rd party relay would also improve reliability in this case.
493
494## Appendix
495
496### Example NIP-47 info event
497
498```jsonc
499{
500 "id": "df467db0a9f9ec77ffe6f561811714ccaa2e26051c20f58f33c3d66d6c2b4d1c",
501 "pubkey": "c04ccd5c82fc1ea3499b9c6a5c0a7ab627fbe00a0116110d4c750faeaecba1e2",
502 "created_at": 1713883677,
503 "kind": 13194,
504 "tags": [
505 [
506 "notifications",
507 "payment_received payment_sent"
508 ]
509 ],
510 "content": "pay_invoice pay_keysend get_balance get_info make_invoice lookup_invoice list_transactions multi_pay_invoice multi_pay_keysend sign_message notifications",
511 "sig": "31f57b369459b5306a5353aa9e03be7fbde169bc881c3233625605dd12f53548179def16b9fe1137e6465d7e4d5bb27ce81fd6e75908c46b06269f4233c845d8"
512}
513```
diff --git a/51.md b/51.md
index 3792d7f..4c0b1f4 100644
--- a/51.md
+++ b/51.md
@@ -29,7 +29,7 @@ For example, _mute list_ can contain the public keys of spammers and bad actors
29| Public chats | 10005 | [NIP-28](28.md) chat channels the user is in | `"e"` (kind:40 channel definitions) | 29| Public chats | 10005 | [NIP-28](28.md) chat channels the user is in | `"e"` (kind:40 channel definitions) |
30| Blocked relays | 10006 | relays clients should never connect to | `"relay"` (relay URLs) | 30| Blocked relays | 10006 | relays clients should never connect to | `"relay"` (relay URLs) |
31| Search relays | 10007 | relays clients should use when performing search queries | `"relay"` (relay URLs) | 31| Search relays | 10007 | relays clients should use when performing search queries | `"relay"` (relay URLs) |
32| Simple groups | 10009 | [NIP-29](29.md) groups the user is in | `"group"` ([NIP-29](29.md) group ids + mandatory relay URL) | 32| Simple groups | 10009 | [NIP-29](29.md) groups the user is in | `"group"` ([NIP-29](29.md) group id + relay URL), `"r"` for each relay in use |
33| Interests | 10015 | topics a user may be interested in and pointers | `"t"` (hashtags) and `"a"` (kind:30015 interest set) | 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) | 34| Emojis | 10030 | user preferred emojis and pointers to emoji sets | `"emoji"` (see [NIP-30](30.md)) and `"a"` (kind:30030 emoji set) |
35| DM relays | 10050 | Where to receive [NIP-17](17.md) direct messages | `"relay"` (see [NIP-17](17.md)) | 35| DM relays | 10050 | Where to receive [NIP-17](17.md) direct messages | `"relay"` (see [NIP-17](17.md)) |
diff --git a/53.md b/53.md
index ee12fef..c3f15ea 100644
--- a/53.md
+++ b/53.md
@@ -119,4 +119,4 @@ Common use cases include meeting rooms/workshops, watch-together activities, or
119 "content": "Zaps to live streams is beautiful.", 119 "content": "Zaps to live streams is beautiful.",
120 "sig": "997f62ddfc0827c121043074d50cfce7a528e978c575722748629a4137c45b75bdbc84170bedc723ef0a5a4c3daebf1fef2e93f5e2ddb98e5d685d022c30b622" 120 "sig": "997f62ddfc0827c121043074d50cfce7a528e978c575722748629a4137c45b75bdbc84170bedc723ef0a5a4c3daebf1fef2e93f5e2ddb98e5d685d022c30b622"
121} 121}
122```` 122```
diff --git a/55.md b/55.md
index 54dc44a..afca0aa 100644
--- a/55.md
+++ b/55.md
@@ -72,6 +72,35 @@ Set the Signer package name:
72intent.`package` = "com.example.signer" 72intent.`package` = "com.example.signer"
73``` 73```
74 74
75If you are sending multiple intents without awaiting you can add some intent flags to sign all events without opening multiple times the signer
76
77```kotlin
78intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
79```
80
81If you are developing a signer application them you need to add this to your AndroidManifest.xml so clients can use the intent flags above
82
83```kotlin
84android:launchMode="singleTop"
85```
86
87Signer MUST answer multiple permissions with an array of results
88
89```kotlin
90
91val results = listOf(
92 Result(
93 package = signerPackageName,
94 result = eventSignture,
95 id = intentId
96 )
97)
98
99val json = results.toJson()
100
101intent.putExtra("results", json)
102```
103
75Send the Intent: 104Send the Intent:
76 105
77```kotlin 106```kotlin
diff --git a/57.md b/57.md
index b533811..3f55e57 100644
--- a/57.md
+++ b/57.md
@@ -66,7 +66,7 @@ A signed `zap request` event is not published, but is instead sent using a HTTP
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 in javascript: 69This request should return a JSON response with a `pr` key, which is the invoice the sender must pay to finalize their zap. Here is an example flow in javascript:
70 70
71```javascript 71```javascript
72const senderPubkey // The sender's pubkey 72const senderPubkey // The sender's pubkey
diff --git a/59.md b/59.md
index a4fc99d..9bb5845 100644
--- a/59.md
+++ b/59.md
@@ -245,7 +245,7 @@ const rumor = createRumor(
245const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey) 245const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey)
246const wrap = createWrap(seal, recipientPublicKey) 246const wrap = createWrap(seal, recipientPublicKey)
247 247
248// Recipient unwraps with his/her private key. 248// Recipient unwraps with their private key.
249 249
250const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey) 250const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey)
251const unsealedRumor = nip44Decrypt(unwrappedSeal, recipientPrivateKey) 251const unsealedRumor = nip44Decrypt(unwrappedSeal, recipientPrivateKey)
diff --git a/60.md b/60.md
index 2f24841..64cd282 100644
--- a/60.md
+++ b/60.md
@@ -159,7 +159,7 @@ Her client:
159 "proofs": [ 159 "proofs": [
160 { "id": "1", "amount": 1 }, 160 { "id": "1", "amount": 1 },
161 { "id": "2", "amount": 2 }, 161 { "id": "2", "amount": 2 },
162 { "id": "8", "amount": 8 }, 162 { "id": "4", "amount": 8 },
163 ] 163 ]
164 }), 164 }),
165 "tags": [ 165 "tags": [
diff --git a/69.md b/69.md
new file mode 100644
index 0000000..330d6e5
--- /dev/null
+++ b/69.md
@@ -0,0 +1,86 @@
1# NIP-69
2
3## Peer-to-peer Order events
4
5`draft` `optional`
6
7## Abstract
8
9Peer-to-peer (P2P) platforms have seen an upturn in recent years, while having more and more options is positive, in the specific case of p2p, having several options contributes to the liquidity split, meaning sometimes there's not enough assets available for trading. If we combine all these individual solutions into one big pool of orders, it will make them much more competitive compared to centralized systems, where a single authority controls the liquidity.
10
11This NIP defines a simple standard for peer-to-peer order events, which enables the creation of a big liquidity pool for all p2p platforms participating.
12
13## The event
14
15Events are [addressable events](https://github.com/nostr-protocol/nips/blob/master/01.md#kinds) and use `38383` as event kind, a p2p event look like this:
16
17```json
18{
19 "id": "84fad0d29cb3529d789faeff2033e88fe157a48e071c6a5d1619928289420e31",
20 "pubkey": "dbe0b1be7aafd3cfba92d7463edbd4e33b2969f61bd554d37ac56f032e13355a",
21 "created_at": 1702548701,
22 "kind": 38383,
23 "tags": [
24 ["d", "ede61c96-4c13-4519-bf3a-dcf7f1e9d842"],
25 ["k", "sell"],
26 ["f", "VES"],
27 ["s", "pending"],
28 ["amt", "0"],
29 ["fa", "100"],
30 ["pm", "face to face", "bank transfer"],
31 ["premium", "1"],
32 [
33 "rating",
34 "{\"total_reviews\":1,\"total_rating\":3.0,\"last_rating\":3,\"max_rate\":5,\"min_rate\":1}"
35 ],
36 ["source", "https://t.me/p2plightning/xxxxxxx"],
37 ["network", "mainnet"],
38 ["layer", "lightning"],
39 ["name", "Nakamoto"],
40 ["g", "<geohash>"],
41 ["bond", "0"],
42 ["expiration", "1719391096"],
43 ["y", "lnp2pbot"],
44 ["z", "order"]
45 ],
46 "content": "",
47 "sig": "7e8fe1eb644f33ff51d8805c02a0e1a6d034e6234eac50ef7a7e0dac68a0414f7910366204fa8217086f90eddaa37ded71e61f736d1838e37c0b73f6a16c4af2"
48}
49```
50
51## Tags
52
53- `d` < Order ID >: A unique identifier for the order.
54- `k` < Order type >: `sell` or `buy`.
55- `f` < Currency >: The asset being traded, using the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) standard.
56- `s` < Status >: `pending`, `canceled`, `in-progress`, `success`.
57- `amt` < Amount >: The amount of Bitcoin to be traded, the amount is defined in satoshis, if `0` means that the amount of satoshis will be obtained from a public API after the taker accepts the order.
58- `fa` < Fiat amount >: The fiat amount being traded, for range orders two values are expected, the minimum and maximum amount.
59- `pm` < Payment method >: The payment method used for the trade, if the order has multiple payment methods, they should be separated by a comma.
60- `premium` < Premium >: The percentage of the premium the maker is willing to pay.
61- `source` [Source]: The source of the order, it can be a URL that redirects to the order.
62- `rating` [Rating]: The rating of the maker, this document does not define how the rating is calculated, it's up to the platform to define it.
63- `network` < Network >: The network used for the trade, it can be `mainnet`, `testnet`, `signet`, etc.
64- `layer` < Layer >: The layer used for the trade, it can be `onchain`, `lightning`, `liquid`, etc.
65- `name` [Name]: The name of the maker.
66- `g` [Geohash]: The geohash of the operation, it can be useful in a face to face trade.
67- `bond` [Bond]: The bond amount, the bond is a security deposit that both parties must pay.
68- `expiration` < Expiration\>: The expiration date of the order ([NIP-40](https://github.com/nostr-protocol/nips/blob/master/40.md)).
69- `y` < Platform >: The platform that created the order.
70- `z` < Document >: `order`.
71
72Mandatory tags are enclosed with `<tag>`, optional tags are enclosed with `[tag]`.
73
74## Implementations
75
76Currently implemented on the following platforms:
77
78- [Mostro](https://github.com/MostroP2P/mostro)
79- [@lnp2pBot](https://github.com/lnp2pBot/bot)
80- [Robosats](https://github.com/RoboSats/robosats/pull/1362)
81
82## References
83
84- [Mostro protocol specification](https://mostro.network/protocol/)
85- [Messages specification for peer 2 peer NIP proposal](https://github.com/nostr-protocol/nips/blob/8250274a22f4882f621510df0054fd6167c10c9e/31001.md)
86- [n3xB](https://github.com/nobu-maeda/n3xb)
diff --git a/7D.md b/7D.md
new file mode 100644
index 0000000..d8509a6
--- /dev/null
+++ b/7D.md
@@ -0,0 +1,34 @@
1NIP-7D
2======
3
4Threads
5-------
6
7`draft` `optional`
8
9A thread is a `kind 11` event. Threads SHOULD include a `subject` with a summary
10of the thread's topic.
11
12```json
13{
14 "kind": 11,
15 "content": "Good morning",
16 "tags": [
17 ["subject", "GM"]
18 ]
19}
20```
21
22Replies to `kind 11` MUST use [NIP-22](./22.md) `kind 1111` comments. Replies should
23always be to the root `kind 11` to avoid arbitrarily nested reply hierarchies.
24
25```json
26{
27 "kind": 1111,
28 "content": "Cool beans",
29 "tags": [
30 ["K", "11"],
31 ["E", <event-id>, <relay-url>, <pubkey>]
32 ]
33}
34```
diff --git a/86.md b/86.md
new file mode 100644
index 0000000..6f64eee
--- /dev/null
+++ b/86.md
@@ -0,0 +1,90 @@
1NIP-86
2======
3
4Relay Management API
5--------------------
6
7`draft` `optional`
8
9Relays may provide an API for performing management tasks. This is made available as a JSON-RPC-like request-response protocol over HTTP, on the same URI as the relay's websocket.
10
11When a relay receives an HTTP(s) request with a `Content-Type` header of `application/nostr+json+rpc` to a URI supporting WebSocket upgrades, it should parse the request as a JSON document with the following fields:
12
13```json
14{
15 "method": "<method-name>",
16 "params": ["<array>", "<of>", "<parameters>"]
17}
18```
19
20Then it should return a response in the format
21
22```json
23{
24 "result": {"<arbitrary>": "<value>"},
25 "error": "<optional error message, if the call has errored>"
26}
27```
28
29This is the list of **methods** that may be supported:
30
31* `supportedmethods`:
32 - params: `[]`
33 - result: `["<method-name>", "<method-name>", ...]` (an array with the names of all the other supported methods)
34* `banpubkey`:
35 - params: `["<32-byte-hex-public-key>", "<optional-reason>"]`
36 - result: `true` (a boolean always set to `true`)
37* `listbannedpubkeys`:
38 - params: `[]`
39 - result: `[{"pubkey": "<32-byte-hex>", "reason": "<optional-reason>"}, ...]`, an array of objects
40* `allowpubkey`:
41 - params: `["<32-byte-hex-public-key>", "<optional-reason>"]`
42 - result: `true` (a boolean always set to `true`)
43* `listallowedpubkeys`:
44 - params: `[]`
45 - result: `[{"pubkey": "<32-byte-hex>", "reason": "<optional-reason>"}, ...]`, an array of objects
46* `listeventsneedingmoderation`:
47 - params: `[]`
48 - result: `[{"id": "<32-byte-hex>", "reason": "<optional-reason>"}]`, an array of objects
49* `allowevent`:
50 - params: `["<32-byte-hex-event-id>", "<optional-reason>"]`
51 - result: `true` (a boolean always set to `true`)
52* `banevent`:
53 - params: `["<32-byte-hex-event-id>", "<optional-reason>"]`
54 - result: `true` (a boolean always set to `true`)
55* `listbannedevents`:
56 - params: `[]`
57 - result: `[{"id": "<32-byte hex>", "reason": "<optional-reason>"}, ...]`, an array of objects
58* `changerelayname`:
59 - params: `["<new-name>"]`
60 - result: `true` (a boolean always set to `true`)
61* `changerelaydescription`:
62 - params: `["<new-description>"]`
63 - result: `true` (a boolean always set to `true`)
64* `changerelayicon`:
65 - params: `["<new-icon-url>"]`
66 - result: `true` (a boolean always set to `true`)
67* `allowkind`:
68 - params: `[<kind-number>]`
69 - result: `true` (a boolean always set to `true`)
70* `disallowkind`:
71 - params: `[<kind-number>]`
72 - result: `true` (a boolean always set to `true`)
73* `listallowedkinds`:
74 - params: `[]`
75 - result: `[<kind-number>, ...]`, an array of numbers
76* `blockip`:
77 - params: `["<ip-address>", "<optional-reason>"]`
78 - result: `true` (a boolean always set to `true`)
79* `unblockip`:
80 - params: `["<ip-address>"]`
81 - result: `true` (a boolean always set to `true`)
82* `listblockedips`:
83 - params: `[]`
84 - result: `[{"ip": "<ip-address>", "reason": "<optional-reason>"}, ...]`, an array of objects
85
86### Authorization
87
88The request must contain an `Authorization` header with a valid [NIP-98](./98.md) event, except the `payload` tag is required. The `u` tag is the relay URL.
89
90If `Authorization` is not provided or is invalid, the endpoint should return a 401 response.
diff --git a/96.md b/96.md
index 05c1b18..3828e76 100644
--- a/96.md
+++ b/96.md
@@ -323,8 +323,8 @@ Note: HTTP File Storage Server developers may skip this section. This is meant f
323A File Server Preference event is a kind 10096 replaceable event meant to select one or more servers the user wants 323A File Server Preference event is a kind 10096 replaceable event meant to select one or more servers the user wants
324to upload files to. Servers are listed as `server` tags: 324to upload files to. Servers are listed as `server` tags:
325 325
326```json 326```jsonc
327{. 327{
328 "kind": 10096, 328 "kind": 10096,
329 "content": "", 329 "content": "",
330 "tags": [ 330 "tags": [
diff --git a/C7.md b/C7.md
new file mode 100644
index 0000000..0d94f18
--- /dev/null
+++ b/C7.md
@@ -0,0 +1,29 @@
1NIP-C7
2======
3
4Chats
5-----
6
7`draft` `optional`
8
9A chat message is a `kind 9` event.
10
11```json
12{
13 "kind": 9,
14 "content": "GM",
15 "tags": []
16}
17```
18
19A reply to a `kind 9` is an additional `kind 9` which quotes the parent using a `q` tag.
20
21```json
22{
23 "kind": 9,
24 "content": "nostr:nevent1...\nyes",
25 "tags": [
26 ["q", <event-id>, <relay-url>, <pubkey>]
27 ]
28}
29```
diff --git a/README.md b/README.md
index 400f1ec..9ef9fec 100644
--- a/README.md
+++ b/README.md
@@ -40,6 +40,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
40- [NIP-18: Reposts](18.md) 40- [NIP-18: Reposts](18.md)
41- [NIP-19: bech32-encoded entities](19.md) 41- [NIP-19: bech32-encoded entities](19.md)
42- [NIP-21: `nostr:` URI scheme](21.md) 42- [NIP-21: `nostr:` URI scheme](21.md)
43- [NIP-22: Comment](22.md)
43- [NIP-23: Long-form Content](23.md) 44- [NIP-23: Long-form Content](23.md)
44- [NIP-24: Extra metadata fields and tags](24.md) 45- [NIP-24: Extra metadata fields and tags](24.md)
45- [NIP-25: Reactions](25.md) 46- [NIP-25: Reactions](25.md)
@@ -77,6 +78,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
77- [NIP-61: Nutzaps](61.md) 78- [NIP-61: Nutzaps](61.md)
78- [NIP-64: Chess (PGN)](64.md) 79- [NIP-64: Chess (PGN)](64.md)
79- [NIP-65: Relay List Metadata](65.md) 80- [NIP-65: Relay List Metadata](65.md)
81- [NIP-69: Peer-to-peer Order events](69.md)
80- [NIP-70: Protected Events](70.md) 82- [NIP-70: Protected Events](70.md)
81- [NIP-71: Video Events](71.md) 83- [NIP-71: Video Events](71.md)
82- [NIP-72: Moderated Communities](72.md) 84- [NIP-72: Moderated Communities](72.md)
@@ -84,6 +86,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
84- [NIP-75: Zap Goals](75.md) 86- [NIP-75: Zap Goals](75.md)
85- [NIP-78: Application-specific data](78.md) 87- [NIP-78: Application-specific data](78.md)
86- [NIP-84: Highlights](84.md) 88- [NIP-84: Highlights](84.md)
89- [NIP-86: Relay Management API](86.md)
87- [NIP-89: Recommended Application Handlers](89.md) 90- [NIP-89: Recommended Application Handlers](89.md)
88- [NIP-90: Data Vending Machines](90.md) 91- [NIP-90: Data Vending Machines](90.md)
89- [NIP-92: Media Attachments](92.md) 92- [NIP-92: Media Attachments](92.md)
@@ -91,6 +94,8 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
91- [NIP-96: HTTP File Storage Integration](96.md) 94- [NIP-96: HTTP File Storage Integration](96.md)
92- [NIP-98: HTTP Auth](98.md) 95- [NIP-98: HTTP Auth](98.md)
93- [NIP-99: Classified Listings](99.md) 96- [NIP-99: Classified Listings](99.md)
97- [NIP-7D: Threads](7D.md)
98- [NIP-C7: Chats](C7.md)
94 99
95## Event Kinds 100## Event Kinds
96 101
@@ -105,10 +110,10 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
105| `6` | Repost | [18](18.md) | 110| `6` | Repost | [18](18.md) |
106| `7` | Reaction | [25](25.md) | 111| `7` | Reaction | [25](25.md) |
107| `8` | Badge Award | [58](58.md) | 112| `8` | Badge Award | [58](58.md) |
108| `9` | Group Chat Message | [29](29.md) | 113| `9` | Chat Message | [C7](C7.md) |
109| `10` | Group Chat Threaded Reply | [29](29.md) | 114| `10` | Group Chat Threaded Reply | 29 (deprecated) |
110| `11` | Group Thread | [29](29.md) | 115| `11` | Thread | [7D](7D.md) |
111| `12` | Group Thread Reply | [29](29.md) | 116| `12` | Group Thread Reply | 29 (deprecated) |
112| `13` | Seal | [59](59.md) | 117| `13` | Seal | [59](59.md) |
113| `14` | Direct Message | [17](17.md) | 118| `14` | Direct Message | [17](17.md) |
114| `16` | Generic Repost | [18](18.md) | 119| `16` | Generic Repost | [18](18.md) |
@@ -125,6 +130,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
125| `1040` | OpenTimestamps | [03](03.md) | 130| `1040` | OpenTimestamps | [03](03.md) |
126| `1059` | Gift Wrap | [59](59.md) | 131| `1059` | Gift Wrap | [59](59.md) |
127| `1063` | File Metadata | [94](94.md) | 132| `1063` | File Metadata | [94](94.md) |
133| `1111` | Comment | [22](22.md) |
128| `1311` | Live Chat Message | [53](53.md) | 134| `1311` | Live Chat Message | [53](53.md) |
129| `1617` | Patches | [34](34.md) | 135| `1617` | Patches | [34](34.md) |
130| `1621` | Issues | [34](34.md) | 136| `1621` | Issues | [34](34.md) |
@@ -217,6 +223,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
217| `34236` | Short-form Portrait Video Event | [71](71.md) | 223| `34236` | Short-form Portrait Video Event | [71](71.md) |
218| `34550` | Community Definition | [72](72.md) | 224| `34550` | Community Definition | [72](72.md) |
219| `37375` | Cashu Wallet Event | [60](60.md) | 225| `37375` | Cashu Wallet Event | [60](60.md) |
226| `38383` | Peer-to-peer Order events | [69](69.md) |
220| `39000-9` | Group metadata events | [29](29.md) | 227| `39000-9` | Group metadata events | [29](29.md) |
221 228
222[NUD: Custom Feeds]: https://wikifreedia.xyz/cip-01/ 229[NUD: Custom Feeds]: https://wikifreedia.xyz/cip-01/
@@ -258,22 +265,32 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
258 265
259| name | value | other parameters | NIP | 266| name | value | other parameters | NIP |
260| ----------------- | ------------------------------------ | ------------------------------- | -------------------------------------------------- | 267| ----------------- | ------------------------------------ | ------------------------------- | -------------------------------------------------- |
261| `e` | event id (hex) | relay URL, marker, pubkey (hex) | [01](01.md), [10](10.md) |
262| `p` | pubkey (hex) | relay URL, petname | [01](01.md), [02](02.md) |
263| `a` | coordinates to an event | relay URL | [01](01.md) | 268| `a` | coordinates to an event | relay URL | [01](01.md) |
269| `A` | root address | relay URL | [22](22.md) |
264| `d` | identifier | -- | [01](01.md) | 270| `d` | identifier | -- | [01](01.md) |
265| `-` | -- | -- | [70](70.md) | 271| `e` | event id (hex) | relay URL, marker, pubkey (hex) | [01](01.md), [10](10.md) |
272| `E` | root event id | relay URL | [22](22.md) |
273| `f` | currency code | -- | [69](69.md) |
266| `g` | geohash | -- | [52](52.md) | 274| `g` | geohash | -- | [52](52.md) |
267| `h` | group id | -- | [29](29.md) | 275| `h` | group id | -- | [29](29.md) |
268| `i` | external identity | proof, url hint | [39](39.md), [73](73.md) | 276| `i` | external identity | proof, url hint | [35](35.md), [39](39.md), [73](73.md) |
277| `I` | root external identity | -- | [22](22.md) |
269| `k` | kind | -- | [18](18.md), [25](25.md), [72](72.md), [73](73.md) | 278| `k` | kind | -- | [18](18.md), [25](25.md), [72](72.md), [73](73.md) |
279| `K` | root scope | -- | [22](22.md) |
270| `l` | label, label namespace | -- | [32](32.md) | 280| `l` | label, label namespace | -- | [32](32.md) |
271| `L` | label namespace | -- | [32](32.md) | 281| `L` | label namespace | -- | [32](32.md) |
272| `m` | MIME type | -- | [94](94.md) | 282| `m` | MIME type | -- | [94](94.md) |
283| `p` | pubkey (hex) | relay URL, petname | [01](01.md), [02](02.md) |
273| `q` | event id (hex) | relay URL, pubkey (hex) | [18](18.md) | 284| `q` | event id (hex) | relay URL, pubkey (hex) | [18](18.md) |
274| `r` | a reference (URL, etc) | -- | [24](24.md), [25](25.md) | 285| `r` | a reference (URL, etc) | -- | [24](24.md), [25](25.md) |
275| `r` | relay url | marker | [65](65.md) | 286| `r` | relay url | marker | [65](65.md) |
276| `t` | hashtag | -- | [24](24.md), [34](34.md) | 287| `s` | status | -- | [69](69.md) |
288| `t` | hashtag | -- | [24](24.md), [34](34.md), [35](35.md) |
289| `u` | url | -- | [61](61.md), [98](98.md) |
290| `x` | infohash | -- | [35](35.md) |
291| `y` | platform | -- | [69](69.md) |
292| `z` | order number | -- | [69](69.md) |
293| `-` | -- | -- | [70](70.md) |
277| `alt` | summary | -- | [31](31.md) | 294| `alt` | summary | -- | [31](31.md) |
278| `amount` | millisatoshis, stringified | -- | [57](57.md) | 295| `amount` | millisatoshis, stringified | -- | [57](57.md) |
279| `bolt11` | `bolt11` invoice | -- | [57](57.md) | 296| `bolt11` | `bolt11` invoice | -- | [57](57.md) |
@@ -286,6 +303,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
286| `emoji` | shortcode, image URL | -- | [30](30.md) | 303| `emoji` | shortcode, image URL | -- | [30](30.md) |
287| `encrypted` | -- | -- | [90](90.md) | 304| `encrypted` | -- | -- | [90](90.md) |
288| `expiration` | unix timestamp (string) | -- | [40](40.md) | 305| `expiration` | unix timestamp (string) | -- | [40](40.md) |
306| `file` | full path (string) | -- | [35](35.md) |
289| `goal` | event id (hex) | relay URL | [75](75.md) | 307| `goal` | event id (hex) | relay URL | [75](75.md) |
290| `image` | image URL | dimensions in pixels | [23](23.md), [52](52.md), [58](58.md) | 308| `image` | image URL | dimensions in pixels | [23](23.md), [52](52.md), [58](58.md) |
291| `imeta` | inline metadata | -- | [92](92.md) | 309| `imeta` | inline metadata | -- | [92](92.md) |
@@ -304,6 +322,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
304| `summary` | summary | -- | [23](23.md), [52](52.md) | 322| `summary` | summary | -- | [23](23.md), [52](52.md) |
305| `thumb` | badge thumbnail | dimensions in pixels | [58](58.md) | 323| `thumb` | badge thumbnail | dimensions in pixels | [58](58.md) |
306| `title` | article title | -- | [23](23.md) | 324| `title` | article title | -- | [23](23.md) |
325| `tracker` | torrent tracker URL | -- | [35](35.md) |
307| `web` | webpage URL | -- | [34](34.md) | 326| `web` | webpage URL | -- | [34](34.md) |
308| `zap` | pubkey (hex), relay URL | weight | [57](57.md) | 327| `zap` | pubkey (hex), relay URL | weight | [57](57.md) |
309 328