mirror of
https://github.com/nostr-protocol/nips.git
synced 2024-12-13 19:06:24 +00:00
Compare commits
63 Commits
ffb5bc65e6
...
c86b97ff65
Author | SHA1 | Date | |
---|---|---|---|
|
c86b97ff65 | ||
|
a1ca7a194b | ||
|
e942427f8f | ||
|
624b989f9f | ||
|
b35dd7d294 | ||
|
b94ad00ac7 | ||
|
d71887a8e2 | ||
|
33cad5108e | ||
|
b04922586e | ||
|
d857cfb1b8 | ||
|
a2be191ecd | ||
|
936616b3c0 | ||
|
a92d2e2edd | ||
|
6d16019e9e | ||
|
2ac43aa3f1 | ||
|
2776a2aa14 | ||
|
5ec59fd70c | ||
|
33efff81a6 | ||
|
54c0c1352d | ||
|
fe1fe75cae | ||
|
e9f4cf52f5 | ||
|
2ffd8ec363 | ||
|
0352f8487d | ||
|
aa2342bd9d | ||
|
3d1a34de91 | ||
|
6acf1c780a | ||
|
e4cc95ea32 | ||
|
06dc14c9c1 | ||
|
821614aee1 | ||
|
64624a62ea | ||
|
73f65133fc | ||
|
e145577b0b | ||
|
0e44178961 | ||
|
43767e1e53 | ||
|
7976f8ab77 | ||
|
ef1746dd2c | ||
|
03c64a6e95 | ||
|
1e47fd7557 | ||
|
84aeb10d39 | ||
|
36fa8bb66f | ||
|
bbcea0c861 | ||
|
f3244a0903 | ||
|
ac60e1d662 | ||
|
108b631429 | ||
|
8794be6775 | ||
|
dfd2c2b8ca | ||
|
a8dfdff753 | ||
|
eca0a83d09 | ||
|
8f112857a2 | ||
|
3e540a38e2 | ||
|
2838e3bd51 | ||
|
926a51e722 | ||
|
6376fd8c69 | ||
|
b8a5447e26 | ||
|
18bdc0ce8c | ||
|
b58f02925e | ||
|
39154346bb | ||
|
29696eb364 | ||
|
82291c6afd | ||
|
5c7aad212e | ||
|
9517ac6f5d | ||
|
66c5cc637f | ||
|
32b9f3f40f |
2
05.md
2
05.md
@ -10,7 +10,7 @@ On events of kind `0` (`user metadata`) one can specify the key `"nip05"` with a
|
|||||||
|
|
||||||
Upon 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>`.
|
Upon 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>`.
|
||||||
|
|
||||||
The 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.
|
The 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.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
|
57
10.md
57
10.md
@ -10,33 +10,6 @@ On "e" and "p" tags in Text Events (kind 1)
|
|||||||
## Abstract
|
## Abstract
|
||||||
This 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.
|
This 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.
|
||||||
|
|
||||||
## Positional "e" tags (DEPRECATED)
|
|
||||||
>This scheme is in common use; but should be considered deprecated.
|
|
||||||
|
|
||||||
`["e", <event-id>, <relay-url>]` as per NIP-01.
|
|
||||||
|
|
||||||
Where:
|
|
||||||
|
|
||||||
* `<event-id>` is the id of the event being referenced.
|
|
||||||
* `<relay-url>` is the URL of a recommended relay associated with the reference. Many clients treat this field as optional.
|
|
||||||
|
|
||||||
**The positions of the "e" tags within the event denote specific meanings as follows**:
|
|
||||||
|
|
||||||
* No "e" tag: <br>
|
|
||||||
This event is not a reply to, nor does it refer to, any other event.
|
|
||||||
|
|
||||||
* One "e" tag: <br>
|
|
||||||
`["e", <id>]`: The id of the event to which this event is a reply.
|
|
||||||
|
|
||||||
* Two "e" tags: `["e", <root-id>]`, `["e", <reply-id>]` <br>
|
|
||||||
`<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.
|
|
||||||
|
|
||||||
* Many "e" tags: `["e", <root-id>]` `["e", <mention-id>]`, ..., `["e", <reply-id>]`<br>
|
|
||||||
There may be any number of `<mention-ids>`. These are the ids of events which may, or may not be in the reply chain.
|
|
||||||
They are citing from this event. `root-id` and `reply-id` are as above.
|
|
||||||
|
|
||||||
>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.
|
|
||||||
|
|
||||||
## Marked "e" tags (PREFERRED)
|
## Marked "e" tags (PREFERRED)
|
||||||
`["e", <event-id>, <relay-url>, <marker>, <pubkey>]`
|
`["e", <event-id>, <relay-url>, <marker>, <pubkey>]`
|
||||||
|
|
||||||
@ -62,3 +35,33 @@ When replying to a text event E the reply event's "p" tags should contain all of
|
|||||||
|
|
||||||
Example: 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`]
|
Example: 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`]
|
||||||
in no particular order.
|
in no particular order.
|
||||||
|
|
||||||
|
## Deprecated Positional "e" tags
|
||||||
|
|
||||||
|
This scheme is not in common use anymore and is here just to keep backward compatibility with older events on the network.
|
||||||
|
|
||||||
|
Positional `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.
|
||||||
|
|
||||||
|
They use simple `e` tags without any marker.
|
||||||
|
|
||||||
|
`["e", <event-id>, <relay-url>]` as per NIP-01.
|
||||||
|
|
||||||
|
Where:
|
||||||
|
|
||||||
|
* `<event-id>` is the id of the event being referenced.
|
||||||
|
* `<relay-url>` is the URL of a recommended relay associated with the reference. Many clients treat this field as optional.
|
||||||
|
|
||||||
|
**The positions of the "e" tags within the event denote specific meanings as follows**:
|
||||||
|
|
||||||
|
* No "e" tag: <br>
|
||||||
|
This event is not a reply to, nor does it refer to, any other event.
|
||||||
|
|
||||||
|
* One "e" tag: <br>
|
||||||
|
`["e", <id>]`: The id of the event to which this event is a reply.
|
||||||
|
|
||||||
|
* Two "e" tags: `["e", <root-id>]`, `["e", <reply-id>]` <br>
|
||||||
|
`<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.
|
||||||
|
|
||||||
|
* Many "e" tags: `["e", <root-id>]` `["e", <mention-id>]`, ..., `["e", <reply-id>]`<br>
|
||||||
|
There may be any number of `<mention-ids>`. These are the ids of events which may, or may not be in the reply chain.
|
||||||
|
They are citing from this event. `root-id` and `reply-id` are as above.
|
4
11.md
4
11.md
@ -262,7 +262,7 @@ processed by appropriate client software.
|
|||||||
|
|
||||||
Relays that require payments may want to expose their fee schedules.
|
Relays that require payments may want to expose their fee schedules.
|
||||||
|
|
||||||
```json
|
```jsonc
|
||||||
{
|
{
|
||||||
"payments_url": "https://my-relay/payments",
|
"payments_url": "https://my-relay/payments",
|
||||||
"fees": {
|
"fees": {
|
||||||
@ -270,7 +270,7 @@ Relays that require payments may want to expose their fee schedules.
|
|||||||
"subscription": [{ "amount": 5000000, "unit": "msats", "period": 2592000 }],
|
"subscription": [{ "amount": 5000000, "unit": "msats", "period": 2592000 }],
|
||||||
"publication": [{ "kinds": [4], "amount": 100, "unit": "msats" }],
|
"publication": [{ "kinds": [4], "amount": 100, "unit": "msats" }],
|
||||||
},
|
},
|
||||||
...
|
// other fields...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
4
17.md
4
17.md
@ -47,7 +47,7 @@ An optional `subject` tag defines the current name/topic of the conversation. An
|
|||||||
|
|
||||||
Following [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.
|
Following [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.
|
||||||
|
|
||||||
```js
|
```jsonc
|
||||||
{
|
{
|
||||||
"id": "<usual hash>",
|
"id": "<usual hash>",
|
||||||
"pubkey": randomPublicKey,
|
"pubkey": randomPublicKey,
|
||||||
@ -133,7 +133,7 @@ When sending a message to anyone, clients must then connect to the relays in the
|
|||||||
|
|
||||||
This example sends the message `Hola, que tal?` from `nsec1w8udu59ydjvedgs3yv5qccshcj8k05fh3l60k9x57asjrqdpa00qkmr89m` to `nsec12ywtkplvyq5t6twdqwwygavp5lm4fhuang89c943nf2z92eez43szvn4dt`.
|
This example sends the message `Hola, que tal?` from `nsec1w8udu59ydjvedgs3yv5qccshcj8k05fh3l60k9x57asjrqdpa00qkmr89m` to `nsec12ywtkplvyq5t6twdqwwygavp5lm4fhuang89c943nf2z92eez43szvn4dt`.
|
||||||
|
|
||||||
The two final GiftWraps, one to the receiver and the other to the sender, are:
|
The two final GiftWraps, one to the receiver and the other to the sender, respectively, are:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
14
22.md
14
22.md
@ -13,7 +13,7 @@ It uses `kind:1111` with plaintext `.content` (no HTML, Markdown, or other forma
|
|||||||
Comments MUST point to the root scope using uppercase tag names (e.g. `K`, `E`, `A` or `I`)
|
Comments MUST point to the root scope using uppercase tag names (e.g. `K`, `E`, `A` or `I`)
|
||||||
and MUST point to the parent item with lowercase ones (e.g. `k`, `e`, `a` or `i`).
|
and MUST point to the parent item with lowercase ones (e.g. `k`, `e`, `a` or `i`).
|
||||||
|
|
||||||
```js
|
```jsonc
|
||||||
{
|
{
|
||||||
kind: 1111,
|
kind: 1111,
|
||||||
content: '<comment>',
|
content: '<comment>',
|
||||||
@ -56,7 +56,7 @@ If the parent item is an event, a `p` tag set to the parent event's author SHOUL
|
|||||||
|
|
||||||
A comment on a blog post looks like this:
|
A comment on a blog post looks like this:
|
||||||
|
|
||||||
```js
|
```jsonc
|
||||||
{
|
{
|
||||||
kind: 1111,
|
kind: 1111,
|
||||||
content: 'Great blog post!',
|
content: 'Great blog post!',
|
||||||
@ -79,7 +79,7 @@ A comment on a blog post looks like this:
|
|||||||
|
|
||||||
A comment on a [NIP-94](94.md) file looks like this:
|
A comment on a [NIP-94](94.md) file looks like this:
|
||||||
|
|
||||||
```js
|
```jsonc
|
||||||
{
|
{
|
||||||
kind: 1111,
|
kind: 1111,
|
||||||
content: 'Great file!',
|
content: 'Great file!',
|
||||||
@ -100,7 +100,7 @@ A comment on a [NIP-94](94.md) file looks like this:
|
|||||||
|
|
||||||
A reply to a comment looks like this:
|
A reply to a comment looks like this:
|
||||||
|
|
||||||
```js
|
```jsonc
|
||||||
{
|
{
|
||||||
kind: 1111,
|
kind: 1111,
|
||||||
content: 'This is a reply to "Great file!"',
|
content: 'This is a reply to "Great file!"',
|
||||||
@ -121,7 +121,7 @@ A reply to a comment looks like this:
|
|||||||
|
|
||||||
A comment on a website's url looks like this:
|
A comment on a website's url looks like this:
|
||||||
|
|
||||||
```js
|
```jsonc
|
||||||
{
|
{
|
||||||
kind: 1111,
|
kind: 1111,
|
||||||
content: 'Nice article!',
|
content: 'Nice article!',
|
||||||
@ -142,7 +142,7 @@ A comment on a website's url looks like this:
|
|||||||
|
|
||||||
A podcast comment example:
|
A podcast comment example:
|
||||||
|
|
||||||
```js
|
```jsonc
|
||||||
{
|
{
|
||||||
id: "80c48d992a38f9c445b943a9c9f1010b396676013443765750431a9004bdac05",
|
id: "80c48d992a38f9c445b943a9c9f1010b396676013443765750431a9004bdac05",
|
||||||
pubkey: "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111",
|
pubkey: "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111",
|
||||||
@ -164,7 +164,7 @@ A podcast comment example:
|
|||||||
|
|
||||||
A reply to a podcast comment:
|
A reply to a podcast comment:
|
||||||
|
|
||||||
```js
|
```jsonc
|
||||||
{
|
{
|
||||||
kind: 1111,
|
kind: 1111,
|
||||||
content: "I'm replying to the above comment.",
|
content: "I'm replying to the above comment.",
|
||||||
|
2
24.md
2
24.md
@ -6,7 +6,7 @@ Extra metadata fields and tags
|
|||||||
|
|
||||||
`draft` `optional`
|
`draft` `optional`
|
||||||
|
|
||||||
This NIP defines extra optional fields added to events.
|
This 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.
|
||||||
|
|
||||||
kind 0
|
kind 0
|
||||||
======
|
======
|
||||||
|
65
29.md
65
29.md
@ -22,15 +22,13 @@ Relays are supposed to generate the events that describe group metadata and grou
|
|||||||
|
|
||||||
A 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`.
|
A 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`.
|
||||||
|
|
||||||
Group identifiers must be strings restricted to the characters `a-z0-9-_`.
|
Group identifiers must be strings restricted to the characters `a-z0-9-_`, and SHOULD be random in order to avoid name collisions.
|
||||||
|
|
||||||
When encountering just the `<host>` without the `'<group-id>`, clients can choose to connect to the group with id `_`, which is a special top-level group dedicated to relay-local discussions.
|
When 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.
|
||||||
|
|
||||||
Group identifiers in most cases should be random or pseudo-random, as that mitigates message replay confusiong and ensures they can be migrated or forked to other relays easily without risking conflicting with other groups using the same id in these new relays. This isn't a hard rule, as, for example, in `unmanaged` and/or ephemeral relays groups might not want to migrate ever, so they might not care about this. Notably, the `_` relay-local group isn't expected to be migrated ever.
|
|
||||||
|
|
||||||
## The `h` tag
|
## The `h` tag
|
||||||
|
|
||||||
Events 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_.
|
Events 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_.
|
||||||
|
|
||||||
## Timeline references
|
## Timeline references
|
||||||
|
|
||||||
@ -64,52 +62,7 @@ These are the events expected to be found in NIP-29 groups.
|
|||||||
|
|
||||||
### Normal user-created events
|
### Normal user-created events
|
||||||
|
|
||||||
These events generally can be sent by all members of a group and they require the `h` tag to be present so they're attached to a specific group.
|
Groups 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.
|
||||||
|
|
||||||
- *text root note* (`kind:11`)
|
|
||||||
|
|
||||||
This is the basic unit of a "microblog" root text note sent to a group.
|
|
||||||
|
|
||||||
```jsonc
|
|
||||||
"kind": 11,
|
|
||||||
"content": "hello my friends lovers of pizza",
|
|
||||||
"tags": [
|
|
||||||
["h", "<group-id>"],
|
|
||||||
["previous", "<event-id-first-chars>", "<event-id-first-chars>", /*...*/]
|
|
||||||
]
|
|
||||||
// other fields...
|
|
||||||
```
|
|
||||||
|
|
||||||
- *threaded text reply* (`kind:12`)
|
|
||||||
|
|
||||||
This 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:
|
|
||||||
|
|
||||||
* `["e", "<kind-11-root-id>", "", "root"]`
|
|
||||||
* `["e", "<kind-12-event-id>", "", "reply"]`
|
|
||||||
|
|
||||||
- *chat message* (`kind:9`)
|
|
||||||
|
|
||||||
This is the basic unit of a _chat message_ sent to a group.
|
|
||||||
|
|
||||||
```jsonc
|
|
||||||
"kind": 9,
|
|
||||||
"content": "hello my friends lovers of pizza",
|
|
||||||
"tags": [
|
|
||||||
["h", "<group-id>"],
|
|
||||||
["previous", "<event-id-first-chars>", "<event-id-first-chars>", /*...*/]
|
|
||||||
]
|
|
||||||
// other fields...
|
|
||||||
```
|
|
||||||
|
|
||||||
- *chat message threaded reply* (`kind:10`)
|
|
||||||
|
|
||||||
Similar 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).
|
|
||||||
|
|
||||||
`kind:10` SHOULD use NIP-10 markers, just like `kind:12`.
|
|
||||||
|
|
||||||
- other events:
|
|
||||||
|
|
||||||
Groups may also accept other events, like long-form articles, calendar, livestream, market announcements and so on. These should be as defined in their respective NIPs, with the addition of the `h` tag.
|
|
||||||
|
|
||||||
### User-related group management events
|
### User-related group management events
|
||||||
|
|
||||||
@ -154,7 +107,7 @@ These are events expected to be sent by the relay master key or by group admins
|
|||||||
|
|
||||||
Clients 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`).
|
Clients 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`).
|
||||||
|
|
||||||
```json
|
```jsonc
|
||||||
{
|
{
|
||||||
"kind": 90xx,
|
"kind": 90xx,
|
||||||
"content": "optional reason",
|
"content": "optional reason",
|
||||||
@ -169,10 +122,10 @@ Each moderation action uses a different kind and requires different arguments, w
|
|||||||
|
|
||||||
| kind | name | tags |
|
| kind | name | tags |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 9000 | `add-user` | `p` with pubkey hex and optional roles |
|
| 9000 | `put-user` | `p` with pubkey hex and optional roles |
|
||||||
| 9001 | `remove-user` | `p` with pubkey hex |
|
| 9001 | `remove-user` | `p` with pubkey hex |
|
||||||
| 9002 | `edit-metadata` | fields from `kind:39000` to be modified |
|
| 9002 | `edit-metadata` | fields from `kind:39000` to be modified |
|
||||||
| 9005 | `delete-event` | |
|
| 9005 | `delete-event` | `e` with event id hex |
|
||||||
| 9007 | `create-group` | |
|
| 9007 | `create-group` | |
|
||||||
| 9008 | `delete-group` | |
|
| 9008 | `delete-group` | |
|
||||||
| 9009 | `create-invite` | |
|
| 9009 | `create-invite` | |
|
||||||
@ -286,4 +239,6 @@ A definition for `kind:10009` was included in [NIP-51](51.md) that allows client
|
|||||||
|
|
||||||
### Using `unmanaged` relays
|
### Using `unmanaged` relays
|
||||||
|
|
||||||
To prevent event leakage, replay and confusion, 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.
|
To 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.
|
||||||
|
|
||||||
|
Groups MAY be named without relay support by adding a `name` to the corresponding tag in a user's `kind 10009` group list.
|
||||||
|
8
44.md
8
44.md
@ -63,7 +63,7 @@ NIP-44 version 2 has the following design characteristics:
|
|||||||
- SHA256 is used instead of SHA3 or BLAKE because it is already used in nostr. Also BLAKE's speed advantage
|
- SHA256 is used instead of SHA3 or BLAKE because it is already used in nostr. Also BLAKE's speed advantage
|
||||||
is smaller in non-parallel environments.
|
is smaller in non-parallel environments.
|
||||||
- A custom padding scheme is used instead of padmé because it provides better leakage reduction for small messages.
|
- A custom padding scheme is used instead of padmé because it provides better leakage reduction for small messages.
|
||||||
- Base64 encoding is used instead of another compression algorithm because it is widely available, and is already used in nostr.
|
- Base64 encoding is used instead of another encoding algorithm because it is widely available, and is already used in nostr.
|
||||||
|
|
||||||
### Encryption
|
### Encryption
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ NIP-44 version 2 has the following design characteristics:
|
|||||||
- Content must be encoded from UTF-8 into byte array
|
- Content must be encoded from UTF-8 into byte array
|
||||||
- Validate plaintext length. Minimum is 1 byte, maximum is 65535 bytes
|
- Validate plaintext length. Minimum is 1 byte, maximum is 65535 bytes
|
||||||
- Padding format is: `[plaintext_length: u16][plaintext][zero_bytes]`
|
- Padding format is: `[plaintext_length: u16][plaintext][zero_bytes]`
|
||||||
- Padding algorithm is related to powers-of-two, with min padded msg size of 32
|
- Padding algorithm is related to powers-of-two, with min padded msg size of 32bytes
|
||||||
- Plaintext length is encoded in big-endian as first 2 bytes of the padded blob
|
- Plaintext length is encoded in big-endian as first 2 bytes of the padded blob
|
||||||
5. Encrypt padded content
|
5. Encrypt padded content
|
||||||
- Use ChaCha20, with key and nonce from step 3
|
- Use ChaCha20, with key and nonce from step 3
|
||||||
@ -148,8 +148,8 @@ validation rules, refer to BIP-340.
|
|||||||
- `x[i:j]`, where `x` is a byte array and `i, j <= 0` returns a `(j - i)`-byte array with a copy of the
|
- `x[i:j]`, where `x` is a byte array and `i, j <= 0` returns a `(j - i)`-byte array with a copy of the
|
||||||
`i`-th byte (inclusive) to the `j`-th byte (exclusive) of `x`.
|
`i`-th byte (inclusive) to the `j`-th byte (exclusive) of `x`.
|
||||||
- Constants `c`:
|
- Constants `c`:
|
||||||
- `min_plaintext_size` is 1. 1b msg is padded to 32b.
|
- `min_plaintext_size` is 1. 1bytes msg is padded to 32bytes.
|
||||||
- `max_plaintext_size` is 65535 (64kb - 1). It is padded to 65536.
|
- `max_plaintext_size` is 65535 (64kB - 1). It is padded to 65536bytes.
|
||||||
- Functions
|
- Functions
|
||||||
- `base64_encode(string)` and `base64_decode(bytes)` are Base64 ([RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648), with padding)
|
- `base64_encode(string)` and `base64_decode(bytes)` are Base64 ([RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648), with padding)
|
||||||
- `concat` refers to byte array concatenation
|
- `concat` refers to byte array concatenation
|
||||||
|
191
46.md
191
46.md
@ -6,7 +6,7 @@ Nostr Remote Signing
|
|||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
|
|
||||||
`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.
|
`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.
|
||||||
|
|
||||||
## Rationale
|
## Rationale
|
||||||
|
|
||||||
@ -25,6 +25,14 @@ This NIP describes a method for 2-way communication between a remote signer and
|
|||||||
|
|
||||||
All pubkeys specified in this NIP are in hex format.
|
All pubkeys specified in this NIP are in hex format.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
1. _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;
|
||||||
|
2. A connection is established (see below), _remote-signer_ learns `client-pubkey`, _client_ learns `remote-signer-pubkey`.
|
||||||
|
3. _client_ uses `client-keypair` to send requests to _remote-signer_ by `p`-tagging and encrypting to `remote-signer-pubkey`;
|
||||||
|
4. _remote-signer_ responds to _client_ by `p`-tagging and encrypting to the `client-pubkey`.
|
||||||
|
5. _client_ requests `get_public_key` to learn `user-pubkey`.
|
||||||
|
|
||||||
## Initiating a connection
|
## Initiating a connection
|
||||||
|
|
||||||
There are two ways to initiate a connection:
|
There are two ways to initiate a connection:
|
||||||
@ -37,79 +45,39 @@ _remote-signer_ provides connection token in the form:
|
|||||||
bunker://<remote-signer-pubkey>?relay=<wss://relay-to-connect-on>&relay=<wss://another-relay-to-connect-on>&secret=<optional-secret-value>
|
bunker://<remote-signer-pubkey>?relay=<wss://relay-to-connect-on>&relay=<wss://another-relay-to-connect-on>&secret=<optional-secret-value>
|
||||||
```
|
```
|
||||||
|
|
||||||
_user_ pastes this token on _client_, which then uses the details to connect 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 optional secret.
|
_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.
|
||||||
|
|
||||||
### Direct connection initiated by the client
|
### Direct connection initiated by the _client_
|
||||||
|
|
||||||
In this case, basically the opposite direction of the first case, _client_ provides a connection token (or encodes the token in a QR code) and _remote-signer_ initiates a connection via the specified relays.
|
_client_ provides a connection token using `nostrconnect://` as the protocol, and `client-pubkey` as the origin. Additional information should be passed as query parameters:
|
||||||
|
|
||||||
|
- `relay` (required) - one or more relay urls on which the _client_ is listening for responses from the _remote-signer_.
|
||||||
|
- `secret` (required) - a short random string that the _remote-signer_ should return as the `result` field of its response.
|
||||||
|
- `perms` (optional) - a comma-separated list of permissions the _client_ is requesting be approved by the _remote-signer_
|
||||||
|
- `name` (optional) - the name of the _client_ application
|
||||||
|
- `url` (optional) - the canonical url of the _client_ application
|
||||||
|
- `image` (optional) - a small image representing the _client_ application
|
||||||
|
|
||||||
|
Here's an example:
|
||||||
|
|
||||||
```
|
```
|
||||||
nostrconnect://<client-pubkey>?relay=<wss://relay-to-connect-on>&metadata=<json metadata in the form: {"name":"...", "url": "...", "description": "..."}>
|
nostrconnect://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
|
||||||
```
|
```
|
||||||
|
|
||||||
## The flow
|
_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.
|
||||||
|
|
||||||
1. _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;
|
|
||||||
2. _client_ gets `remote-signer-pubkey` (either via a `bunker://` connection string or a NIP-05 login-flow; shown below);
|
|
||||||
3. _client_ use `client-keypair` to send requests to _remote-signer_ by `p`-tagging and encrypting to `remote-signer-pubkey`;
|
|
||||||
4. _remote-signer_ responds to _client_ by `p`-tagging and encrypting to the `client-pubkey`.
|
|
||||||
|
|
||||||
### Example flow for signing an event
|
|
||||||
|
|
||||||
- `remote-signer-pubkey` is `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
|
|
||||||
- `user-pubkey` is also `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
|
|
||||||
- `client-pubkey` is `eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86`
|
|
||||||
|
|
||||||
#### Signature request
|
|
||||||
|
|
||||||
```js
|
|
||||||
{
|
|
||||||
"kind": 24133,
|
|
||||||
"pubkey": "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86",
|
|
||||||
"content": nip04({
|
|
||||||
"id": <random_string>,
|
|
||||||
"method": "sign_event",
|
|
||||||
"params": [json_stringified(<{
|
|
||||||
content: "Hello, I'm signing remotely",
|
|
||||||
kind: 1,
|
|
||||||
tags: [],
|
|
||||||
created_at: 1714078911
|
|
||||||
}>)]
|
|
||||||
}),
|
|
||||||
"tags": [["p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"]], // p-tags the remote-signer-pubkey
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Response event
|
|
||||||
|
|
||||||
```js
|
|
||||||
{
|
|
||||||
"kind": 24133,
|
|
||||||
"pubkey": "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52",
|
|
||||||
"content": nip04({
|
|
||||||
"id": <random_string>,
|
|
||||||
"result": json_stringified(<signed-event>)
|
|
||||||
}),
|
|
||||||
"tags": [["p", "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86"]], // p-tags the client-pubkey
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Diagram
|
|
||||||
|
|
||||||
![signing-example](https://i.nostr.build/P3gW.png)
|
|
||||||
|
|
||||||
## Request Events `kind: 24133`
|
## Request Events `kind: 24133`
|
||||||
|
|
||||||
```js
|
```jsonc
|
||||||
{
|
{
|
||||||
"kind": 24133,
|
"kind": 24133,
|
||||||
"pubkey": <local_keypair_pubkey>,
|
"pubkey": <local_keypair_pubkey>,
|
||||||
"content": <nip04(<request>)>,
|
"content": <nip44(<request>)>,
|
||||||
"tags": [["p", <remote-signer-pubkey>]],
|
"tags": [["p", <remote-signer-pubkey>]],
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `content` field is a JSON-RPC-like message that is [NIP-04](04.md) encrypted and has the following structure:
|
The `content` field is a JSON-RPC-like message that is [NIP-44](44.md) encrypted and has the following structure:
|
||||||
|
|
||||||
```jsonc
|
```jsonc
|
||||||
{
|
{
|
||||||
@ -125,11 +93,11 @@ The `content` field is a JSON-RPC-like message that is [NIP-04](04.md) encrypted
|
|||||||
|
|
||||||
### Methods/Commands
|
### Methods/Commands
|
||||||
|
|
||||||
Each of the following are methods that the client sends to the remote signer.
|
Each of the following are methods that the _client_ sends to the _remote-signer_.
|
||||||
|
|
||||||
| Command | Params | Result |
|
| Command | Params | Result |
|
||||||
| ------------------------ | ------------------------------------------------- | ---------------------------------------------------------------------- |
|
| ------------------------ | ------------------------------------------------- | ---------------------------------------------------------------------- |
|
||||||
| `connect` | `[<user_pubkey>, <optional_secret>, <optional_requested_permissions>]` | "ack" |
|
| `connect` | `[<remote-signer-pubkey>, <optional_secret>, <optional_requested_permissions>]` | "ack" OR `<required-secret-value>` |
|
||||||
| `sign_event` | `[<{kind, content, tags, created_at}>]` | `json_stringified(<signed_event>)` |
|
| `sign_event` | `[<{kind, content, tags, created_at}>]` | `json_stringified(<signed_event>)` |
|
||||||
| `ping` | `[]` | "pong" |
|
| `ping` | `[]` | "pong" |
|
||||||
| `get_relays` | `[]` | `json_stringified({<relay_url>: {read: <boolean>, write: <boolean>}})` |
|
| `get_relays` | `[]` | `json_stringified({<relay_url>: {read: <boolean>, write: <boolean>}})` |
|
||||||
@ -138,11 +106,10 @@ Each of the following are methods that the client sends to the remote signer.
|
|||||||
| `nip04_decrypt` | `[<third_party_pubkey>, <nip04_ciphertext_to_decrypt>]` | `<plaintext>` |
|
| `nip04_decrypt` | `[<third_party_pubkey>, <nip04_ciphertext_to_decrypt>]` | `<plaintext>` |
|
||||||
| `nip44_encrypt` | `[<third_party_pubkey>, <plaintext_to_encrypt>]` | `<nip44_ciphertext>` |
|
| `nip44_encrypt` | `[<third_party_pubkey>, <plaintext_to_encrypt>]` | `<nip44_ciphertext>` |
|
||||||
| `nip44_decrypt` | `[<third_party_pubkey>, <nip44_ciphertext_to_decrypt>]` | `<plaintext>` |
|
| `nip44_decrypt` | `[<third_party_pubkey>, <nip44_ciphertext_to_decrypt>]` | `<plaintext>` |
|
||||||
| `create_account` | `[<username>, <domain>, <optional_email>, <optional_requested_permissions>]` | `<newly_created_user_pubkey>` |
|
|
||||||
|
|
||||||
### Requested permissions
|
### Requested permissions
|
||||||
|
|
||||||
The `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.
|
The `connect` method may be provided with `optional_requested_permissions` for user convenience. The permissions are a comma-separated list of `method[:params]`, i.e. `nip44_encrypt,sign_event:4` meaning permissions to call `nip44_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.
|
||||||
|
|
||||||
## Response Events `kind:24133`
|
## Response Events `kind:24133`
|
||||||
|
|
||||||
@ -151,13 +118,13 @@ The `connect` method may be provided with `optional_requested_permissions` for u
|
|||||||
"id": <id>,
|
"id": <id>,
|
||||||
"kind": 24133,
|
"kind": 24133,
|
||||||
"pubkey": <remote-signer-pubkey>,
|
"pubkey": <remote-signer-pubkey>,
|
||||||
"content": <nip04(<response>)>,
|
"content": <nip44(<response>)>,
|
||||||
"tags": [["p", <client-pubkey>]],
|
"tags": [["p", <client-pubkey>]],
|
||||||
"created_at": <unix timestamp in seconds>
|
"created_at": <unix timestamp in seconds>
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `content` field is a JSON-RPC-like message that is [NIP-04](04.md) encrypted and has the following structure:
|
The `content` field is a JSON-RPC-like message that is [NIP-44](44.md) encrypted and has the following structure:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -171,9 +138,54 @@ The `content` field is a JSON-RPC-like message that is [NIP-04](04.md) encrypted
|
|||||||
- `results` is a string of the result of the call (this can be either a string or a JSON stringified object)
|
- `results` is a string of the result of the call (this can be either a string or a JSON stringified object)
|
||||||
- `error`, _optionally_, it is an error in string form, if any. Its presence indicates an error with the request.
|
- `error`, _optionally_, it is an error in string form, if any. Its presence indicates an error with the request.
|
||||||
|
|
||||||
### Auth Challenges
|
## Example flow for signing an event
|
||||||
|
|
||||||
An 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:
|
- `remote-signer-pubkey` is `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
|
||||||
|
- `user-pubkey` is also `fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52`
|
||||||
|
- `client-pubkey` is `eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86`
|
||||||
|
|
||||||
|
### Signature request
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"kind": 24133,
|
||||||
|
"pubkey": "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86",
|
||||||
|
"content": nip44({
|
||||||
|
"id": <random_string>,
|
||||||
|
"method": "sign_event",
|
||||||
|
"params": [json_stringified(<{
|
||||||
|
content: "Hello, I'm signing remotely",
|
||||||
|
kind: 1,
|
||||||
|
tags: [],
|
||||||
|
created_at: 1714078911
|
||||||
|
}>)]
|
||||||
|
}),
|
||||||
|
"tags": [["p", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"]], // p-tags the remote-signer-pubkey
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Response event
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"kind": 24133,
|
||||||
|
"pubkey": "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52",
|
||||||
|
"content": nip44({
|
||||||
|
"id": <random_string>,
|
||||||
|
"result": json_stringified(<signed-event>)
|
||||||
|
}),
|
||||||
|
"tags": [["p", "eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86"]], // p-tags the client-pubkey
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Diagram
|
||||||
|
|
||||||
|
![signing-example](https://i.nostr.build/P3gW.png)
|
||||||
|
|
||||||
|
|
||||||
|
## Auth Challenges
|
||||||
|
|
||||||
|
An 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:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -183,42 +195,33 @@ An Auth Challenge is a response that a remote signer can send back when it needs
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Clients 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.
|
_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).
|
||||||
|
|
||||||
#### Example event signing request with auth challenge
|
### Example event signing request with auth challenge
|
||||||
|
|
||||||
![signing-example-with-auth-challenge](https://i.nostr.build/W3aj.png)
|
![signing-example-with-auth-challenge](https://i.nostr.build/W3aj.png)
|
||||||
|
|
||||||
## Appendix
|
## Appendix
|
||||||
|
|
||||||
### NIP-05 Login Flow
|
### Announcing _remote-signer_ metadata
|
||||||
|
|
||||||
Clients might choose to present a more familiar login flow, so users can type a NIP-05 address instead of a `bunker://` string.
|
_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:
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"names":{
|
||||||
|
"_": <remote-signer-app-pubkey>,
|
||||||
|
},
|
||||||
|
"nip46": {
|
||||||
|
"relays": ["wss://relay1","wss://relay2"...],
|
||||||
|
"nostrconnect_url": "https://remote-signer-domain.example/<nostrconnect>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
When the user types a NIP-05 the client:
|
The `<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.
|
||||||
|
|
||||||
- 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 `user-pubkey`)
|
### Remote signer discovery via NIP-89
|
||||||
- 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.
|
|
||||||
- Now the client has enough information to send commands to the remote signer on behalf of the user.
|
|
||||||
|
|
||||||
### OAuth-like Flow
|
_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.
|
||||||
|
|
||||||
#### Remote signer discovery via NIP-89
|
_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>`.
|
||||||
|
|
||||||
In 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.
|
|
||||||
|
|
||||||
First the client will query for `kind: 31990` events that have a `k` tag of `24133`.
|
|
||||||
|
|
||||||
These are generally shown to a user, and once the user selects which remote signer to use and provides the `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 `user-keypair` that they would like to use in this case. If the `user-pubkey` is managed on another _remote-signer_ the connection will fail.
|
|
||||||
|
|
||||||
In 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.
|
|
||||||
|
|
||||||
Clients 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.
|
|
||||||
|
|
||||||
#### Example Oauth-like flow to create a new user account with Nsecbunker
|
|
||||||
|
|
||||||
Coming soon...
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [NIP-04 - Encryption](04.md)
|
|
||||||
|
128
47.md
128
47.md
@ -8,32 +8,42 @@ Nostr Wallet Connect
|
|||||||
|
|
||||||
## Rationale
|
## Rationale
|
||||||
|
|
||||||
This 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.
|
This 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.
|
||||||
|
|
||||||
## Terms
|
## Terms
|
||||||
|
|
||||||
* **client**: Nostr app on any platform that wants to pay Lightning invoices.
|
* **client**: Nostr app on any platform that wants to interact with a lightning wallet.
|
||||||
* **user**: The person using the **client**, and want's to connect their wallet app to their **client**.
|
* **user**: The person using the **client**, and wants to connect their wallet to their **client**.
|
||||||
* **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.
|
* **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.
|
||||||
|
|
||||||
## Theory of Operation
|
## Theory of Operation
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
5. The **wallet service** may send encrypted notifications (kind 23196) of wallet events (such as a received payment) to the **client**.
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
|
|
||||||
There are three event kinds:
|
There are four event kinds:
|
||||||
- `NIP-47 info event`: 13194
|
- `NIP-47 info event`: 13194
|
||||||
- `NIP-47 request`: 23194
|
- `NIP-47 request`: 23194
|
||||||
- `NIP-47 response`: 23195
|
- `NIP-47 response`: 23195
|
||||||
|
- `NIP-47 notification event`: 23196
|
||||||
|
|
||||||
The 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
|
### Info Event
|
||||||
a 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.
|
|
||||||
|
The info event should be a replaceable event that is published by the **wallet service** on the relay to indicate which capabilities it supports.
|
||||||
|
|
||||||
|
The content should be a plaintext string with the supported capabilities space-separated, eg. `pay_invoice get_balance notifications`.
|
||||||
|
|
||||||
|
If 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`.
|
||||||
|
|
||||||
|
### Request and Response Events
|
||||||
|
|
||||||
Both 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.
|
Both 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.
|
||||||
Optionally, 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.
|
Optionally, 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
|
|||||||
The `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.
|
The `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.
|
||||||
If the command was successful, the `error` field must be null.
|
If the command was successful, the `error` field must be null.
|
||||||
|
|
||||||
|
### Notification Events
|
||||||
|
|
||||||
|
The notification event SHOULD contain one `p` tag, the public key of the **user**.
|
||||||
|
|
||||||
|
The 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:
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"notification_type": "payment_received", //indicates the structure of the notification field
|
||||||
|
"notification": {
|
||||||
|
"payment_hash": "0123456789abcdef..." // notification-related data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Error codes
|
### Error codes
|
||||||
- `RATE_LIMITED`: The client is sending commands too fast. It should retry in a few seconds.
|
- `RATE_LIMITED`: The client is sending commands too fast. It should retry in a few seconds.
|
||||||
- `NOT_IMPLEMENTED`: The command is not known or is intentionally not implemented.
|
- `NOT_IMPLEMENTED`: The command is not known or is intentionally not implemented.
|
||||||
@ -120,7 +146,8 @@ Response:
|
|||||||
{
|
{
|
||||||
"result_type": "pay_invoice",
|
"result_type": "pay_invoice",
|
||||||
"result": {
|
"result": {
|
||||||
"preimage": "0123456789abcdef..." // preimage of the payment
|
"preimage": "0123456789abcdef...", // preimage of the payment
|
||||||
|
"fees_paid": 123, // value in msats, optional
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -148,14 +175,15 @@ Request:
|
|||||||
Response:
|
Response:
|
||||||
|
|
||||||
For every invoice in the request, a separate response event is sent. To differentiate between the responses, each
|
For every invoice in the request, a separate response event is sent. To differentiate between the responses, each
|
||||||
response event contains an `d` tag with the id of the invoice it is responding to, if no id was given, then the
|
response event contains a `d` tag with the id of the invoice it is responding to, if no id was given, then the
|
||||||
payment hash of the invoice should be used.
|
payment hash of the invoice should be used.
|
||||||
|
|
||||||
```jsonc
|
```jsonc
|
||||||
{
|
{
|
||||||
"result_type": "multi_pay_invoice",
|
"result_type": "multi_pay_invoice",
|
||||||
"result": {
|
"result": {
|
||||||
"preimage": "0123456789abcdef..." // preimage of the payment
|
"preimage": "0123456789abcdef...", // preimage of the payment
|
||||||
|
"fees_paid": 123, // value in msats, optional
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -189,6 +217,7 @@ Response:
|
|||||||
"result_type": "pay_keysend",
|
"result_type": "pay_keysend",
|
||||||
"result": {
|
"result": {
|
||||||
"preimage": "0123456789abcdef...", // preimage of the payment
|
"preimage": "0123456789abcdef...", // preimage of the payment
|
||||||
|
"fees_paid": 123, // value in msats, optional
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -225,7 +254,8 @@ pubkey should be used.
|
|||||||
{
|
{
|
||||||
"result_type": "multi_pay_keysend",
|
"result_type": "multi_pay_keysend",
|
||||||
"result": {
|
"result": {
|
||||||
"preimage": "0123456789abcdef..." // preimage of the payment
|
"preimage": "0123456789abcdef...", // preimage of the payment
|
||||||
|
"fees_paid": 123, // value in msats, optional
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -394,6 +424,59 @@ Response:
|
|||||||
"block_height": 1,
|
"block_height": 1,
|
||||||
"block_hash": "hex string",
|
"block_hash": "hex string",
|
||||||
"methods": ["pay_invoice", "get_balance", "make_invoice", "lookup_invoice", "list_transactions", "get_info"], // list of supported methods for this connection
|
"methods": ["pay_invoice", "get_balance", "make_invoice", "lookup_invoice", "list_transactions", "get_info"], // list of supported methods for this connection
|
||||||
|
"notifications": ["payment_received", "payment_sent"], // list of supported notifications for this connection, optional.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notifications
|
||||||
|
|
||||||
|
### `payment_received`
|
||||||
|
|
||||||
|
Description: A payment was successfully received by the wallet.
|
||||||
|
|
||||||
|
Notification:
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"notification_type": "payment_received",
|
||||||
|
"notification": {
|
||||||
|
"type": "incoming",
|
||||||
|
"invoice": "string", // encoded invoice
|
||||||
|
"description": "string", // invoice's description, optional
|
||||||
|
"description_hash": "string", // invoice's description hash, optional
|
||||||
|
"preimage": "string", // payment's preimage
|
||||||
|
"payment_hash": "string", // Payment hash for the payment
|
||||||
|
"amount": 123, // value in msats
|
||||||
|
"fees_paid": 123, // value in msats
|
||||||
|
"created_at": unixtimestamp, // invoice/payment creation time
|
||||||
|
"expires_at": unixtimestamp, // invoice expiration time, optional if not applicable
|
||||||
|
"settled_at": unixtimestamp, // invoice/payment settlement time
|
||||||
|
"metadata": {} // generic metadata that can be used to add things like zap/boostagram details for a payer name/comment/etc.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `payment_sent`
|
||||||
|
|
||||||
|
Description: A payment was successfully sent by the wallet.
|
||||||
|
|
||||||
|
Notification:
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"notification_type": "payment_sent",
|
||||||
|
"notification": {
|
||||||
|
"type": "outgoing",
|
||||||
|
"invoice": "string", // encoded invoice
|
||||||
|
"description": "string", // invoice's description, optional
|
||||||
|
"description_hash": "string", // invoice's description hash, optional
|
||||||
|
"preimage": "string", // payment's preimage
|
||||||
|
"payment_hash": "string", // Payment hash for the payment
|
||||||
|
"amount": 123, // value in msats
|
||||||
|
"fees_paid": 123, // value in msats
|
||||||
|
"created_at": unixtimestamp, // invoice/payment creation time
|
||||||
|
"expires_at": unixtimestamp, // invoice expiration time, optional if not applicable
|
||||||
|
"settled_at": unixtimestamp, // invoice/payment settlement time
|
||||||
|
"metadata": {} // generic metadata that can be used to add things like zap/boostagram details for a payer name/comment/etc.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -407,3 +490,24 @@ Response:
|
|||||||
|
|
||||||
## Using a dedicated relay
|
## Using a dedicated relay
|
||||||
This 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.
|
This 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.
|
||||||
|
|
||||||
|
## Appendix
|
||||||
|
|
||||||
|
### Example NIP-47 info event
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"id": "df467db0a9f9ec77ffe6f561811714ccaa2e26051c20f58f33c3d66d6c2b4d1c",
|
||||||
|
"pubkey": "c04ccd5c82fc1ea3499b9c6a5c0a7ab627fbe00a0116110d4c750faeaecba1e2",
|
||||||
|
"created_at": 1713883677,
|
||||||
|
"kind": 13194,
|
||||||
|
"tags": [
|
||||||
|
[
|
||||||
|
"notifications",
|
||||||
|
"payment_received payment_sent"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"content": "pay_invoice pay_keysend get_balance get_info make_invoice lookup_invoice list_transactions multi_pay_invoice multi_pay_keysend sign_message notifications",
|
||||||
|
"sig": "31f57b369459b5306a5353aa9e03be7fbde169bc881c3233625605dd12f53548179def16b9fe1137e6465d7e4d5bb27ce81fd6e75908c46b06269f4233c845d8"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
8
51.md
8
51.md
@ -14,7 +14,7 @@ When new items are added to an existing list, clients SHOULD append them to the
|
|||||||
|
|
||||||
## Types of lists
|
## Types of lists
|
||||||
|
|
||||||
## Standard lists
|
### Standard lists
|
||||||
|
|
||||||
Standard lists use normal replaceable events, meaning users may only have a single list of each kind. They have special meaning and clients may rely on them to augment a user's profile or browsing experience.
|
Standard lists use normal replaceable events, meaning users may only have a single list of each kind. They have special meaning and clients may rely on them to augment a user's profile or browsing experience.
|
||||||
|
|
||||||
@ -29,14 +29,14 @@ For example, _mute list_ can contain the public keys of spammers and bad actors
|
|||||||
| Public chats | 10005 | [NIP-28](28.md) chat channels the user is in | `"e"` (kind:40 channel definitions) |
|
| Public chats | 10005 | [NIP-28](28.md) chat channels the user is in | `"e"` (kind:40 channel definitions) |
|
||||||
| Blocked relays | 10006 | relays clients should never connect to | `"relay"` (relay URLs) |
|
| Blocked relays | 10006 | relays clients should never connect to | `"relay"` (relay URLs) |
|
||||||
| Search relays | 10007 | relays clients should use when performing search queries | `"relay"` (relay URLs) |
|
| Search relays | 10007 | relays clients should use when performing search queries | `"relay"` (relay URLs) |
|
||||||
| Simple groups | 10009 | [NIP-29](29.md) groups the user is in | `"group"` ([NIP-29](29.md) group ids + mandatory relay URL) |
|
| Simple groups | 10009 | [NIP-29](29.md) groups the user is in | `"group"` ([NIP-29](29.md) group id + relay URL + optional group name), `"r"` for each relay in use |
|
||||||
| Interests | 10015 | topics a user may be interested in and pointers | `"t"` (hashtags) and `"a"` (kind:30015 interest set) |
|
| Interests | 10015 | topics a user may be interested in and pointers | `"t"` (hashtags) and `"a"` (kind:30015 interest set) |
|
||||||
| Emojis | 10030 | user preferred emojis and pointers to emoji sets | `"emoji"` (see [NIP-30](30.md)) and `"a"` (kind:30030 emoji set) |
|
| Emojis | 10030 | user preferred emojis and pointers to emoji sets | `"emoji"` (see [NIP-30](30.md)) and `"a"` (kind:30030 emoji set) |
|
||||||
| DM relays | 10050 | Where to receive [NIP-17](17.md) direct messages | `"relay"` (see [NIP-17](17.md)) |
|
| DM relays | 10050 | Where to receive [NIP-17](17.md) direct messages | `"relay"` (see [NIP-17](17.md)) |
|
||||||
| Good wiki authors | 10101 | [NIP-54](54.md) user recommended wiki authors | `"p"` (pubkeys) |
|
| Good wiki authors | 10101 | [NIP-54](54.md) user recommended wiki authors | `"p"` (pubkeys) |
|
||||||
| Good wiki relays | 10102 | [NIP-54](54.md) relays deemed to only host useful articles | `"relay"` (relay URLs) |
|
| Good wiki relays | 10102 | [NIP-54](54.md) relays deemed to only host useful articles | `"relay"` (relay URLs) |
|
||||||
|
|
||||||
## Sets
|
### Sets
|
||||||
|
|
||||||
Sets are lists with well-defined meaning that can enhance the functionality and the UI of clients that rely on them. Unlike standard lists, users are expected to have more than one set of each kind, therefore each of them must be assigned a different `"d"` identifier.
|
Sets are lists with well-defined meaning that can enhance the functionality and the UI of clients that rely on them. Unlike standard lists, users are expected to have more than one set of each kind, therefore each of them must be assigned a different `"d"` identifier.
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ Aside from their main identifier, the `"d"` tag, sets can optionally have a `"ti
|
|||||||
| Emoji sets | 30030 | categorized emoji groups | `"emoji"` (see [NIP-30](30.md)) |
|
| Emoji sets | 30030 | categorized emoji groups | `"emoji"` (see [NIP-30](30.md)) |
|
||||||
| Release artifact sets | 30063 | groups of files of a software release | `"e"` (kind:1063 [file metadata](94.md) events), `"i"` (application identifier, typically reverse domain notation), `"version"` |
|
| Release artifact sets | 30063 | groups of files of a software release | `"e"` (kind:1063 [file metadata](94.md) events), `"i"` (application identifier, typically reverse domain notation), `"version"` |
|
||||||
|
|
||||||
## Deprecated standard lists
|
### Deprecated standard lists
|
||||||
|
|
||||||
Some clients have used these lists in the past, but they should work on transitioning to the [standard formats](#standard-lists) above.
|
Some clients have used these lists in the past, but they should work on transitioning to the [standard formats](#standard-lists) above.
|
||||||
|
|
||||||
|
92
68.md
Normal file
92
68.md
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
NIP-68
|
||||||
|
======
|
||||||
|
|
||||||
|
Picture-first feeds
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
`draft` `optional`
|
||||||
|
|
||||||
|
This NIP defines event kind `20` for picture-first clients. Images must be self-contained. They are hosted externally and referenced using `imeta` tags.
|
||||||
|
|
||||||
|
The idea is for this type of event to cater to Nostr clients resembling platforms like Instagram, Flickr, Snapchat, or 9GAG, where the picture itself takes center stage in the user experience.
|
||||||
|
|
||||||
|
## Picture Events
|
||||||
|
|
||||||
|
Picture events contain a `title` tag and description in the `.content`.
|
||||||
|
|
||||||
|
They may contain multiple images to be displayed as a single post.
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"id": <32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>,
|
||||||
|
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
|
||||||
|
"created_at": <Unix timestamp in seconds>,
|
||||||
|
"kind": 20,
|
||||||
|
"content": "<description of post>",
|
||||||
|
"tags": [
|
||||||
|
["title", "<short title of post>"],
|
||||||
|
|
||||||
|
// Picture Data
|
||||||
|
[
|
||||||
|
"imeta",
|
||||||
|
"url https://nostr.build/i/my-image.jpg",
|
||||||
|
"m image/jpeg",
|
||||||
|
"blurhash eVF$^OI:${M{o#*0-nNFxakD-?xVM}WEWB%iNKxvR-oetmo#R-aen$",
|
||||||
|
"dim 3024x4032",
|
||||||
|
"alt A scenic photo overlooking the coast of Costa Rica",
|
||||||
|
"x <sha256 hash as specified in NIP 94>",
|
||||||
|
"fallback https://nostrcheck.me/alt1.jpg",
|
||||||
|
"fallback https://void.cat/alt1.jpg"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"imeta",
|
||||||
|
"url https://nostr.build/i/my-image2.jpg",
|
||||||
|
"m image/jpeg",
|
||||||
|
"blurhash eVF$^OI:${M{o#*0-nNFxakD-?xVM}WEWB%iNKxvR-oetmo#R-aen$",
|
||||||
|
"dim 3024x4032",
|
||||||
|
"alt Another scenic photo overlooking the coast of Costa Rica",
|
||||||
|
"x <sha256 hash as specified in NIP 94>",
|
||||||
|
"fallback https://nostrcheck.me/alt2.jpg",
|
||||||
|
"fallback https://void.cat/alt2.jpg",
|
||||||
|
|
||||||
|
"annotate-user <32-bytes hex of a pubkey>:<posX>:<posY>" // Tag users in specific locations in the picture
|
||||||
|
],
|
||||||
|
|
||||||
|
["content-warning", "<reason>"], // if NSFW
|
||||||
|
|
||||||
|
// Tagged users
|
||||||
|
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>"],
|
||||||
|
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>"],
|
||||||
|
|
||||||
|
// Specify the media type for filters to allow clients to filter by supported kinds
|
||||||
|
["m", "image/jpeg"],
|
||||||
|
|
||||||
|
// Hashes of each image to make them queryable
|
||||||
|
["x", "<sha256>"]
|
||||||
|
|
||||||
|
// Hashtags
|
||||||
|
["t", "<tag>"],
|
||||||
|
["t", "<tag>"],
|
||||||
|
|
||||||
|
// location
|
||||||
|
["location", "<location>"], // city name, state, country
|
||||||
|
["g", "<geohash>"],
|
||||||
|
|
||||||
|
// When text is written in the image, add the tag to represent the language
|
||||||
|
["L", "ISO-639-1"],
|
||||||
|
["l", "en", "ISO-639-1"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `imeta` tag `annotate-user` places a user link in the specific position in the image.
|
||||||
|
|
||||||
|
Only the following media types are accepted:
|
||||||
|
- `image/apng`: Animated Portable Network Graphics (APNG)
|
||||||
|
- `image/avif`: AV1 Image File Format (AVIF)
|
||||||
|
- `image/gif`: Graphics Interchange Format (GIF)
|
||||||
|
- `image/jpeg`: Joint Photographic Expert Group image (JPEG)
|
||||||
|
- `image/png`: Portable Network Graphics (PNG)
|
||||||
|
- `image/webp`: Web Picture format (WEBP)
|
||||||
|
|
||||||
|
Picture events might be used with [NIP-71](71.md)'s kind `34236` to display short vertical videos in the same feed.
|
34
7D.md
Normal file
34
7D.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
NIP-7D
|
||||||
|
======
|
||||||
|
|
||||||
|
Threads
|
||||||
|
-------
|
||||||
|
|
||||||
|
`draft` `optional`
|
||||||
|
|
||||||
|
A thread is a `kind 11` event. Threads SHOULD include a `subject` with a summary
|
||||||
|
of the thread's topic.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kind": 11,
|
||||||
|
"content": "Good morning",
|
||||||
|
"tags": [
|
||||||
|
["subject", "GM"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Replies to `kind 11` MUST use [NIP-22](./22.md) `kind 1111` comments. Replies should
|
||||||
|
always be to the root `kind 11` to avoid arbitrarily nested reply hierarchies.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kind": 1111,
|
||||||
|
"content": "Cool beans",
|
||||||
|
"tags": [
|
||||||
|
["K", "11"],
|
||||||
|
["E", <event-id>, <relay-url>, <pubkey>]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
90
86.md
Normal file
90
86.md
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
NIP-86
|
||||||
|
======
|
||||||
|
|
||||||
|
Relay Management API
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
`draft` `optional`
|
||||||
|
|
||||||
|
Relays 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.
|
||||||
|
|
||||||
|
When 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:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"method": "<method-name>",
|
||||||
|
"params": ["<array>", "<of>", "<parameters>"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then it should return a response in the format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"result": {"<arbitrary>": "<value>"},
|
||||||
|
"error": "<optional error message, if the call has errored>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the list of **methods** that may be supported:
|
||||||
|
|
||||||
|
* `supportedmethods`:
|
||||||
|
- params: `[]`
|
||||||
|
- result: `["<method-name>", "<method-name>", ...]` (an array with the names of all the other supported methods)
|
||||||
|
* `banpubkey`:
|
||||||
|
- params: `["<32-byte-hex-public-key>", "<optional-reason>"]`
|
||||||
|
- result: `true` (a boolean always set to `true`)
|
||||||
|
* `listbannedpubkeys`:
|
||||||
|
- params: `[]`
|
||||||
|
- result: `[{"pubkey": "<32-byte-hex>", "reason": "<optional-reason>"}, ...]`, an array of objects
|
||||||
|
* `allowpubkey`:
|
||||||
|
- params: `["<32-byte-hex-public-key>", "<optional-reason>"]`
|
||||||
|
- result: `true` (a boolean always set to `true`)
|
||||||
|
* `listallowedpubkeys`:
|
||||||
|
- params: `[]`
|
||||||
|
- result: `[{"pubkey": "<32-byte-hex>", "reason": "<optional-reason>"}, ...]`, an array of objects
|
||||||
|
* `listeventsneedingmoderation`:
|
||||||
|
- params: `[]`
|
||||||
|
- result: `[{"id": "<32-byte-hex>", "reason": "<optional-reason>"}]`, an array of objects
|
||||||
|
* `allowevent`:
|
||||||
|
- params: `["<32-byte-hex-event-id>", "<optional-reason>"]`
|
||||||
|
- result: `true` (a boolean always set to `true`)
|
||||||
|
* `banevent`:
|
||||||
|
- params: `["<32-byte-hex-event-id>", "<optional-reason>"]`
|
||||||
|
- result: `true` (a boolean always set to `true`)
|
||||||
|
* `listbannedevents`:
|
||||||
|
- params: `[]`
|
||||||
|
- result: `[{"id": "<32-byte hex>", "reason": "<optional-reason>"}, ...]`, an array of objects
|
||||||
|
* `changerelayname`:
|
||||||
|
- params: `["<new-name>"]`
|
||||||
|
- result: `true` (a boolean always set to `true`)
|
||||||
|
* `changerelaydescription`:
|
||||||
|
- params: `["<new-description>"]`
|
||||||
|
- result: `true` (a boolean always set to `true`)
|
||||||
|
* `changerelayicon`:
|
||||||
|
- params: `["<new-icon-url>"]`
|
||||||
|
- result: `true` (a boolean always set to `true`)
|
||||||
|
* `allowkind`:
|
||||||
|
- params: `[<kind-number>]`
|
||||||
|
- result: `true` (a boolean always set to `true`)
|
||||||
|
* `disallowkind`:
|
||||||
|
- params: `[<kind-number>]`
|
||||||
|
- result: `true` (a boolean always set to `true`)
|
||||||
|
* `listallowedkinds`:
|
||||||
|
- params: `[]`
|
||||||
|
- result: `[<kind-number>, ...]`, an array of numbers
|
||||||
|
* `blockip`:
|
||||||
|
- params: `["<ip-address>", "<optional-reason>"]`
|
||||||
|
- result: `true` (a boolean always set to `true`)
|
||||||
|
* `unblockip`:
|
||||||
|
- params: `["<ip-address>"]`
|
||||||
|
- result: `true` (a boolean always set to `true`)
|
||||||
|
* `listblockedips`:
|
||||||
|
- params: `[]`
|
||||||
|
- result: `[{"ip": "<ip-address>", "reason": "<optional-reason>"}, ...]`, an array of objects
|
||||||
|
|
||||||
|
### Authorization
|
||||||
|
|
||||||
|
The 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.
|
||||||
|
|
||||||
|
If `Authorization` is not provided or is invalid, the endpoint should return a 401 response.
|
2
90.md
2
90.md
@ -185,7 +185,7 @@ Any job feedback event MIGHT include results in the `.content` field, as describ
|
|||||||
* Customer publishes a job request (e.g. `kind:5000` speech-to-text).
|
* Customer publishes a job request (e.g. `kind:5000` speech-to-text).
|
||||||
* Service Providers MAY submit `kind:7000` job-feedback events (e.g. `payment-required`, `processing`, `error`, etc.).
|
* Service Providers MAY submit `kind:7000` job-feedback events (e.g. `payment-required`, `processing`, `error`, etc.).
|
||||||
* Upon completion, the service provider publishes the result of the job with a `kind:6000` job-result event.
|
* Upon completion, the service provider publishes the result of the job with a `kind:6000` job-result event.
|
||||||
* At any point, if there is an `amount` pending to be paid as instructed by the service provider, the user can pay the included `bolt11` or zap the job result event the service provider has sent to the user
|
* At any point, if there is an `amount` pending to be paid as instructed by the service provider, the user can pay the included `bolt11` or zap the job result event the service provider has sent to the user.
|
||||||
|
|
||||||
Job feedback (`kind:7000`) and Job Results (`kind:6000-6999`) events MAY include an `amount` tag, this can be interpreted as a suggestion to pay. Service Providers MUST use the `payment-required` feedback event to signal that a payment is required and no further actions will be performed until the payment is sent.
|
Job feedback (`kind:7000`) and Job Results (`kind:6000-6999`) events MAY include an `amount` tag, this can be interpreted as a suggestion to pay. Service Providers MUST use the `payment-required` feedback event to signal that a payment is required and no further actions will be performed until the payment is sent.
|
||||||
|
|
||||||
|
4
96.md
4
96.md
@ -323,8 +323,8 @@ Note: HTTP File Storage Server developers may skip this section. This is meant f
|
|||||||
A File Server Preference event is a kind 10096 replaceable event meant to select one or more servers the user wants
|
A File Server Preference event is a kind 10096 replaceable event meant to select one or more servers the user wants
|
||||||
to upload files to. Servers are listed as `server` tags:
|
to upload files to. Servers are listed as `server` tags:
|
||||||
|
|
||||||
```json
|
```jsonc
|
||||||
{.
|
{
|
||||||
"kind": 10096,
|
"kind": 10096,
|
||||||
"content": "",
|
"content": "",
|
||||||
"tags": [
|
"tags": [
|
||||||
|
100
BREAKING.md
100
BREAKING.md
@ -5,54 +5,57 @@ reverse chronological order.
|
|||||||
|
|
||||||
| Date | Commit | NIP | Change |
|
| Date | Commit | NIP | Change |
|
||||||
| ----------- | --------- | -------- | ------ |
|
| ----------- | --------- | -------- | ------ |
|
||||||
| 2024-10-15 | [1cda2dcc](https://github.com/nostr-protocol/nips/commit/1cda2dcc) | [NIP-71](71.md) | some tags were replaced with `imeta` tag |
|
| 2024-12-05 | [6d16019e](https://github.com/nostr-protocol/nips/commit/6d16019e) | [46](46.md) | message encryption was changed to NIP-44 |
|
||||||
| 2024-10-15 | [1cda2dcc](https://github.com/nostr-protocol/nips/commit/1cda2dcc) | [NIP-71](71.md) | `kind: 34237` was dropped |
|
| 2024-11-12 | [2838e3bd](https://github.com/nostr-protocol/nips/commit/2838e3bd) | [29](29.md) | `kind: 12` and `kind: 10` were removed (use `kind: 1111` instead) |
|
||||||
| 2024-10-07 | [7bb8997b](https://github.com/nostr-protocol/nips/commit/7bb8997b) | [NIP-55](55.md) | some fields and passing data were changed |
|
| 2024-11-12 | [926a51e7](https://github.com/nostr-protocol/nips/commit/926a51e7) | [46](46.md) | NIP-05 login was removed |
|
||||||
| 2024-08-18 | [3aff37bd](https://github.com/nostr-protocol/nips/commit/3aff37bd) | [NIP-54](54.md) | content should be Asciidoc |
|
| 2024-11-12 | [926a51e7](https://github.com/nostr-protocol/nips/commit/926a51e7) | [46](46.md) | `create_account` method was removed |
|
||||||
| 2024-07-31 | [3ea2f1a4](https://github.com/nostr-protocol/nips/commit/3ea2f1a4) | [NIP-45](45.md) | [444ad28d](https://github.com/nostr-protocol/nips/commit/444ad28d) was reverted |
|
| 2024-11-12 | [926a51e7](https://github.com/nostr-protocol/nips/commit/926a51e7) | [46](46.md) | `connect` params and result were changed |
|
||||||
| 2024-07-30 | [444ad28d](https://github.com/nostr-protocol/nips/commit/444ad28d) | [NIP-45](45.md) | NIP-45 was deprecated |
|
| 2024-10-29 | [f1e8d2c4](https://github.com/nostr-protocol/nips/commit/f1e8d2c4) | [46](46.md) | bunker URL should use `remote-signer-key` |
|
||||||
| 2024-07-26 | [ecee40df](https://github.com/nostr-protocol/nips/commit/ecee40df) | [NIP-19](19.md) | `nrelay` was deprecated |
|
| 2024-10-15 | [1cda2dcc](https://github.com/nostr-protocol/nips/commit/1cda2dcc) | [71](71.md) | some tags were replaced with `imeta` tag |
|
||||||
| 2024-07-23 | [0227a2cd](https://github.com/nostr-protocol/nips/commit/0227a2cd) | [NIP-01](01.md) | events should be sorted by id after created_at |
|
| 2024-10-15 | [1cda2dcc](https://github.com/nostr-protocol/nips/commit/1cda2dcc) | [71](71.md) | `kind: 34237` was dropped |
|
||||||
| 2024-06-06 | [58e94b20](https://github.com/nostr-protocol/nips/commit/58e94b20) | [NIP-25](25.md) | [8073c848](https://github.com/nostr-protocol/nips/commit/8073c848) was reverted |
|
| 2024-10-07 | [7bb8997b](https://github.com/nostr-protocol/nips/commit/7bb8997b) | [55](55.md) | some fields and passing data were changed |
|
||||||
| 2024-06-06 | [a6dfc7b5](https://github.com/nostr-protocol/nips/commit/a6dfc7b5) | [NIP-55](55.md) | NIP number was changed |
|
| 2024-08-18 | [3aff37bd](https://github.com/nostr-protocol/nips/commit/3aff37bd) | [54](54.md) | content should be Asciidoc |
|
||||||
| 2024-05-25 | [5d1d1c17](https://github.com/nostr-protocol/nips/commit/5d1d1c17) | [NIP-71](71.md) | 'aes-256-gcm' tag was removed |
|
| 2024-07-31 | [3ea2f1a4](https://github.com/nostr-protocol/nips/commit/3ea2f1a4) | [45](45.md) | [444ad28d](https://github.com/nostr-protocol/nips/commit/444ad28d) was reverted |
|
||||||
| 2024-05-07 | [8073c848](https://github.com/nostr-protocol/nips/commit/8073c848) | [NIP-25](25.md) | e-tags were changed to not include entire thread |
|
| 2024-07-30 | [444ad28d](https://github.com/nostr-protocol/nips/commit/444ad28d) | [45](45.md) | NIP-45 was deprecated |
|
||||||
| 2024-04-30 | [bad88262](https://github.com/nostr-protocol/nips/commit/bad88262) | [NIP-34](34.md) | 'earliest-unique-commit' tag was removed (use 'r' tag instead) |
|
| 2024-07-26 | [ecee40df](https://github.com/nostr-protocol/nips/commit/ecee40df) | [19](19.md) | `nrelay` was deprecated |
|
||||||
| 2024-02-25 | [4a171cb0](https://github.com/nostr-protocol/nips/commit/4a171cb0) | [NIP-18](18.md) | quote repost should use `q` tag |
|
| 2024-07-23 | [0227a2cd](https://github.com/nostr-protocol/nips/commit/0227a2cd) | [01](01.md) | events should be sorted by id after created_at |
|
||||||
| 2024-02-21 | [c6cd655c](https://github.com/nostr-protocol/nips/commit/c6cd655c) | [NIP-46](46.md) | Params were stringified |
|
| 2024-06-06 | [58e94b20](https://github.com/nostr-protocol/nips/commit/58e94b20) | [25](25.md) | [8073c848](https://github.com/nostr-protocol/nips/commit/8073c848) was reverted |
|
||||||
| 2024-02-16 | [cbec02ab](https://github.com/nostr-protocol/nips/commit/cbec02ab) | [NIP-49](49.md) | Password first normalized to NFKC |
|
| 2024-06-06 | [a6dfc7b5](https://github.com/nostr-protocol/nips/commit/a6dfc7b5) | [55](55.md) | NIP number was changed |
|
||||||
| 2024-02-15 | [afbb8dd0](https://github.com/nostr-protocol/nips/commit/afbb8dd0) | [NIP-39](39.md) | PGP identity was removed |
|
| 2024-05-25 | [5d1d1c17](https://github.com/nostr-protocol/nips/commit/5d1d1c17) | [71](71.md) | 'aes-256-gcm' tag was removed |
|
||||||
| 2024-02-07 | [d3dad114](https://github.com/nostr-protocol/nips/commit/d3dad114) | [NIP-46](46.md) | Connection token format was changed |
|
| 2024-05-07 | [8073c848](https://github.com/nostr-protocol/nips/commit/8073c848) | [25](25.md) | e-tags were changed to not include entire thread |
|
||||||
| 2024-01-30 | [1a2b21b6](https://github.com/nostr-protocol/nips/commit/1a2b21b6) | [NIP-59](59.md) | 'p' tag became optional |
|
| 2024-04-30 | [bad88262](https://github.com/nostr-protocol/nips/commit/bad88262) | [34](34.md) | 'earliest-unique-commit' tag was removed (use 'r' tag instead) |
|
||||||
| 2023-01-27 | [c2f34817](https://github.com/nostr-protocol/nips/commit/c2f34817) | [NIP-47](47.md) | optional expiration tag should be honored |
|
| 2024-02-25 | [4a171cb0](https://github.com/nostr-protocol/nips/commit/4a171cb0) | [18](18.md) | quote repost should use `q` tag |
|
||||||
| 2024-01-10 | [3d8652ea](https://github.com/nostr-protocol/nips/commit/3d8652ea) | [NIP-02](02.md) | list entries should be chronological |
|
| 2024-02-21 | [c6cd655c](https://github.com/nostr-protocol/nips/commit/c6cd655c) | [46](46.md) | Params were stringified |
|
||||||
| 2024-01-10 | [3d8652ea](https://github.com/nostr-protocol/nips/commit/3d8652ea) | [NIP-51](51.md) | list entries should be chronological |
|
| 2024-02-16 | [cbec02ab](https://github.com/nostr-protocol/nips/commit/cbec02ab) | [49](49.md) | Password first normalized to NFKC |
|
||||||
| 2023-12-30 | [29869821](https://github.com/nostr-protocol/nips/commit/29869821) | [NIP-52](52.md) | 'name' tag was removed (use 'title' tag instead) |
|
| 2024-02-15 | [afbb8dd0](https://github.com/nostr-protocol/nips/commit/afbb8dd0) | [39](39.md) | PGP identity was removed |
|
||||||
| 2023-12-27 | [17c67ef5](https://github.com/nostr-protocol/nips/commit/17c67ef5) | [NIP-94](94.md) | 'aes-256-gcm' tag was removed |
|
| 2024-02-07 | [d3dad114](https://github.com/nostr-protocol/nips/commit/d3dad114) | [46](46.md) | Connection token format was changed |
|
||||||
| 2023-12-03 | [0ba45895](https://github.com/nostr-protocol/nips/commit/0ba45895) | [NIP-01](01.md) | WebSocket status code `4000` was replaced by 'CLOSED' message |
|
| 2024-01-30 | [1a2b21b6](https://github.com/nostr-protocol/nips/commit/1a2b21b6) | [59](59.md) | 'p' tag became optional |
|
||||||
| 2023-11-28 | [6de35f9e](https://github.com/nostr-protocol/nips/commit/6de35f9e) | [NIP-89](89.md) | 'client' tag value was changed |
|
| 2023-01-27 | [c2f34817](https://github.com/nostr-protocol/nips/commit/c2f34817) | [47](47.md) | optional expiration tag should be honored |
|
||||||
| 2023-11-20 | [7822a8b1](https://github.com/nostr-protocol/nips/commit/7822a8b1) | [NIP-51](51.md) | `kind: 30000` and `kind: 30001` were deprecated |
|
| 2024-01-10 | [3d8652ea](https://github.com/nostr-protocol/nips/commit/3d8652ea) | [02](02.md), [51](51.md) | list entries should be chronological |
|
||||||
| 2023-11-11 | [cbdca1e9](https://github.com/nostr-protocol/nips/commit/cbdca1e9) | [NIP-84](84.md) | 'range' tag was removed |
|
| 2023-12-30 | [29869821](https://github.com/nostr-protocol/nips/commit/29869821) | [52](52.md) | 'name' tag was removed (use 'title' tag instead) |
|
||||||
| 2023-11-10 | [c945d8bd](https://github.com/nostr-protocol/nips/commit/c945d8bd) | [NIP-32](32.md) | 'l' tag annotations was removed |
|
| 2023-12-27 | [17c67ef5](https://github.com/nostr-protocol/nips/commit/17c67ef5) | [94](94.md) | 'aes-256-gcm' tag was removed |
|
||||||
| 2023-11-07 | [108b7f16](https://github.com/nostr-protocol/nips/commit/108b7f16) | [NIP-01](01.md) | 'OK' message must have 4 items |
|
| 2023-12-03 | [0ba45895](https://github.com/nostr-protocol/nips/commit/0ba45895) | [01](01.md) | WebSocket status code `4000` was replaced by 'CLOSED' message |
|
||||||
| 2023-10-17 | [cf672b76](https://github.com/nostr-protocol/nips/commit/cf672b76) | [NIP-03](03.md) | 'block' tag was removed |
|
| 2023-11-28 | [6de35f9e](https://github.com/nostr-protocol/nips/commit/6de35f9e) | [89](89.md) | 'client' tag value was changed |
|
||||||
| 2023-09-29 | [7dc6385f](https://github.com/nostr-protocol/nips/commit/7dc6385f) | [NIP-57](57.md) | optional 'a' tag was included in `zap receipt` |
|
| 2023-11-20 | [7822a8b1](https://github.com/nostr-protocol/nips/commit/7822a8b1) | [51](51.md) | `kind: 30001` was deprecated |
|
||||||
| 2023-08-21 | [89915e02](https://github.com/nostr-protocol/nips/commit/89915e02) | [NIP-11](11.md) | 'min_prefix' was removed |
|
| 2023-11-20 | [7822a8b1](https://github.com/nostr-protocol/nips/commit/7822a8b1) | [51](51.md) | the meaning of `kind: 30000` was changed |
|
||||||
| 2023-08-20 | [37c4375e](https://github.com/nostr-protocol/nips/commit/37c4375e) | [NIP-01](01.md) | replaceable events with same timestamp should be retained event with lowest id |
|
| 2023-11-11 | [cbdca1e9](https://github.com/nostr-protocol/nips/commit/cbdca1e9) | [84](84.md) | 'range' tag was removed |
|
||||||
| 2023-08-15 | [88ee873c](https://github.com/nostr-protocol/nips/commit/88ee873c) | [NIP-15](15.md) | 'countries' tag was renamed to 'regions' |
|
| 2023-11-10 | [c945d8bd](https://github.com/nostr-protocol/nips/commit/c945d8bd) | [32](32.md) | 'l' tag annotations was removed |
|
||||||
| 2023-08-14 | [72bb8a12](https://github.com/nostr-protocol/nips/commit/72bb8a12) | [NIP-12](12.md) | NIP-12, 16, 20 and 33 were merged into NIP-01 |
|
| 2023-11-07 | [108b7f16](https://github.com/nostr-protocol/nips/commit/108b7f16) | [01](01.md) | 'OK' message must have 4 items |
|
||||||
| 2023-08-14 | [72bb8a12](https://github.com/nostr-protocol/nips/commit/72bb8a12) | [NIP-16](16.md) | NIP-12, 16, 20 and 33 were merged into NIP-01 |
|
| 2023-10-17 | [cf672b76](https://github.com/nostr-protocol/nips/commit/cf672b76) | [03](03.md) | 'block' tag was removed |
|
||||||
| 2023-08-14 | [72bb8a12](https://github.com/nostr-protocol/nips/commit/72bb8a12) | [NIP-20](20.md) | NIP-12, 16, 20 and 33 were merged into NIP-01 |
|
| 2023-09-29 | [7dc6385f](https://github.com/nostr-protocol/nips/commit/7dc6385f) | [57](57.md) | optional 'a' tag was included in `zap receipt` |
|
||||||
| 2023-08-14 | [72bb8a12](https://github.com/nostr-protocol/nips/commit/72bb8a12) | [NIP-33](33.md) | NIP-12, 16, 20 and 33 were merged into NIP-01 |
|
| 2023-08-21 | [89915e02](https://github.com/nostr-protocol/nips/commit/89915e02) | [11](11.md) | 'min_prefix' was removed |
|
||||||
| 2023-08-11 | [d87f8617](https://github.com/nostr-protocol/nips/commit/d87f8617) | [NIP-25](25.md) | empty `content` should be considered as "+" |
|
| 2023-08-20 | [37c4375e](https://github.com/nostr-protocol/nips/commit/37c4375e) | [01](01.md) | replaceable events with same timestamp should be retained event with lowest id |
|
||||||
| 2023-08-01 | [5d63b157](https://github.com/nostr-protocol/nips/commit/5d63b157) | [NIP-57](57.md) | 'zap' tag was changed |
|
| 2023-08-15 | [88ee873c](https://github.com/nostr-protocol/nips/commit/88ee873c) | [15](15.md) | 'countries' tag was renamed to 'regions' |
|
||||||
| 2023-07-15 | [d1814405](https://github.com/nostr-protocol/nips/commit/d1814405) | [NIP-01](01.md) | `since` and `until` filters should be `since <= created_at <= until` |
|
| 2023-08-14 | [72bb8a12](https://github.com/nostr-protocol/nips/commit/72bb8a12) | [12](12.md), [16](16.md), [20](20.md), [33](33.md) | NIP-12, 16, 20 and 33 were merged into NIP-01 |
|
||||||
| 2023-07-12 | [a1cd2bd8](https://github.com/nostr-protocol/nips/commit/a1cd2bd8) | [NIP-25](25.md) | custom emoji was supported |
|
| 2023-08-11 | [d87f8617](https://github.com/nostr-protocol/nips/commit/d87f8617) | [25](25.md) | empty `content` should be considered as "+" |
|
||||||
| 2023-06-18 | [83cbd3e1](https://github.com/nostr-protocol/nips/commit/83cbd3e1) | [NIP-11](11.md) | 'image' was renamed to 'icon' |
|
| 2023-08-01 | [5d63b157](https://github.com/nostr-protocol/nips/commit/5d63b157) | [57](57.md) | 'zap' tag was changed |
|
||||||
| 2023-04-13 | [bf0a0da6](https://github.com/nostr-protocol/nips/commit/bf0a0da6) | [NIP-15](15.md) | different NIP was re-added as NIP-15 |
|
| 2023-07-15 | [d1814405](https://github.com/nostr-protocol/nips/commit/d1814405) | [01](01.md) | `since` and `until` filters should be `since <= created_at <= until` |
|
||||||
| 2023-04-09 | [fb5b7c73](https://github.com/nostr-protocol/nips/commit/fb5b7c73) | [NIP-15](15.md) | NIP-15 was merged into NIP-01 |
|
| 2023-07-12 | [a1cd2bd8](https://github.com/nostr-protocol/nips/commit/a1cd2bd8) | [25](25.md) | custom emoji was supported |
|
||||||
| 2023-03-29 | [599e1313](https://github.com/nostr-protocol/nips/commit/599e1313) | [NIP-18](18.md) | NIP-18 was bring back |
|
| 2023-06-18 | [83cbd3e1](https://github.com/nostr-protocol/nips/commit/83cbd3e1) | [11](11.md) | 'image' was renamed to 'icon' |
|
||||||
| 2023-03-15 | [e1004d3d](https://github.com/nostr-protocol/nips/commit/e1004d3d) | [NIP-19](19.md) | `1: relay` was changed to optionally |
|
| 2023-04-13 | [bf0a0da6](https://github.com/nostr-protocol/nips/commit/bf0a0da6) | [15](15.md) | different NIP was re-added as NIP-15 |
|
||||||
|
| 2023-04-09 | [fb5b7c73](https://github.com/nostr-protocol/nips/commit/fb5b7c73) | [15](15.md) | NIP-15 was merged into NIP-01 |
|
||||||
|
| 2023-03-29 | [599e1313](https://github.com/nostr-protocol/nips/commit/599e1313) | [18](18.md) | NIP-18 was bring back |
|
||||||
|
| 2023-03-15 | [e1004d3d](https://github.com/nostr-protocol/nips/commit/e1004d3d) | [19](19.md) | `1: relay` was changed to optionally |
|
||||||
|
|
||||||
Breaking changes prior to 2023-03-01 are not yet documented.
|
Breaking changes prior to 2023-03-01 are not yet documented.
|
||||||
|
|
||||||
@ -60,4 +63,3 @@ Breaking changes prior to 2023-03-01 are not yet documented.
|
|||||||
|
|
||||||
- If it isn't clear that a change is breaking or not, we list it.
|
- If it isn't clear that a change is breaking or not, we list it.
|
||||||
- The date is the date it was merged, not necessarily the date of the commit.
|
- The date is the date it was merged, not necessarily the date of the commit.
|
||||||
|
|
||||||
|
29
C7.md
Normal file
29
C7.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
NIP-C7
|
||||||
|
======
|
||||||
|
|
||||||
|
Chats
|
||||||
|
-----
|
||||||
|
|
||||||
|
`draft` `optional`
|
||||||
|
|
||||||
|
A chat message is a `kind 9` event.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kind": 9,
|
||||||
|
"content": "GM",
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A reply to a `kind 9` is an additional `kind 9` which quotes the parent using a `q` tag.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kind": 9,
|
||||||
|
"content": "nostr:nevent1...\nyes",
|
||||||
|
"tags": [
|
||||||
|
["q", <event-id>, <relay-url>, <pubkey>]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
32
README.md
32
README.md
@ -78,6 +78,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
|||||||
- [NIP-61: Nutzaps](61.md)
|
- [NIP-61: Nutzaps](61.md)
|
||||||
- [NIP-64: Chess (PGN)](64.md)
|
- [NIP-64: Chess (PGN)](64.md)
|
||||||
- [NIP-65: Relay List Metadata](65.md)
|
- [NIP-65: Relay List Metadata](65.md)
|
||||||
|
- [NIP-68: Picture-first feeds](68.md)
|
||||||
- [NIP-69: Peer-to-peer Order events](69.md)
|
- [NIP-69: Peer-to-peer Order events](69.md)
|
||||||
- [NIP-70: Protected Events](70.md)
|
- [NIP-70: Protected Events](70.md)
|
||||||
- [NIP-71: Video Events](71.md)
|
- [NIP-71: Video Events](71.md)
|
||||||
@ -86,6 +87,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
|||||||
- [NIP-75: Zap Goals](75.md)
|
- [NIP-75: Zap Goals](75.md)
|
||||||
- [NIP-78: Application-specific data](78.md)
|
- [NIP-78: Application-specific data](78.md)
|
||||||
- [NIP-84: Highlights](84.md)
|
- [NIP-84: Highlights](84.md)
|
||||||
|
- [NIP-86: Relay Management API](86.md)
|
||||||
- [NIP-89: Recommended Application Handlers](89.md)
|
- [NIP-89: Recommended Application Handlers](89.md)
|
||||||
- [NIP-90: Data Vending Machines](90.md)
|
- [NIP-90: Data Vending Machines](90.md)
|
||||||
- [NIP-92: Media Attachments](92.md)
|
- [NIP-92: Media Attachments](92.md)
|
||||||
@ -93,6 +95,8 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
|||||||
- [NIP-96: HTTP File Storage Integration](96.md)
|
- [NIP-96: HTTP File Storage Integration](96.md)
|
||||||
- [NIP-98: HTTP Auth](98.md)
|
- [NIP-98: HTTP Auth](98.md)
|
||||||
- [NIP-99: Classified Listings](99.md)
|
- [NIP-99: Classified Listings](99.md)
|
||||||
|
- [NIP-7D: Threads](7D.md)
|
||||||
|
- [NIP-C7: Chats](C7.md)
|
||||||
|
|
||||||
## Event Kinds
|
## Event Kinds
|
||||||
|
|
||||||
@ -107,14 +111,15 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
|||||||
| `6` | Repost | [18](18.md) |
|
| `6` | Repost | [18](18.md) |
|
||||||
| `7` | Reaction | [25](25.md) |
|
| `7` | Reaction | [25](25.md) |
|
||||||
| `8` | Badge Award | [58](58.md) |
|
| `8` | Badge Award | [58](58.md) |
|
||||||
| `9` | Group Chat Message | [29](29.md) |
|
| `9` | Chat Message | [C7](C7.md) |
|
||||||
| `10` | Group Chat Threaded Reply | [29](29.md) |
|
| `10` | Group Chat Threaded Reply | 29 (deprecated) |
|
||||||
| `11` | Group Thread | [29](29.md) |
|
| `11` | Thread | [7D](7D.md) |
|
||||||
| `12` | Group Thread Reply | [29](29.md) |
|
| `12` | Group Thread Reply | 29 (deprecated) |
|
||||||
| `13` | Seal | [59](59.md) |
|
| `13` | Seal | [59](59.md) |
|
||||||
| `14` | Direct Message | [17](17.md) |
|
| `14` | Direct Message | [17](17.md) |
|
||||||
| `16` | Generic Repost | [18](18.md) |
|
| `16` | Generic Repost | [18](18.md) |
|
||||||
| `17` | Reaction to a website | [25](25.md) |
|
| `17` | Reaction to a website | [25](25.md) |
|
||||||
|
| `20` | Picture | [68](68.md) |
|
||||||
| `40` | Channel Creation | [28](28.md) |
|
| `40` | Channel Creation | [28](28.md) |
|
||||||
| `41` | Channel Metadata | [28](28.md) |
|
| `41` | Channel Metadata | [28](28.md) |
|
||||||
| `42` | Channel Message | [28](28.md) |
|
| `42` | Channel Message | [28](28.md) |
|
||||||
@ -127,6 +132,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
|||||||
| `1040` | OpenTimestamps | [03](03.md) |
|
| `1040` | OpenTimestamps | [03](03.md) |
|
||||||
| `1059` | Gift Wrap | [59](59.md) |
|
| `1059` | Gift Wrap | [59](59.md) |
|
||||||
| `1063` | File Metadata | [94](94.md) |
|
| `1063` | File Metadata | [94](94.md) |
|
||||||
|
| `1111` | Comment | [22](22.md) |
|
||||||
| `1311` | Live Chat Message | [53](53.md) |
|
| `1311` | Live Chat Message | [53](53.md) |
|
||||||
| `1617` | Patches | [34](34.md) |
|
| `1617` | Patches | [34](34.md) |
|
||||||
| `1621` | Issues | [34](34.md) |
|
| `1621` | Issues | [34](34.md) |
|
||||||
@ -178,7 +184,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
|||||||
| `24242` | Blobs stored on mediaservers | [Blossom][blossom] |
|
| `24242` | Blobs stored on mediaservers | [Blossom][blossom] |
|
||||||
| `27235` | HTTP Auth | [98](98.md) |
|
| `27235` | HTTP Auth | [98](98.md) |
|
||||||
| `30000` | Follow sets | [51](51.md) |
|
| `30000` | Follow sets | [51](51.md) |
|
||||||
| `30001` | Generic lists | [51](51.md) |
|
| `30001` | Generic lists | 51 (deprecated) |
|
||||||
| `30002` | Relay sets | [51](51.md) |
|
| `30002` | Relay sets | [51](51.md) |
|
||||||
| `30003` | Bookmark sets | [51](51.md) |
|
| `30003` | Bookmark sets | [51](51.md) |
|
||||||
| `30004` | Curation sets | [51](51.md) |
|
| `30004` | Curation sets | [51](51.md) |
|
||||||
@ -261,23 +267,33 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
|||||||
|
|
||||||
| name | value | other parameters | NIP |
|
| name | value | other parameters | NIP |
|
||||||
| ----------------- | ------------------------------------ | ------------------------------- | -------------------------------------------------- |
|
| ----------------- | ------------------------------------ | ------------------------------- | -------------------------------------------------- |
|
||||||
| `e` | event id (hex) | relay URL, marker, pubkey (hex) | [01](01.md), [10](10.md) |
|
|
||||||
| `p` | pubkey (hex) | relay URL, petname | [01](01.md), [02](02.md) |
|
|
||||||
| `a` | coordinates to an event | relay URL | [01](01.md) |
|
| `a` | coordinates to an event | relay URL | [01](01.md) |
|
||||||
|
| `A` | root address | relay URL | [22](22.md) |
|
||||||
| `d` | identifier | -- | [01](01.md) |
|
| `d` | identifier | -- | [01](01.md) |
|
||||||
| `-` | -- | -- | [70](70.md) |
|
| `e` | event id (hex) | relay URL, marker, pubkey (hex) | [01](01.md), [10](10.md) |
|
||||||
|
| `E` | root event id | relay URL | [22](22.md) |
|
||||||
|
| `f` | currency code | -- | [69](69.md) |
|
||||||
| `g` | geohash | -- | [52](52.md) |
|
| `g` | geohash | -- | [52](52.md) |
|
||||||
| `h` | group id | -- | [29](29.md) |
|
| `h` | group id | -- | [29](29.md) |
|
||||||
| `i` | external identity | proof, url hint | [35](35.md), [39](39.md), [73](73.md) |
|
| `i` | external identity | proof, url hint | [35](35.md), [39](39.md), [73](73.md) |
|
||||||
|
| `I` | root external identity | -- | [22](22.md) |
|
||||||
| `k` | kind | -- | [18](18.md), [25](25.md), [72](72.md), [73](73.md) |
|
| `k` | kind | -- | [18](18.md), [25](25.md), [72](72.md), [73](73.md) |
|
||||||
|
| `K` | root scope | -- | [22](22.md) |
|
||||||
| `l` | label, label namespace | -- | [32](32.md) |
|
| `l` | label, label namespace | -- | [32](32.md) |
|
||||||
| `L` | label namespace | -- | [32](32.md) |
|
| `L` | label namespace | -- | [32](32.md) |
|
||||||
| `m` | MIME type | -- | [94](94.md) |
|
| `m` | MIME type | -- | [94](94.md) |
|
||||||
|
| `p` | pubkey (hex) | relay URL, petname | [01](01.md), [02](02.md) |
|
||||||
|
| `P` | pubkey (hex) | -- | [57](57.md) |
|
||||||
| `q` | event id (hex) | relay URL, pubkey (hex) | [18](18.md) |
|
| `q` | event id (hex) | relay URL, pubkey (hex) | [18](18.md) |
|
||||||
| `r` | a reference (URL, etc) | -- | [24](24.md), [25](25.md) |
|
| `r` | a reference (URL, etc) | -- | [24](24.md), [25](25.md) |
|
||||||
| `r` | relay url | marker | [65](65.md) |
|
| `r` | relay url | marker | [65](65.md) |
|
||||||
|
| `s` | status | -- | [69](69.md) |
|
||||||
| `t` | hashtag | -- | [24](24.md), [34](34.md), [35](35.md) |
|
| `t` | hashtag | -- | [24](24.md), [34](34.md), [35](35.md) |
|
||||||
|
| `u` | url | -- | [61](61.md), [98](98.md) |
|
||||||
| `x` | infohash | -- | [35](35.md) |
|
| `x` | infohash | -- | [35](35.md) |
|
||||||
|
| `y` | platform | -- | [69](69.md) |
|
||||||
|
| `z` | order number | -- | [69](69.md) |
|
||||||
|
| `-` | -- | -- | [70](70.md) |
|
||||||
| `alt` | summary | -- | [31](31.md) |
|
| `alt` | summary | -- | [31](31.md) |
|
||||||
| `amount` | millisatoshis, stringified | -- | [57](57.md) |
|
| `amount` | millisatoshis, stringified | -- | [57](57.md) |
|
||||||
| `bolt11` | `bolt11` invoice | -- | [57](57.md) |
|
| `bolt11` | `bolt11` invoice | -- | [57](57.md) |
|
||||||
|
Loading…
Reference in New Issue
Block a user