Compare commits

...

83 Commits

Author SHA1 Message Date
Pablo Fernandez
30f6d05aba index zap senders with P tag 2023-12-30 22:41:01 +00:00
Vitor Pamplona
98d7f1cd9c Merge pull request #946 from jiftechnify/patch-nip44
Fix typos of NIP-44
2023-12-29 17:40:47 -05:00
Vitor Pamplona
2986982106 Merge pull request #914 from tyiu/nip52-amendments
Amend NIP-52 to require the content field to be provided, and decouple calendar identifier from calendar name
2023-12-29 17:25:26 -05:00
fiatjaf_
000c51ef44 Merge pull request #635 from nostr-protocol/clarify-json-serialization
Clarify JSON serialization for the `id` field
2023-12-29 18:15:21 -03:00
Vitor Pamplona
17c67ef557 Removes the aes-256-gcm tag since no one seems to be using. People that wish to use encryption can now use the new NIP-44 scheme. 2023-12-27 11:21:27 -03:00
Josua Schmid
5e0cfb62ec Rephrase deprecation of kind 2 (#943) 2023-12-27 11:06:36 -03:00
Asai Toshiya
0266d86b41 Simplify list of NIP-07 implementations 2023-12-27 11:00:57 -03:00
Josua Schmid
8e5a275f99 NIP-01 Tighten-up subscription_id management rule
Rephrase to "MUST" so that one does not assume a connection could `CLOSE` any `subscription_id`, which it must not.
2023-12-26 20:02:34 -03:00
hodlbod
92f3a45bdc Merge pull request #949 from schmijos/patch-1
Reword REQ for multiple filters
2023-12-26 12:28:52 -08:00
Josua Schmid
3b065c3c04 Reword REQ for multiple filters 2023-12-26 20:54:06 +01:00
jiftechnify
95218740e2 fix typos 2023-12-25 12:00:05 +09:00
hodlbod
d52b09732a Merge pull request #944 from AsaiToshiya/patch-2
Add leading zero to NIP numbers
2023-12-22 08:50:58 -08:00
Asai Toshiya
91244c50cd Add leading zero to NIP numbers 2023-12-23 01:07:55 +09:00
hodlbod
b946a86135 Merge pull request #942 from schmijos/patch-1
Emphasis on multiple filters in NIP-01
2023-12-22 06:57:44 -08:00
hodlbod
ffc32c43e6 Merge pull request #939 from coracle-social/nip44-tweaks
Clean up NIP 44
2023-12-22 06:56:51 -08:00
Josua Schmid
d30f03316f Emphasis on multiple filters in NIP-01
As mentioned further down in NIP-01

> A REQ message may contain multiple filters.

The comma makes it more clear that the message array can container more than 3 items. So it is understood easily to match

```
// correct:
["REQ", <subscription_id>, filters1, filters2, …]
```

instead of 

```
// wrong:
["REQ", <subscription_id>, [filters1, filters2, …]]
```
2023-12-22 14:54:17 +01:00
Josua Schmid
5ed4232584 Mention deprecation of kind 2
Kind `2` was [consciously](https://github.com/nostr-protocol/nips/pull/703#issuecomment-1672098794) removed in 72bb8a128b (diff-39307f1617417657ee9874be314f13aabdc74401b124d0afe8217f2919c9c7d8L105). Mentioning the fact should help prevent further confusion.
2023-12-22 06:20:01 -03:00
Jon Staab
2b78cc9304 Add clarification about not replacing nip 04 2023-12-20 13:01:12 -08:00
Jon Staab
732b0ce0a4 Clean up NIP 44 to clarify separation of concerns (encryption vs messaging), improve formatting and clarify encryption/decryption steps 2023-12-20 11:28:43 -08:00
fiatjaf
ff533d7a99 remove nip44 calls from nip07 temporarily. 2023-12-20 14:39:39 -03:00
Paul Miller
9be51261c0 nip-44: add audit notice 2023-12-20 14:37:42 -03:00
fiatjaf
4199f20236 nip-44: remove author names and arbitrary line-breaks. 2023-12-20 13:56:03 -03:00
Paul Miller
822b70a565 NIP44 encryption standard, revision 3 (#746)
* Introduce NIP-44 encryption standard

* Finalize NIP-44

* Update spec.

---------

Co-authored-by: Jonathan Staab <shtaab@gmail.com>
2023-12-20 09:22:02 -03:00
Pablo Fernandez
09f8244e6f Merge pull request #831 from nostr-protocol/nostr-connect
rewrite NIP-46
2023-12-18 10:22:17 +00:00
Terry Yiu
ec08d65665 Deprecate 'name' for 'title' on calendar events and add 'title' as a shared tag via NIP-24 2023-12-08 09:59:44 -08:00
Terry Yiu
b8308a9a04 Amend NIP-52 to require the content field to be provided, and decouple calendar identifier from calendar name 2023-12-08 09:39:28 -08:00
fiatjaf
ef106eccca nip-42 phrasing. 2023-12-07 21:18:45 -03:00
hodlbod
730324f487 Merge pull request #921 from alexgleason/follows
NIP-02: Rename Contact List to Follow List
2023-12-07 10:03:55 -08:00
Pablo Fernandez
d0aef4c158 add nip-44 calls 2023-12-07 17:01:25 +00:00
Alex Gleason
0146892501 NIP-02: Rename Contact List to Follow List 2023-12-07 10:01:57 -06:00
fiatjaf_
2bd4bf7841 CLOSED messages for relays that want to reject REQs and NIP-42 AUTH integration (#902)
Co-authored-by: monlovesmango <96307647+monlovesmango@users.noreply.github.com>
2023-12-06 12:01:27 -03:00
hodlbod
c07f0eab1a Merge pull request #915 from xiaolou86/master
Fix typos
2023-12-04 06:05:01 -08:00
xiaolou86
8aba861bdf fix typo in 90.md 2023-12-04 16:08:38 +08:00
xiaolou86
85abb0e026 fix typo in 53.md 2023-12-04 16:08:37 +08:00
xiaolou86
625dad7c1a fix typo in 10.md 2023-12-04 16:08:36 +08:00
xiaolou86
149ead1679 fix typo in 47.md 2023-12-04 16:08:35 +08:00
CapDog
9913395d90 fix Lightning.Pub hotlink 2023-12-03 18:59:23 -03:00
hodlbod
fa120e8edf Merge pull request #913 from mikedilger/master
Fix sort order of event kinds
2023-12-02 16:26:42 -08:00
Mike Dilger
0a97dc0440 Fix sort order of event kinds 2023-12-03 13:22:34 +13:00
fiatjaf
0ba4589550 remove websocket status code meaning as it is replaced by the CLOSED message. 2023-12-02 15:05:59 -03:00
fiatjaf
131fcab95c reformat lightning.pub. 2023-12-02 14:54:42 -03:00
fiatjaf
3983a52d3b latest discoveries. 2023-12-02 14:45:10 -03:00
CapDog
210e38ae36 Add kind Lightning.Pub's kind 21000 (#911) 2023-12-02 14:44:57 -03:00
fiatjaf_
d67988e64e delete NIP-22 (#897) 2023-12-01 13:54:12 -03:00
hodlbod
0874eab3c9 Merge pull request #910 from AsaiToshiya/patch-1
Update tag list
2023-12-01 08:31:45 -08:00
Asai Toshiya
cf57f1d068 Update tag list 2023-12-01 20:45:55 +09:00
ok300
f2e30c63cb Update NIP58 Badges: Remove section on PoW 2023-12-01 08:04:05 -03:00
hodlbod
27cbc2995f Merge pull request #908 from 501st-alpha1/patch-1
Fix typo
2023-11-30 21:50:24 -08:00
Scott Weldon
45e65e9bee Fix typo 2023-11-30 21:21:42 -08:00
Leon
fb87a03d5f Update 07.md
Add OneKey to nip07
2023-11-30 12:44:17 -03:00
Jon Staab
6de35f9e6a Make client hints backwards compatible 2023-11-27 12:11:36 -03:00
Alejandro
1a106c6bff [NIP-75] add optional 'image' and 'summary' tags (#890) 2023-11-23 19:17:28 +01:00
Asai Toshiya
5ae5a6d055 Remove "NIP-33" (#896) 2023-11-23 14:44:12 -03:00
fiatjaf
6c35537ca4 @v0l improvements. 2023-11-21 22:31:13 -03:00
fiatjaf_
e4bddbee7b peer-pubkey
Co-authored-by: monlovesmango <96307647+monlovesmango@users.noreply.github.com>
2023-11-21 22:25:19 -03:00
fiatjaf_
c5c2d86a47 peer-pubkey
Co-authored-by: monlovesmango <96307647+monlovesmango@users.noreply.github.com>
2023-11-21 20:49:35 -03:00
fiatjaf
cb37a9320e rewrite NIP-46. 2023-11-21 15:49:40 -03:00
fiatjaf_
7822a8b126 Merge pull request #880 from nostr-protocol/save-nip-51 2023-11-19 14:47:02 -03:00
fiatjaf_
ea6d277c7c Merge pull request #889 from nostr-protocol/nip11-restricted-writes 2023-11-19 11:18:19 -03:00
fiatjaf
438812d0a4 clarify that restricted_writes do not include normal anti-spam heuristics and other kinds of soft-restrictions. 2023-11-19 10:17:57 -03:00
fiatjaf
33a719f345 fix kind:40 name (following @snowcait). 2023-11-19 07:17:11 -03:00
Semisol
da19c078ab Merge branch 'master' into clarify-json-serialization 2023-11-19 01:45:41 +01:00
fiatjaf
5e6e7a2bc8 get rid of these arbitrary markdown linebreaks and put everything in the same line. 2023-11-18 11:36:20 -03:00
fiatjaf
2d678bcd5a metadata tags for lists kept at "title", "image" and "description", following @erskingardner and @darecjo. 2023-11-18 11:35:32 -03:00
fiatjaf
50d20123e2 "restricted_writes" on NIP-11. 2023-11-18 09:39:54 -03:00
Semisol
4d709d1804 add form feed and backspace 2023-11-17 18:12:47 +03:00
fiatjaf
4a77fc5c40 add interests list and interests sets (following @erskingardner and @pablof7z). 2023-11-16 15:50:39 -03:00
fiatjaf
2a0b701ff7 add bookmark sets (following @staab). 2023-11-16 12:26:50 -03:00
fiatjaf
31382e57a1 add muted words, blocked relays, search relays, chat groups, emoji list and emoji sets. update set metadata tags.
following @vitorpamplona, @frnandu, @snowcait, @hzrd149
closes https://github.com/nostr-protocol/nips/pull/559
closes https://github.com/nostr-protocol/nips/pull/673
2023-11-15 20:52:27 -03:00
fiatjaf_
6c3eebf510 clarify that standard lists use normal replaceable events
Co-authored-by: hodlbod <jstaab@protonmail.com>
2023-11-15 20:06:49 -03:00
fiatjaf_
cd598bad5b remove the point that makes kind 30001 be a grab-bag of things
Co-authored-by: hodlbod <jstaab@protonmail.com>
2023-11-15 20:06:00 -03:00
fiatjaf
804ee187c2 make follow sets kind 30000 as per @monlovesmango's comment. 2023-11-15 15:39:23 -03:00
fiatjaf
4db99145d7 remove legacy communities list example. 2023-11-15 15:34:46 -03:00
fiatjaf
61b736574c add kind 30005 follow sets. 2023-11-15 15:32:22 -03:00
fiatjaf
54befcc09a deprecate the 30001 ad-hoc pin/bookmark/communities lists and use new kinds. 2023-11-15 15:32:09 -03:00
fiatjaf
fd288d4ea9 update readme kind list. 2023-11-15 08:28:59 -03:00
fiatjaf
cf1b17280f bring back kind:10000 mute lists, deprecation notice about kind:30000/"mute" and more examples. 2023-11-15 08:24:46 -03:00
fiatjaf
1bbd87c42f distinction between lists and sets. 2023-11-15 07:33:26 -03:00
fiatjaf
da05b5d915 add "communities" list and examples. 2023-11-15 00:47:15 -03:00
fiatjaf
5dc6071978 cut, simplify and update NIP-51 to reflect the real-world usage. 2023-11-15 00:22:09 -03:00
Semisol
21d71791c8 Add carriage returns and tab characters since those may be in some events 2023-07-04 13:55:15 +03:00
Semisol
3f8658ecc1 Fix typo 2023-07-04 13:49:52 +03:00
Semisol
b5a7b67d78 Clarify JSON serialization for the id field 2023-07-03 18:02:15 +03:00
25 changed files with 672 additions and 449 deletions

36
01.md
View File

@@ -29,7 +29,7 @@ The only object type that exists is the `event`, which has the following format
}
```
To obtain the `event.id`, we `sha256` the serialized event. The serialization is done over the UTF-8 JSON-serialized string (with no white space or line breaks between the fields) of the following structure:
To obtain the `event.id`, we `sha256` the serialized event. The serialization is done over the UTF-8 JSON-serialized string (which is described below) of the following structure:
```
[
@@ -42,6 +42,18 @@ To obtain the `event.id`, we `sha256` the serialized event. The serialization is
]
```
To prevent implementation differences from creating a different event ID for the same event, the following rules MUST be followed while serializing:
- No whitespace, line breaks or other unnecessary formatting should be included in the output JSON.
- No characters except the following should be escaped, and instead should be included verbatim:
- A line break, `0x0A`, as `\n`
- A double quote, `0x22`, as `\"`
- A backslash, `0x5C`, as `\\`
- A carriage return, `0x0D`, as `\r`
- A tab character, `0x09`, as `\t`
- A backspace, `0x08`, as `\b`
- A form feed, `0x0C`, as `\f`
- UTF-8 should be used for encoding.
### Tags
Each tag is an array of strings of arbitrary size, with some conventions around them. Take a look at the example below:
@@ -96,21 +108,17 @@ These are just conventions and relay implementations may differ.
Relays expose a websocket endpoint to which clients can connect. Clients SHOULD open a single websocket connection to each relay and use it for all their subscriptions. Relays MAY limit number of connections from specific IP/client/etc.
### Meaning of WebSocket status codes
- When a websocket is closed by the relay with a status code `4000` that means the client shouldn't try to connect again.
### From client to relay: sending events and creating subscriptions
Clients can send 3 types of messages, which must be JSON arrays, according to the following patterns:
* `["EVENT", <event JSON as defined above>]`, used to publish events.
* `["REQ", <subscription_id>, <filters JSON>...]`, used to request events and subscribe to new updates.
* `["REQ", <subscription_id>, <filters1>, <filters2>, ...]`, used to request events and subscribe to new updates.
* `["CLOSE", <subscription_id>]`, used to stop previous subscriptions.
`<subscription_id>` is an arbitrary, non-empty string of max length 64 chars, that should be used to represent a subscription. Relays should manage `<subscription_id>`s independently for each WebSocket connection; even if `<subscription_id>`s are the same string, they should be treated as different subscriptions for different connections.
`<subscription_id>` is an arbitrary, non-empty string of max length 64 chars. It represents a subscription per connection. Relays MUST manage `<subscription_id>`s independently for each WebSocket connection. `<subscription_id>`s are not guarantueed to be globally unique.
`<filters>` is a JSON object that determines what events will be sent in that subscription, it can have the following attributes:
`<filtersX>` is a JSON object that determines what events will be sent in that subscription, it can have the following attributes:
```json
{
@@ -145,19 +153,25 @@ Relays can send 4 types of messages, which must also be JSON arrays, according t
* `["EVENT", <subscription_id>, <event JSON as defined above>]`, used to send events requested by clients.
* `["OK", <event_id>, <true|false>, <message>]`, used to indicate acceptance or denial of an `EVENT` message.
* `["EOSE", <subscription_id>]`, used to indicate the _end of stored events_ and the beginning of events newly received in real-time.
* `["CLOSED", <subscription_id>, <message>]`, used to indicate that a subscription was ended on the server side.
* `["NOTICE", <message>]`, used to send human-readable error messages or other things to clients.
This NIP defines no rules for how `NOTICE` messages should be sent or treated.
- `EVENT` messages MUST be sent only with a subscription ID related to a subscription previously initiated by the client (using the `REQ` message above).
- `OK` messages MUST be sent in response to `EVENT` messages received from clients, they must have the 3rd parameter set to `true` when an event has been accepted by the relay, `false` otherwise. The 4th parameter MUST always be present, but MAY be an empty string when the 3rd is `true`, otherwise it MUST be a string formed by a machine-readable single-word prefix followed by a `:` and then a human-readable message. The standardized machine-readable prefixes are: `duplicate`, `pow`, `blocked`, `rate-limited`, `invalid`, and `error` for when none of that fits. Some examples:
- `OK` messages MUST be sent in response to `EVENT` messages received from clients, they must have the 3rd parameter set to `true` when an event has been accepted by the relay, `false` otherwise. The 4th parameter MUST always be present, but MAY be an empty string when the 3rd is `true`, otherwise it MUST be a string formed by a machine-readable single-word prefix followed by a `:` and then a human-readable message. Some examples:
* `["OK", "b1a649ebe8...", true, ""]`
* `["OK", "b1a649ebe8...", true, "pow: difficulty 25>=24"]`
* `["OK", "b1a649ebe8...", true, "duplicate: already have this event"]`
* `["OK", "b1a649ebe8...", false, "blocked: you are banned from posting here"]`
* `["OK", "b1a649ebe8...", false, "blocked: please register your pubkey at https://my-expensive-relay.example.com"]`
* `["OK", "b1a649ebe8...", false, "rate-limited: slow down there chief"]`
* `["OK", "b1a649ebe8...", false, "invalid: event creation date is too far off from the current time. Is your system clock in sync?"]`
* `["OK", "b1a649ebe8...", false, "invalid: event creation date is too far off from the current time"]`
* `["OK", "b1a649ebe8...", false, "pow: difficulty 26 is less than 30"]`
* `["OK", "b1a649ebe8...", false, "error: could not connect to the database"]`
- `CLOSED` messages MUST be sent in response to a `REQ` when the relay refuses to fulfill it. It can also be sent when a relay decides to kill a subscription on its side before a client has disconnected or sent a `CLOSE`. This message uses the same pattern of `OK` messages with the machine-readable prefix and human-readable message. Some examples:
* `["CLOSED", "sub1", "duplicate: sub1 already opened"]`
* `["CLOSED", "sub1", "unsupported: filter contains unknown elements"]`
* `["CLOSED", "sub1", "error: could not connect to the database"]`
* `["CLOSED", "sub1", "error: shutting down idle subscription"]`
- The standardized machine-readable prefixes for `OK` and `CLOSED` are: `duplicate`, `pow`, `blocked`, `rate-limited`, `invalid`, and `error` for when none of that fits.

20
02.md
View File

@@ -1,12 +1,12 @@
NIP-02
======
Contact List and Petnames
-------------------------
Follow List
-----------
`final` `optional`
A special event with kind `3`, meaning "contact list" is defined as having a list of `p` tags, one for each of the followed/known profiles one is following.
A special event with kind `3`, meaning "follow list" is defined as having a list of `p` tags, one for each of the followed/known profiles one is following.
Each tag entry should contain the key for the profile, a relay URL where events from that key can be found (can be set to an empty string if not needed), and a local name (or "petname") for that profile (can also be set to an empty string or not provided), i.e., `["p", <32-bytes hex key>, <main relay URL>, <petname>]`. The `content` can be anything and should be ignored.
@@ -25,27 +25,27 @@ For example:
}
```
Every new contact list that gets published overwrites the past ones, so it should contain all entries. Relays and clients SHOULD delete past contact lists as soon as they receive a new one.
Every new following list that gets published overwrites the past ones, so it should contain all entries. Relays and clients SHOULD delete past following lists as soon as they receive a new one.
## Uses
### Contact list backup
### Follow list backup
If one believes a relay will store their events for sufficient time, they can use this kind-3 event to backup their following list and recover on a different device.
### Profile discovery and context augmentation
A client may rely on the kind-3 event to display a list of followed people by profiles one is browsing; make lists of suggestions on who to follow based on the contact lists of other people one might be following or browsing; or show the data in other contexts.
A client may rely on the kind-3 event to display a list of followed people by profiles one is browsing; make lists of suggestions on who to follow based on the follow lists of other people one might be following or browsing; or show the data in other contexts.
### Relay sharing
A client may publish a full list of contacts with good relays for each of their contacts so other clients may use these to update their internal relay lists if needed, increasing censorship-resistance.
A client may publish a follow list with good relays for each of their follows so other clients may use these to update their internal relay lists if needed, increasing censorship-resistance.
### Petname scheme
The data from these contact lists can be used by clients to construct local ["petname"](http://www.skyhunter.com/marcs/petnames/IntroPetNames.html) tables derived from other people's contact lists. This alleviates the need for global human-readable names. For example:
The data from these follow lists can be used by clients to construct local ["petname"](http://www.skyhunter.com/marcs/petnames/IntroPetNames.html) tables derived from other people's follow lists. This alleviates the need for global human-readable names. For example:
A user has an internal contact list that says
A user has an internal follow list that says
```json
[
@@ -53,7 +53,7 @@ A user has an internal contact list that says
]
```
And receives two contact lists, one from `21df6d143fb96c2ec9d63726bf9edc71` that says
And receives two follow lists, one from `21df6d143fb96c2ec9d63726bf9edc71` that says
```json
[

4
04.md
View File

@@ -1,10 +1,12 @@
> __Warning__ `unrecommended`: deprecated in favor of [NIP-44](44.md)
NIP-04
======
Encrypted Direct Message
------------------------
`final` `optional`
`final` `unrecommended` `optional`
A special event with kind `4`, meaning "encrypted direct message". It is supposed to have the following attributes:

17
07.md
View File

@@ -18,21 +18,10 @@ async window.nostr.signEvent(event: { created_at: number, kind: number, tags: st
Aside from these two basic above, the following functions can also be implemented optionally:
```
async window.nostr.getRelays(): { [url: string]: {read: boolean, write: boolean} } // returns a basic map of relay urls to relay policies
async window.nostr.nip04.encrypt(pubkey, plaintext): string // returns ciphertext and iv as specified in nip-04
async window.nostr.nip04.decrypt(pubkey, ciphertext): string // takes ciphertext and iv as specified in nip-04
async window.nostr.nip04.encrypt(pubkey, plaintext): string // returns ciphertext and iv as specified in nip-04 (deprecated)
async window.nostr.nip04.decrypt(pubkey, ciphertext): string // takes ciphertext and iv as specified in nip-04 (deprecated)
```
### Implementation
- [horse](https://github.com/fiatjaf/horse) (Chrome and derivatives)
- [nos2x](https://github.com/fiatjaf/nos2x) (Chrome and derivatives)
- [Alby](https://getalby.com) (Chrome and derivatives, Firefox)
- [Blockcore](https://www.blockcore.net/wallet) (Chrome and derivatives)
- [nos2x-fox](https://diegogurpegui.com/nos2x-fox/) (Firefox)
- [Flamingo](https://www.getflamingo.org/) (Chrome and derivatives)
- [AKA Profiles](https://github.com/neilck/aka-extension) (Chrome, stores multiple keys)
- [TokenPocket](https://www.tokenpocket.pro/) (Android, IOS, Chrome and derivatives)
- [Nostrmo](https://github.com/haorendashu/nostrmo_faq#download) (Android, IOS)
- [Spring Browser](https://spring.site) (Android)
- [nodestr](https://github.com/lightning-digital-entertainment/nodestr) (NodeJS polyfill)
- [Nostore](https://apps.apple.com/us/app/nostore/id1666553677) (Safari on iOS/MacOS)
See https://github.com/aljazceru/awesome-nostr#nip-07-browser-extensions.

2
09.md
View File

@@ -8,7 +8,7 @@ Event Deletion
A special event with kind `5`, meaning "deletion" is defined as having a list of one or more `e` tags, each referencing an event the author is requesting to be deleted.
Each tag entry must contain an "e" event id and/or NIP-33 `a` tags intended for deletion.
Each tag entry must contain an "e" event id and/or `a` tags intended for deletion.
The event's `content` field MAY contain a text note describing the reason for the deletion.

2
10.md
View File

@@ -33,7 +33,7 @@ Where:
* 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 citings from this event. `root-id` and `reply-id` are as above.
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.

11
11.md
View File

@@ -79,6 +79,7 @@ are rejected or fail immediately.
"min_pow_difficulty": 30,
"auth_required": true,
"payment_required": true,
"restricted_writes": true,
"created_at_lower_limit": 31536000,
"created_at_upper_limit": 3
},
@@ -124,9 +125,15 @@ Even if set to False, authentication may be required for specific actions.
- `payment_required`: this relay requires payment before a new connection may perform any action.
- `created_at_lower_limit`: 'created_at' lower limit as defined in [NIP-22](22.md)
- `restricted_writes`: this relay requires some kind of condition to be fulfilled in order to
accept events (not necessarily, but including `payment_required` and `min_pow_difficulty`).
This should only be set to `true` when users are expected to know the relay policy before trying
to write to it -- like belonging to a special pubkey-based whitelist or writing only events of
a specific niche kind or content. Normal anti-spam heuristics, for example, do not qualify.
- `created_at_upper_limit`: 'created_at' upper limit as defined in [NIP-22](22.md)
- `created_at_lower_limit`: 'created_at' lower limit
- `created_at_upper_limit`: 'created_at' upper limit
### Event Retention

2
15.md
View File

@@ -108,7 +108,7 @@ Fields that are not self-explanatory:
```
Fields that are not self-explanatory:
- `quantity` can be null in the case of items with unlimited abailability, like digital items, or services
- `quantity` can be null in the case of items with unlimited availability, like digital items, or services
- `specs`:
- an optional array of key pair values. It allows for the Customer UI to present product specifications in a structure mode. It also allows comparison between products
- eg: `[["operating_system", "Android 12.0"], ["screen_size", "6.4 inches"], ["connector_type", "USB Type C"]]`

45
22.md
View File

@@ -1,45 +0,0 @@
NIP-22
======
Event `created_at` Limits
-------------------------
`draft` `optional`
Relays may define both upper and lower limits within which they will consider an event's `created_at` to be acceptable. Both the upper and lower limits MUST be unix timestamps in seconds as defined in [NIP-01](01.md).
If a relay supports this NIP, the relay SHOULD send the client an `OK` result saying the event was not stored for the `created_at` timestamp not being within the permitted limits.
Client Behavior
---------------
Clients SHOULD use the [NIP-11](11.md) `supported_nips` field to learn if a relay uses event `created_at` time limits as defined by this NIP.
Motivation
----------
This NIP formalizes restrictions on event timestamps as accepted by a relay and allows clients to be aware of relays that have these restrictions.
The event `created_at` field is just a unix timestamp and can be set to a time in the past or future. Relays accept and share events dated to 20 years ago or 50,000 years in the future. This NIP aims to define a way for relays that do not want to store events with *any* timestamp to set their own restrictions.
_Replaceable events_ can behave rather unexpectedly if the user wrote them - or tried to write them - with a wrong system clock. Persisting an update with a backdated system now would result in the update not getting persisted without a notification and if they did the last update with a forward dated system, they will again fail to do another update with the now correct time.
A wide adoption of this NIP could create a better user experience as it would decrease the amount of events that appear wildly out of order or even from impossible dates in the distant past or future.
Keep in mind that there is a use case where a user migrates their old posts onto a new relay. If a relay rejects events that were not recently created, it cannot serve this use case.
Python (pseudocode) Example
---------------------------
```python
import time
TIME = int(time.time())
LOWER_LIMIT = TIME - (60 * 60 * 24) # Define lower limit as 1 day into the past
UPPER_LIMIT = TIME + (60 * 15) # Define upper limit as 15 minutes into the future
if event.created_at not in range(LOWER_LIMIT, UPPER_LIMIT):
ws.send('["OK", event.id, False, "invalid: the event created_at field is out of the acceptable range (-24h, +15min) for this relay"]')
```
Note: These are just example limits, the relay operator can choose whatever limits they want.

1
24.md
View File

@@ -39,3 +39,4 @@ tags
These tags may be present in multiple event kinds. Whenever a different meaning is not specified by some more specific NIP, they have the following meanings:
- `r`: a web URL the event is referring to in some way
- `title`: title of the event

71
42.md
View File

@@ -12,32 +12,31 @@ This NIP defines a way for clients to authenticate to relays by signing an ephem
A relay may want to require clients to authenticate to access restricted resources. For example,
- A relay may request payment or other forms of whitelisting to publish events -- this can naïvely be achieved by limiting publication
to events signed by the whitelisted key, but with this NIP they may choose to accept any events as long as they are published from an
authenticated user;
- A relay may limit access to `kind: 4` DMs to only the parties involved in the chat exchange, and for that it may require authentication
before clients can query for that kind.
- A relay may request payment or other forms of whitelisting to publish events -- this can naïvely be achieved by limiting publication to events signed by the whitelisted key, but with this NIP they may choose to accept any events as long as they are published from an authenticated user;
- A relay may limit access to `kind: 4` DMs to only the parties involved in the chat exchange, and for that it may require authentication before clients can query for that kind.
- A relay may limit subscriptions of any kind to paying users or users whitelisted through any other means, and require authentication.
## Definitions
This NIP defines a new message, `AUTH`, which relays can send when they support authentication and clients can send to relays when they want
to authenticate. When sent by relays, the message is of the following form:
### New client-relay protocol messages
This NIP defines a new message, `AUTH`, which relays CAN send when they support authentication and clients can send to relays when they want to authenticate. When sent by relays the message has the following form:
```json
["AUTH", <challenge-string>]
```
And, when sent by clients, of the following form:
And, when sent by clients, the following form:
```json
["AUTH", <signed-event-json>]
```
The signed event is an ephemeral event not meant to be published or queried, it must be of `kind: 22242` and it should have at least two tags,
one for the relay URL and one for the challenge string as received from the relay.
Relays MUST exclude `kind: 22242` events from being broadcasted to any client.
`created_at` should be the current time. Example:
`AUTH` messages sent by clients MUST be answered with an `OK` message, like any `EVENT` message.
### Canonical authentication event
The signed event is an ephemeral event not meant to be published or queried, it must be of `kind: 22242` and it should have at least two tags, one for the relay URL and one for the challenge string as received from the relay. Relays MUST exclude `kind: 22242` events from being broadcasted to any client. `created_at` should be the current time. Example:
```json
{
@@ -50,27 +49,49 @@ Relays MUST exclude `kind: 22242` events from being broadcasted to any client.
}
```
### `OK` and `CLOSED` machine-readable prefixes
This NIP defines two new prefixes that can be used in `OK` (in response to event writes by clients) and `CLOSED` (in response to rejected subscriptions by clients):
- `"auth-required: "` - for when a client has not performed `AUTH` and the relay requires that to fulfill the query or write the event.
- `"restricted: "` - for when a client has already performed `AUTH` but the key used to perform it is still not allowed by the relay or is exceeding its authorization.
## Protocol flow
At any moment the relay may send an `AUTH` message to the client containing a challenge. After receiving that the client may decide to
authenticate itself or not. The challenge is expected to be valid for the duration of the connection or until a next challenge is sent by
the relay.
At any moment the relay may send an `AUTH` message to the client containing a challenge. The challenge is valid for the duration of the connection or until another challenge is sent by the relay. The client MAY decide to send its `AUTH` event at any point and the authenticated session is valid afterwards for the duration of the connection.
The client may send an auth message right before performing an action for which it knows authentication will be required -- for example, right
before requesting `kind: 4` chat messages --, or it may do right on connection start or at some other moment it deems best. The authentication
is expected to last for the duration of the WebSocket connection.
### `auth-required` in response to a `REQ` message
Upon receiving a message from an unauthenticated user it can't fulfill without authentication, a relay may choose to notify the client. For
that it can use a `NOTICE` or `OK` message with a standard prefix `"restricted: "` that is readable both by humans and machines, for example:
Given that a relay is likely to require clients to perform authentication only for certain jobs, like answering a `REQ` or accepting an `EVENT` write, these are some expected common flows:
```json
["NOTICE", "restricted: we can't serve DMs to unauthenticated users, does your client implement NIP-42?"]
```
relay: ["AUTH", "<challenge>"]
client: ["REQ", "sub_1", {"kinds": [4]}]
relay: ["CLOSED", "sub_1", "auth-required: we can't serve DMs to unauthenticated users"]
client: ["AUTH", {"id": "abcdef...", ...}]
relay: ["OK", "abcdef...", true, ""]
client: ["REQ", "sub_1", {"kinds": [4]}]
relay: ["EVENT", "sub_1", {...}]
relay: ["EVENT", "sub_1", {...}]
relay: ["EVENT", "sub_1", {...}]
relay: ["EVENT", "sub_1", {...}]
...
```
or it can return an `OK` message noting the reason an event was not written using the same prefix:
In this case, the `AUTH` message from the relay could be sent right as the client connects or it can be sent immediately before the `CLOSED` is sent. The only requirement is that _the client must have a stored challenge associated with that relay_ so it can act upon that in response to the `auth-required` `CLOSED` message.
```json
["OK", <event-id>, false, "restricted: we do not accept events from unauthenticated users, please sign up at https://example.com/"]
### `auth-required` in response to an `EVENT` message
The same flow is valid for when a client wants to write an `EVENT` to the relay, except now the relay sends back an `OK` message instead of a `CLOSED` message:
```
relay: ["AUTH", "<challenge>"]
client: ["EVENT", {"id": "012345...", ...}]
relay: ["OK", "012345...", false, "auth-required: we only accept events from registered users"]
client: ["AUTH", {"id": "abcdef...", ...}]
relay: ["OK", "abcdef...", true, ""]
client: ["EVENT", {"id": "012345...", ...}]
relay: ["OK", "012345...", true, ""]
```
## Signed Event Verification

295
44.md Normal file
View File

@@ -0,0 +1,295 @@
NIP-44
=====
Encrypted Payloads (Versioned)
------------------------------
`optional`
The NIP introduces a new data format for keypair-based encryption. This NIP is versioned
to allow multiple algorithm choices to exist simultaneously. This format may be used for
many things, but MUST be used in the context of a signed event as described in NIP 01.
*Note*: this format DOES NOT define any `kind`s related to a new direct messaging standard,
only the encryption required to define one. It SHOULD NOT be used as a drop-in replacement
for NIP 04 payloads.
## Versions
Currently defined encryption algorithms:
- `0x00` - Reserved
- `0x01` - Deprecated and undefined
- `0x02` - secp256k1 ECDH, HKDF, padding, ChaCha20, HMAC-SHA256, base64
## Limitations
Every nostr user has their own public key, which solves key distribution problems present
in other solutions. However, nostr's relay-based architecture makes it difficult to implement
more robust private messaging protocols with things like metadata hiding, forward secrecy,
and post compromise secrecy.
The goal of this NIP is to have a _simple_ way to encrypt payloads used in the context of a signed
event. When applying this NIP to any use case, it's important to keep in mind your users' threat
model and this NIP's limitations. For high-risk situations, users should chat in specialized E2EE
messaging software and limit use of nostr to exchanging contacts.
On its own, messages sent using this scheme have a number of important shortcomings:
- No deniability: it is possible to prove an event was signed by a particular key
- No forward secrecy: when a key is compromised, it is possible to decrypt all previous conversations
- No post-compromise security: when a key is compromised, it is possible to decrypt all future conversations
- No post-quantum security: a powerful quantum computer would be able to decrypt the messages
- IP address leak: user IP may be seen by relays and all intermediaries between user and relay
- Date leak: `created_at` is public, since it is a part of NIP 01 event
- Limited message size leak: padding only partially obscures true message length
- No attachments: they are not supported
Lack of forward secrecy may be partially mitigated by only sending messages to trusted relays, and asking
relays to delete stored messages after a certain duration has elapsed.
## Version 2
NIP-44 version 2 has the following design characteristics:
- Payloads are authenticated using a MAC before signing rather than afterwards because events are assumed
to be signed as specified in NIP-01. The outer signature serves to authenticate the full payload, and MUST
be validated before decrypting.
- ChaCha is used instead of AES because it's faster and has
[better security against multi-key attacks](https://datatracker.ietf.org/doc/draft-irtf-cfrg-aead-limits/).
- ChaCha is used instead of XChaCha because XChaCha has not been standardized. Also, xChaCha's improved collision
resistance of nonces isn't necessary since every message has a new (key, nonce) pair.
- HMAC-SHA256 is used instead of Poly1305 because polynomial MACs are much easier to forge.
- 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.
- 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.
### Encryption
1. Calculate a conversation key
- Execute ECDH (scalar multiplication) of public key B by private key A
Output `shared_x` must be unhashed, 32-byte encoded x coordinate of the shared point
- Use HKDF-extract with sha256, `IKM=shared_x` and `salt=utf8_encode('nip44-v2')`
- HKDF output will be a `conversation_key` between two users.
- It is always the same, when key roles are swapped: `conv(a, B) == conv(b, A)`
2. Generate a random 32-byte nonce
- Always use [CSPRNG](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator)
- Don't generate a nonce from message content
- Don't re-use the same nonce between messages: doing so would make them decryptable,
but won't leak the long-term key
3. Calculate message keys
- The keys are generated from `conversation_key` and `nonce`. Validate that both are 32 bytes long
- Use HKDF-expand, with sha256, `PRK=conversation_key`, `info=nonce` and `L=76`
- Slice 76-byte HKDF output into: `chacha_key` (bytes 0..32), `chacha_nonce` (bytes 32..44), `hmac_key` (bytes 44..76)
4. Add padding
- Content must be encoded from UTF-8 into byte array
- Validate plaintext length. Minimum is 1 byte, maximum is 65535 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
- Plaintext length is encoded in big-endian as first 2 bytes of the padded blob
5. Encrypt padded content
- Use ChaCha20, with key and nonce from step 3
6. Calculate MAC (message authentication code)
- AAD (additional authenticated data) is used - instead of calculating MAC on ciphertext,
it's calculated over a concatenation of `nonce` and `ciphertext`
- Validate that AAD (nonce) is 32 bytes
7. Base64-encode (with padding) params using `concat(version, nonce, ciphertext, mac)`
Encrypted payloads MUST be included in an event's payload, hashed, and signed as defined in NIP 01, using schnorr
signature scheme over secp256k1.
### Decryption
Before decryption, the event's pubkey and signature MUST be validated as defined in NIP 01. The public key MUST be
a valid non-zero secp256k1 curve point, and the signature must be valid secp256k1 schnorr signature. For exact
validation rules, refer to BIP-340.
1. Check if first payload's character is `#`
- `#` is an optional future-proof flag that means non-base64 encoding is used
- The `#` is not present in base64 alphabet, but, instead of throwing `base64 is invalid`,
implementations MUST indicate that the encryption version is not yet supported
2. Decode base64
- Base64 is decoded into `version, nonce, ciphertext, mac`
- If the version is unknown, implementations must indicate that the encryption version is not supported
- Validate length of base64 message to prevent DoS on base64 decoder: it can be in range from 132 to 87472 chars
- Validate length of decoded message to verify output of the decoder: it can be in range from 99 to 65603 bytes
3. Calculate conversation key
- See step 1 of [encryption](#Encryption)
4. Calculate message keys
- See step 3 of [encryption](#Encryption)
5. Calculate MAC (message authentication code) with AAD and compare
- Stop and throw an error if MAC doesn't match the decoded one from step 2
- Use constant-time comparison algorithm
6. Decrypt ciphertext
- Use ChaCha20 with key and nonce from step 3
7. Remove padding
- Read the first two BE bytes of plaintext that correspond to plaintext length
- Verify that the length of sliced plaintext matches the value of the two BE bytes
- Verify that calculated padding from step 3 of the [encryption](#Encryption) process matches the actual padding
### Details
- Cryptographic methods
- `secure_random_bytes(length)` fetches randomness from CSPRNG.
- `hkdf(IKM, salt, info, L)` represents HKDF [(RFC 5869)](https://datatracker.ietf.org/doc/html/rfc5869)
with SHA256 hash function comprised of methods `hkdf_extract(IKM, salt)` and `hkdf_expand(OKM, info, L)`.
- `chacha20(key, nonce, data)` is ChaCha20 [(RFC 8439)](https://datatracker.ietf.org/doc/html/rfc8439) with
starting counter set to 0.
- `hmac_sha256(key, message)` is HMAC [(RFC 2104)](https://datatracker.ietf.org/doc/html/rfc2104).
- `secp256k1_ecdh(priv_a, pub_b)` is multiplication of point B by scalar a (`a ⋅ B`), defined in
[BIP340](https://github.com/bitcoin/bips/blob/e918b50731397872ad2922a1b08a5a4cd1d6d546/bip-0340.mediawiki).
The operation produces a shared point, and we encode the shared point's 32-byte x coordinate, using method
`bytes(P)` from BIP340. Private and public keys must be validated as per BIP340: pubkey must be a valid,
on-curve point, and private key must be a scalar in range `[1, secp256k1_order - 1]`.
- Operators
- `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`.
- Constants `c`:
- `min_plaintext_size` is 1. 1b msg is padded to 32b.
- `max_plaintext_size` is 65535 (64kb - 1). It is padded to 65536.
- Functions
- `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
- `is_equal_ct(a, b)` is constant-time equality check of 2 byte arrays
- `utf8_encode(string)` and `utf8_decode(bytes)` transform string to byte array and back
- `write_u8(number)` restricts number to values 0..255 and encodes into Big-Endian uint8 byte array
- `write_u16_be(number)` restricts number to values 0..65535 and encodes into Big-Endian uint16 byte array
- `zeros(length)` creates byte array of length `length >= 0`, filled with zeros
- `floor(number)` and `log2(number)` are well-known mathematical methods
### Implementation pseudocode
The following is a collection of python-like pseudocode functions which implement the above primitives,
intended to guide impelmenters. A collection of implementations in different languages is available at https://github.com/paulmillr/nip44.
```py
# Calculates length of the padded byte array.
def calc_padded_len(unpadded_len):
next_power = 1 << (floor(log2(unpadded_len - 1))) + 1
if next_power <= 256:
chunk = 32
else:
chunk = next_power / 8
if unpadded_len <= 32:
return 32
else:
return chunk * (floor((len - 1) / chunk) + 1)
# Converts unpadded plaintext to padded bytearray
def pad(plaintext):
unpadded = utf8_encode(plaintext)
unpadded_len = len(plaintext)
if (unpadded_len < c.min_plaintext_size or
unpadded_len > c.max_plaintext_size): raise Exception('invalid plaintext length')
prefix = write_u16_be(unpadded_len)
suffix = zeros(calc_padded_len(unpadded_len) - unpadded_len)
return concat(prefix, unpadded, suffix)
# Converts padded bytearray to unpadded plaintext
def unpad(padded):
unpadded_len = read_uint16_be(padded[0:2])
unpadded = padded[2:2+unpadded_len]
if (unpadded_len == 0 or
len(unpadded) != unpadded_len or
len(padded) != 2 + calc_padded_len(unpadded_len)): raise Exception('invalid padding')
return utf8_decode(unpadded)
# metadata: always 65b (version: 1b, nonce: 32b, max: 32b)
# plaintext: 1b to 0xffff
# padded plaintext: 32b to 0xffff
# ciphertext: 32b+2 to 0xffff+2
# raw payload: 99 (65+32+2) to 65603 (65+0xffff+2)
# compressed payload (base64): 132b to 87472b
def decode_payload(payload):
plen = len(payload)
if plen == 0 or payload[0] == '#': raise Exception('unknown version')
if plen < 132 or plen > 87472: raise Exception('invalid payload size')
data = base64_decode(payload)
dlen = len(d)
if dlen < 99 or dlen > 65603: raise Exception('invalid data size');
vers = data[0]
if vers != 2: raise Exception('unknown version ' + vers)
nonce = data[1:33]
ciphertext = data[33:dlen - 32]
mac = data[dlen - 32:dlen]
return (nonce, ciphertext, mac)
def hmac_aad(key, message, aad):
if len(aad) != 32: raise Exception('AAD associated data must be 32 bytes');
return hmac(sha256, key, concat(aad, message));
# Calculates long-term key between users A and B: `get_key(Apriv, Bpub) == get_key(Bpriv, Apub)`
def get_conversation_key(private_key_a, public_key_b):
shared_x = secp256k1_ecdh(private_key_a, public_key_b)
return hkdf_extract(IKM=shared_x, salt=utf8_encode('nip44-v2'))
# Calculates unique per-message key
def get_message_keys(conversation_key, nonce):
if len(conversation_key) != 32: raise Exception('invalid conversation_key length')
if len(nonce) != 32: raise Exception('invalid nonce length')
keys = hkdf_expand(OKM=conversation_key, info=nonce, L=76)
chacha_key = keys[0:32]
chacha_nonce = keys[32:44]
hmac_key = keys[44:76]
return (chacha_key, chacha_nonce, hmac_key)
def encrypt(plaintext, conversation_key, nonce):
(chacha_key, chacha_nonce, hmac_key) = get_message_keys(conversation_key, nonce)
padded = pad(plaintext)
ciphertext = chacha20(key=chacha_key, nonce=chacha_nonce, data=padded)
mac = hmac_aad(key=hmac_key, message=ciphertext, aad=nonce)
return base64_encode(concat(write_u8(2), nonce, ciphertext, mac))
def decrypt(payload, conversation_key):
(nonce, ciphertext, mac) = decode_payload(payload)
(chacha_key, chacha_nonce, hmac_key) = get_message_keys(conversation_key, nonce)
calculated_mac = hmac_aad(key=hmac_key, message=ciphertext, aad=nonce)
if not is_equal_ct(calculated_mac, mac): raise Exception('invalid MAC')
padded_plaintext = chacha20(key=chacha_key, nonce=chacha_nonce, data=ciphertext)
return unpad(padded_plaintext)
# Usage:
# conversation_key = get_conversation_key(sender_privkey, recipient_pubkey)
# nonce = secure_random_bytes(32)
# payload = encrypt('hello world', conversation_key, nonce)
# 'hello world' == decrypt(payload, conversation_key)
```
### Audit
The v2 of the standard was audited by [Cure53](https://cure53.de) in December 2023.
Check out [audit-2023.12.pdf](https://github.com/paulmillr/nip44/blob/ce63c2eaf345e9f7f93b48f829e6bdeb7e7d7964/audit-2023.12.pdf)
and [auditor's website](https://cure53.de/audit-report_nip44-implementations.pdf).
### Tests and code
A collection of implementations in different languages is available at https://github.com/paulmillr/nip44.
We publish extensive test vectors. Instead of having it in the document directly, a sha256 checksum of vectors is provided:
269ed0f69e4c192512cc779e78c555090cebc7c785b609e338a62afc3ce25040 nip44.vectors.json
Example of a test vector from the file:
```json
{
"sec1": "0000000000000000000000000000000000000000000000000000000000000001",
"sec2": "0000000000000000000000000000000000000000000000000000000000000002",
"conversation_key": "c41c775356fd92eadc63ff5a0dc1da211b268cbea22316767095b2871ea1412d",
"nonce": "0000000000000000000000000000000000000000000000000000000000000001",
"plaintext": "a",
"payload": "AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABee0G5VSK0/9YypIObAtDKfYEAjD35uVkHyB0F4DwrcNaCXlCWZKaArsGrY6M9wnuTMxWfp1RTN9Xga8no+kF5Vsb"
}
```
The file also contains intermediate values. A quick guidance with regards to its usage:
- `valid.get_conversation_key`: calculate conversation_key from secret key sec1 and public key pub2
- `valid.get_message_keys`: calculate chacha_key, chacha_nocne, hmac_key from conversation_key and nonce
- `valid.calc_padded_len`: take unpadded length (first value), calculate padded length (second value)
- `valid.encrypt_decrypt`: emulate real conversation. Calculate pub2 from sec2, verify conversation_key from (sec1, pub2), encrypt, verify payload, then calculate pub1 from sec1, verify conversation_key from (sec2, pub1), decrypt, verify plaintext.
- `valid.encrypt_decrypt_long_msg`: same as previous step, but instead of a full plaintext and payload, their checksum is provided.
- `invalid.encrypt_msg_lengths`
- `invalid.get_conversation_key`: calculating conversation_key must throw an error
- `invalid.decrypt`: decrypting message content must throw an error

11
45.md
View File

@@ -27,7 +27,9 @@ In case a relay uses probabilistic counts, it MAY indicate it in the response wi
["COUNT", <subscription_id>, {"count": <integer>}]
```
## Examples:
Whenever the relay decides to refuse to fulfill the `COUNT` request, it MUST return a `CLOSED` message.
## Examples
### Followers count
@@ -49,3 +51,10 @@ In case a relay uses probabilistic counts, it MAY indicate it in the response wi
["COUNT", <subscription_id>, {"kinds": [1]}]
["COUNT", <subscription_id>, {"count": 93412452, "approximate": true}]
```
### Relay refuses to count
```
["COUNT", <subscription_id>, {"kinds": [4], "authors": [<pubkey>], "#p": [<pubkey>]}]
["CLOSED", <subscription_id>, "auth-required: cannot count other people's DMs"]
```

191
46.md
View File

@@ -2,161 +2,98 @@ NIP-46
======
Nostr Connect
------------------------
-------------
`draft` `optional`
## Rationale
This NIP describes a method for 2-way communication between a **remote signer** and a normal Nostr client. The remote signer could be, for example, a hardware device dedicated to signing Nostr events, while the client is a normal Nostr client.
Private keys should be exposed to as few systems - apps, operating systems, devices - as possible as each system adds to the attack surface.
## Signer Discovery
Entering private keys can also be annoying and requires exposing them to even more systems such as the operating system's clipboard that might be monitored by malicious apps.
The client always starts by generating a random key which is used to communicate with the signer, then it one of the methods below is used to allow the client to know what is the signer public key for the session and which relays to use.
### Started by the signer (nsecBunker)
## Terms
The remote signer generates a connection token in the form
* **App**: Nostr app on any platform that *requires* to act on behalf of a nostr account.
* **Signer**: Nostr app that holds the private key of a nostr account and *can sign* on its behalf.
```
<npub1...>#<optional-secret>?relay=wss://...&relay=wss://...
```
The user copies that token and pastes it in the client UI somehow. Then the client can send events of kind `24133` to the specified relays and wait for responses from the remote signer.
## `TL;DR`
### Started by the client
The client generates a QR code in the following form (URL-encoded):
**App** and **Signer** sends ephemeral encrypted messages to each other using kind `24133`, using a relay of choice.
```
nostrconnect://<client-key-hex>?relay=wss://...&metadata={"name":"...", "url": "...", "description": "..."}
```
App prompts the Signer to do things such as fetching the public key or signing events.
The signer scans the QR code and sends a `connect` message to the client in the specified relays.
The `content` field must be an encrypted JSONRPC-ish **request** or **response**.
## Event payloads
## Signer Protocol
Event payloads are [NIP-04](04.md)-encrypted JSON blobs that look like JSONRPC messages (their format is specified inside the `.content` of the event formats nelow).
### Messages
Events sent by the client to the remote signer have the following format:
#### Request
```json
```js
{
"id": <random_string>,
"method": <one_of_the_methods>,
"params": [<anything>, <else>]
"pubkey": "<client-key-hex>"
"kind": 24133,
"tags": [
["p", "<signer-key-hex>"]
],
"content": "nip04_encrypted_json({id: <random-string>, method: <see-below>, params: [array_of_strings]})",
...
}
```
#### Response
And the events the remote signer sends to the client have the following format:
```json
{
"id": <request_id>,
"result": <anything>,
"error": <reason>
}
```js
"pubkey": "<signer-key-hex>"
"kind": 24133,
"tags": [
["p", "<client-key-hex>"]
],
"content": "nip04_encrypted_json({id: <request-id>, result: <string>, error: <reason-string>})",
...
```
The signer key will always be the key of the user who controls the signer device.
### Methods
#### Mandatory
These are mandatory methods the remote signer app MUST implement:
- **describe**
- params []
- result `["describe", "get_public_key", "sign_event", "connect", "disconnect", "delegate", ...]`
- **get_public_key**
- params []
- result `pubkey`
- **sign_event**
- params [`event`]
- result `event_with_signature`
#### optional
- **connect**
- params [`pubkey`]
- **disconnect**
- params []
- **delegate**
- params [`delegatee`, `{ kind: number, since: number, until: number }`]
- result `{ from: string, to: string, cond: string, sig: string }`
- params: [`pubkey`, `secret`]
- result: `"ack"`
- **get_public_key**
- params: []
- result: `pubkey-hex`
- **sign_event**
- params: [`event`]
- result: `json_string(event_with_pubkey_id_and_signature)`
- **get_relays**
- params []
- result `{ [url: string]: {read: boolean, write: boolean} }`
- params: []
- result: `json_string({[url: string]: {read: boolean, write: boolean}})`
- **nip04_encrypt**
- params [`pubkey`, `plaintext`]
- result `nip4 ciphertext`
- params: [`third-party-pubkey`, `plaintext`]
- result: `nip04-ciphertext`
- **nip04_decrypt**
- params [`pubkey`, `nip4 ciphertext`]
- result [`plaintext`]
NOTICE: `pubkey` and `signature` are hex-encoded strings.
### Nostr Connect URI
**Signer** discovers **App** by scanning a QR code, clicking on a deep link or copy-pasting an URI.
The **App** generates a special URI with prefix `nostrconnect://` and base path the hex-encoded `pubkey` with the following querystring parameters **URL encoded**
- `relay` URL of the relay of choice where the **App** is connected and the **Signer** must send and listen for messages.
- `metadata` metadata JSON of the **App**
- `name` human-readable name of the **App**
- `url` (optional) URL of the website requesting the connection
- `description` (optional) description of the **App**
- `icons` (optional) array of URLs for icons of the **App**.
#### JavaScript
```js
const uri = `nostrconnect://<pubkey>?relay=${encodeURIComponent("wss://relay.damus.io")}&metadata=${encodeURIComponent(JSON.stringify({"name": "Example"}))}`
```
#### Example
```sh
nostrconnect://b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4?relay=wss%3A%2F%2Frelay.damus.io&metadata=%7B%22name%22%3A%22Example%22%7D
```
## Flows
The `content` field contains encrypted message as specified by [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md). The `kind` chosen is `24133`.
### Connect
1. User clicks on **"Connect"** button on a website or scan it with a QR code
2. It will show an URI to open a "nostr connect" enabled **Signer**
3. In the URI there is a pubkey of the **App** ie. `nostrconnect://<pubkey>&relay=<relay>&metadata=<metadata>`
4. The **Signer** will send a message to ACK the `connect` request, along with his public key
### Disconnect (from App)
1. User clicks on **"Disconnect"** button on the **App**
2. The **App** will send a message to the **Signer** with a `disconnect` request
3. The **Signer** will send a message to ACK the `disconnect` request
### Disconnect (from Signer)
1. User clicks on **"Disconnect"** button on the **Signer**
2. The **Signer** will send a message to the **App** with a `disconnect` request
### Get Public Key
1. The **App** will send a message to the **Signer** with a `get_public_key` request
3. The **Signer** will send back a message with the public key as a response to the `get_public_key` request
### Sign Event
1. The **App** will send a message to the **Signer** with a `sign_event` request along with the **event** to be signed
2. The **Signer** will show a popup to the user to inspect the event and sign it
3. The **Signer** will send back a message with the event including the `id` and the schnorr `signature` as a response to the `sign_event` request
### Delegate
1. The **App** will send a message with metadata to the **Signer** with a `delegate` request along with the **conditions** query string and the **pubkey** of the **App** to be delegated.
2. The **Signer** will show a popup to the user to delegate the **App** to sign on his behalf
3. The **Signer** will send back a message with the signed [NIP-26 delegation token](https://github.com/nostr-protocol/nips/blob/master/26.md) or reject it
- params: [`third-party-pubkey`, `nip04-ciphertext`]
- result: `plaintext`
- **nip44_get_key**
- params: [`third-party-pubkey`]
- result: `nip44-conversation-key`
- **nip44_encrypt**
- params: [`third-party-pubkey`, `plaintext`]
- result: `nip44-ciphertext`
- **nip44_decrypt**
- params: [`third-party-pubkey`, `nip44-ciphertext`]
- result: `plaintext`
- **ping**
- params: []
- result: `"pong"`

2
47.md
View File

@@ -129,7 +129,7 @@ Errors:
## Example pay invoice flow
0. The user scans the QR code generated by the **wallet service** with their **client** application, they follow a `nostr+walletconnect:` deeplink or configure the connection details manually.
1. **client** sends an event to the **wallet service** service with kind `23194`. The content is a `pay_invoice` request. The private key is the secret from the connection string above.
1. **client** sends an event to the **wallet service** with kind `23194`. The content is a `pay_invoice` request. The private key is the secret from the connection string above.
2. **wallet service** verifies that the author's key is authorized to perform the payment, decrypts the payload and sends the payment.
3. **wallet service** responds to the event by sending an event with kind `23195` and content being a response either containing an error message or a preimage.

197
51.md
View File

@@ -6,148 +6,107 @@ Lists
`draft` `optional`
A "list" event is defined as having a list of public and/or private tags. Public tags will be listed in the event `tags`. Private tags will be encrypted in the event `content`. Encryption for private tags will use [NIP-04 - Encrypted Direct Message](04.md) encryption, using the list author's private and public key for the shared secret. A distinct event kind should be used for each list type created.
This NIP defines lists of things that users can create. Lists can contain references to anything, and these references can be **public** or **private**.
If a list should only be defined once per user (like the "mute" list) the list is declared as a _replaceable event_. These lists may be referred to as "replaceable lists". Otherwise, the list is a _parameterized replaceable event_ and the list name will be used as the `d` tag. These lists may be referred to as "parameterized replaceable lists".
Public items in a list are specified in the event `tags` array, while private items are specified in a JSON array that mimics the structure of the event `tags` array, but stringified and encrypted using the same scheme from [NIP-04](04.md) (the shared key is computed using the author's public and private key) and stored in the `.content`.
## Replaceable List Event Example
## Types of lists
Lets say a user wants to create a 'Mute' list and has keys:
```
priv: fb505c65d4df950f5d28c9e4d285ee12ffaf315deef1fc24e3c7cd1e7e35f2b1
pub: b1a5c93edcc8d586566fde53a20bdb50049a97b15483cb763854e57016e0fa3d
```
The user wants to publicly include these users:
## Standard lists
```json
["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"],
["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"]
```
and privately include these users (below is the JSON that would be encrypted and placed in the event content):
Standard lists use non-parameterized replaceable events, meaning users may only have a single list of each kind. They have special meaning and clients may rely on them to augment a user's profile or browsing experience.
```json
[
["p", "9ec7a778167afb1d30c4833de9322da0c08ba71a69e1911d5578d3144bb56437"],
["p", "8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168"]
]
```
For example, _mute lists_ can contain the public keys of spammers and bad actors users don't want to see in their feeds or receive annoying notifications from.
Then the user would create a 'Mute' list event like below:
| name | kind | description | expected tag items |
| --- | --- | --- | --- |
| Mute list | 10000 | things the user doesn't want to see in their feeds | `"p"` (pubkeys), `"t"` (hashtags), `"word"` (lowercase string), `"e"` (threads) |
| Pinned notes | 10001 | events the user intends to showcase in their profile page | `"e"` (kind:1 notes) |
| Bookmarks | 10003 | uncategorized, "global" list of things a user wants to save | `"e"` (kind:1 notes), `"a"` (kind:30023 articles), `"t"` (hashtags), `"r" (URLs)` |
| Communities | 10004 | [NIP-72](72.md) communities the user belongs to | `"a"` (kind:34550 community definitions) |
| Public chats | 10005 | [NIP-28](28.md) chat channels the user is in | `"e"` (kind:40 channel definitions) |
| Blocked relays | 10006 | relays clients should never connect to | `"relay"` (relay URLs) |
| Search relays | 10007 | relays clients should use when performing search queries | `"relay"` (relay URLs) |
| 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) |
## 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.
For example, _relay sets_ can be displayed in a dropdown UI to give users the option to switch to which relays they will publish an event or from which relays they will read the replies to an event; _curation sets_ can be used by apps to showcase curations made by others tagged to different topics.
Aside from their main identifier, the `"d"` tag, sets can optionally have a `"title"`, an `"image"` and a `"description"` tags that can be used to enhance their UI.
| name | kind | description | expected tag items |
| --- | --- | --- | --- |
| Follow sets | 30000 | categorized groups of users a client may choose to check out in different circumstances | `"p"` (pubkeys) |
| Relay sets | 30002 | user-defined relay groups the user can easily pick and choose from during various operations | `"relay"` (relay URLs) |
| Bookmark sets | 30003 | user-defined bookmarks categories , for when bookmarks must be in labeled separate groups | `"e"` (kind:1 notes), `"a"` (kind:30023 articles), `"t"` (hashtags), `"r" (URLs)` |
| Curation sets | 30004 | groups of articles picked by users as interesting and/or belonging to the same category | `"a"` (kind:30023 articles), `"e"` (kind:1 notes) |
| Interest sets | 30015 | interest topics represented by a bunch of "hashtags" | `"t"` (hashtags) |
| Emoji sets | 30030 | categorized emoji groups | `"emoji"` (see [NIP-30](30.md)) |
## Deprecated standard lists
Some clients have used these lists in the past, but they should work on transitioning to the [standard formats](#standard-lists) above.
| kind | "d" tag | use instead |
| --- | --- | --- |
| 30000 | `"mute"` | kind 10000 _mute list_ |
| 30001 | `"pin"` | kind 10001 _pin list_ |
| 30001 | `"bookmark"` | kind 10003 _bookmarks list_ |
| 30001 | `"communities"` | kind 10004 _communities list_ |
## Examples
### A _mute list_ with some public items and some encrypted items
```json
{
"id": "a92a316b75e44cfdc19986c634049158d4206fcc0b7b9c7ccbcdabe28beebcd0",
"pubkey": "854043ae8f1f97430ca8c1f1a090bdde6488bd5115c7a45307a2a212750ae4cb",
"created_at": 1699597889,
"kind": 10000,
"tags": [
["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"],
["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"],
["p", "07caba282f76441955b695551c3c5c742e5b9202a3784780f8086fdcdc1da3a9"],
["p", "a55c15f5e41d5aebd236eca5e0142789c5385703f1a7485aa4b38d94fd18dcc4"]
],
"content": "VezuSvWak++ASjFMRqBPWS3mK5pZ0vRLL325iuIL4S+r8n9z+DuMau5vMElz1tGC/UqCDmbzE2kwplafaFo/FnIZMdEj4pdxgptyBV1ifZpH3TEF6OMjEtqbYRRqnxgIXsuOSXaerWgpi0pm+raHQPseoELQI/SZ1cvtFqEUCXdXpa5AYaSd+quEuthAEw7V1jP+5TDRCEC8jiLosBVhCtaPpLcrm8HydMYJ2XB6Ixs=?iv=/rtV49RFm0XyFEwG62Eo9A==",
...other fields
"content": "TJob1dQrf2ndsmdbeGU+05HT5GMnBSx3fx8QdDY/g3NvCa7klfzgaQCmRZuo1d3WQjHDOjzSY1+MgTK5WjewFFumCcOZniWtOMSga9tJk1ky00tLoUUzyLnb1v9x95h/iT/KpkICJyAwUZ+LoJBUzLrK52wNTMt8M5jSLvCkRx8C0BmEwA/00pjOp4eRndy19H4WUUehhjfV2/VV/k4hMAjJ7Bb5Hp9xdmzmCLX9+64+MyeIQQjQAHPj8dkSsRahP7KS3MgMpjaF8nL48Bg5suZMxJayXGVp3BLtgRZx5z5nOk9xyrYk+71e2tnP9IDvSMkiSe76BcMct+m7kGVrRcavDI4n62goNNh25IpghT+a1OjjkpXt9me5wmaL7fxffV1pchdm+A7KJKIUU3kLC7QbUifF22EucRA9xiEyxETusNludBXN24O3llTbOy4vYFsq35BeZl4v1Cse7n2htZicVkItMz3wjzj1q1I1VqbnorNXFgllkRZn4/YXfTG/RMnoK/bDogRapOV+XToZ+IvsN0BqwKSUDx+ydKpci6htDRF2WDRkU+VQMqwM0CoLzy2H6A2cqyMMMD9SLRRzBg==?iv=S3rFeFr1gsYqmQA7bNnNTQ==",
"sig": "1173822c53261f8cffe7efbf43ba4a97a9198b3e402c2a1df130f42a8985a2d0d3430f4de350db184141e45ca844ab4e5364ea80f11d720e36357e1853dba6ca"
}
```
### A _curation set_ of articles and notes about yaks
## Parameterized Replaceable List Event Example
Lets say a user wants to create a 'Categorized People' list of `nostr` people and has keys:
```
priv: fb505c65d4df950f5d28c9e4d285ee12ffaf315deef1fc24e3c7cd1e7e35f2b1
pub: b1a5c93edcc8d586566fde53a20bdb50049a97b15483cb763854e57016e0fa3d
```
The user wants to publicly include these users:
```json
["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"],
["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"]
```
and privately include these users (below is the JSON that would be encrypted and placed in the event content):
```json
[
["p", "9ec7a778167afb1d30c4833de9322da0c08ba71a69e1911d5578d3144bb56437"],
["p", "8c0da4862130283ff9e67d889df264177a508974e2feb96de139804ea66d6168"]
]
```
Then the user would create a 'Categorized People' list event like below:
```json
{
"kind": 30000,
"id": "567b41fc9060c758c4216fe5f8d3df7c57daad7ae757fa4606f0c39d4dd220ef",
"pubkey": "d6dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c",
"created_at": 1695327657,
"kind": 30004,
"tags": [
["d", "nostr"],
["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"],
["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"],
["d", "jvdy9i4"],
["name", "Yaks"],
["picture", "https://cdn.britannica.com/40/188540-050-9AC748DE/Yak-Himalayas-Nepal.jpg"],
["about", "The domestic yak, also known as the Tartary ox, grunting ox, or hairy cattle, is a species of long-haired domesticated cattle found throughout the Himalayan region of the Indian subcontinent, the Tibetan Plateau, Gilgit-Baltistan, Tajikistan and as far north as Mongolia and Siberia."],
["a", "30023:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:95ODQzw3ajNoZ8SyMDOzQ"],
["a", "30023:54af95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:1-MYP8dAhramH9J5gJWKx"],
["a", "30023:f8fe95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:D2Tbd38bGrFvU0bIbvSMt"],
["e", "d78ba0d5dce22bfff9db0a9e996c9ef27e2c91051de0c4e1da340e0326b4941e"]
],
"content": "VezuSvWak++ASjFMRqBPWS3mK5pZ0vRLL325iuIL4S+r8n9z+DuMau5vMElz1tGC/UqCDmbzE2kwplafaFo/FnIZMdEj4pdxgptyBV1ifZpH3TEF6OMjEtqbYRRqnxgIXsuOSXaerWgpi0pm+raHQPseoELQI/SZ1cvtFqEUCXdXpa5AYaSd+quEuthAEw7V1jP+5TDRCEC8jiLosBVhCtaPpLcrm8HydMYJ2XB6Ixs=?iv=/rtV49RFm0XyFEwG62Eo9A==",
...other fields
"content": "",
"sig": "a9a4e2192eede77e6c9d24ddfab95ba3ff7c03fbd07ad011fff245abea431fb4d3787c2d04aad001cb039cb8de91d83ce30e9a94f82ac3c5a2372aa1294a96bd"
}
```
Lets say a user wants to create a 'Categorized Bookmarks' list of `bookmarks` and has keys:
```
priv: fb505c65d4df950f5d28c9e4d285ee12ffaf315deef1fc24e3c7cd1e7e35f2b1
pub: b1a5c93edcc8d586566fde53a20bdb50049a97b15483cb763854e57016e0fa3d
```
The user wants to publicly include these bookmarks:
## Encryption process pseudocode
```json
["e", "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "wss://nostr.example.com"],
["a", "30023:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", "wss://nostr.example.com"],
["r", "https://github.com/nostr-protocol/nostr", "Nostr repository"],
```
and privately include these bookmarks (below is the JSON that would be encrypted and placed in the event content):
```json
[
["r", "https://my-private.bookmark", "My private bookmark"],
["a", "30001:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", "wss://nostr.example.com"],
```scala
val private_items = [
["p", "07caba282f76441955b695551c3c5c742e5b9202a3784780f8086fdcdc1da3a9"],
["a", "a55c15f5e41d5aebd236eca5e0142789c5385703f1a7485aa4b38d94fd18dcc4"],
]
val base64blob = nip04.encrypt(json.encode_to_string(private_items))
event.content = base64blob
```
Then the user would create a 'Categorized Bookmarks' list event like below:
```json
{
"kind": 30001,
"tags": [
["d", "bookmarks"],
["e", "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "wss://nostr.example.com"],
["a", "30023:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", "wss://nostr.example.com"],
["r", "https://github.com/nostr-protocol/nostr", "Nostr repository"],
],
"content": "y3AyaLJfnmYr9x9Od9o4aYrmL9+Ynmsim5y2ONrU0urOTq+V81CyAthQ2mUOWE9xwGgrizhY7ILdQwWhy6FK0sA33GHtC0egUJw1zIdknPe7BZjznD570yk/8RXYgGyDKdexME+RMYykrnYFxq1+y/h00kmJg4u+Gpn+ZjmVhNYxl9b+TiBOAXG9UxnK/H0AmUqDpcldn6+j1/AiStwYZhD1UZ3jzDIk2qcCDy7MlGnYhSP+kNmG+2b0T/D1L0Z7?iv=PGJJfPE84gacAh7T0e6duQ==",
...other fields
}
```
## List Event Kinds
| kind | list type |
| ------ | ----------------------- |
| 10000 | Mute |
| 10001 | Pin |
| 30000 | Categorized People |
| 30001 | Categorized Bookmarks |
| 30002 | Categorized Relay Sets |
### Mute List
An event with kind `10000` is defined as a replaceable list event for listing content a user wants to mute. Any standardized tag can be included in a Mute List.
### Pin List
An event with kind `10001` is defined as a replaceable list event for listing content a user wants to pin. Any standardized tag can be included in a Pin List.
### Categorized People List
An event with kind `30000` is defined as a parameterized replaceable list event for categorizing people. The 'd' parameter for this event holds the category name of the list. The tags included in these lists MUST follow the format of kind 3 events as defined in [NIP-02 - Contact List and Petnames](02.md).
### Categorized Bookmarks List
An event of kind `30001` is defined as a parameterized replaceable list event for categorizing bookmarks. The 'd' parameter for this event holds the category name of the list. The bookmark lists may contain metadata tags such as 'title', 'image', 'summary' as defined in [NIP-23 - Long-form Content](23.md). Any standardized tag can be included in a Categorized Bookmark List.
### Categorized Relay Set
An event of kind `30002` is defined as a parameterized replaceable list event for categorizing relays. The 'd' parameter for this event holds the category name of the list. The relays lists may contain metadata tags such as 'title', 'image', 'summary' as defined in [NIP-23 - Long-form Content](23.md). These sets can be used by clients in order to determine which relays to query in different scenarios.

34
52.md
View File

@@ -22,19 +22,22 @@ This kind of calendar event starts on a date and ends before a different date in
The format uses a parameterized replaceable event kind `31922`.
The `.content` of these events is optional and should be a detailed description of the calendar event.
The `.content` of these events should be a detailed description of the calendar event. It is required but can be an empty string.
The list of tags are as follows:
* `d` (required) universally unique identifier (UUID). Generated by the client creating the calendar event.
* `name` (required) name of the calendar event
* `title` (required) title of the calendar event
* `start` (required) inclusive start date in ISO 8601 format (YYYY-MM-DD). Must be less than `end`, if it exists.
* `end` (optional) exclusive end date in ISO 8601 format (YYYY-MM-DD). If omitted, the calendar event ends on the same date as `start`.
* `location` (optional) location of the calendar event. e.g. address, GPS coordinates, meeting room name, link to video call
* `location` (optional, repeated) location of the calendar event. e.g. address, GPS coordinates, meeting room name, link to video call
* `g` (optional) [geohash](https://en.wikipedia.org/wiki/Geohash) to associate calendar event with a searchable physical location
* `p` (optional, repeated) 32-bytes hex pubkey of a participant, optional recommended relay URL, and participant's role in the meeting
* `t` (optional, repeated) hashtag to categorize calendar event
* `r` (optional, repeated) references / links to web pages, documents, video calls, recorded videos, etc.
The following tags are deprecated:
* `name` name of the calendar event. Use only if `title` is not available.
```json
{
"id": <32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>,
@@ -45,7 +48,7 @@ The list of tags are as follows:
"tags": [
["d", "<UUID>"],
["name", "<name of calendar event>"],
["title", "<title of calendar event>"],
// Dates
["start", "<YYYY-MM-DD>"],
@@ -78,21 +81,24 @@ This kind of calendar event spans between a start time and end time.
The format uses a parameterized replaceable event kind `31923`.
The `.content` of these events is optional and should be a detailed description of the calendar event.
The `.content` of these events should be a detailed description of the calendar event. It is required but can be an empty string.
The list of tags are as follows:
* `d` (required) universally unique identifier (UUID). Generated by the client creating the calendar event.
* `name` (required) name of the calendar event
* `title` (required) title of the calendar event
* `start` (required) inclusive start Unix timestamp in seconds. Must be less than `end`, if it exists.
* `end` (optional) exclusive end Unix timestamp in seconds. If omitted, the calendar event ends instantaneously.
* `start_tzid` (optional) time zone of the start timestamp, as defined by the IANA Time Zone Database. e.g., `America/Costa_Rica`
* `end_tzid` (optional) time zone of the end timestamp, as defined by the IANA Time Zone Database. e.g., `America/Costa_Rica`. If omitted and `start_tzid` is provided, the time zone of the end timestamp is the same as the start timestamp.
* `location` (optional) location of the calendar event. e.g. address, GPS coordinates, meeting room name, link to video call
* `location` (optional, repeated) location of the calendar event. e.g. address, GPS coordinates, meeting room name, link to video call
* `g` (optional) [geohash](https://en.wikipedia.org/wiki/Geohash) to associate calendar event with a searchable physical location
* `p` (optional, repeated) 32-bytes hex pubkey of a participant, optional recommended relay URL, and participant's role in the meeting
* `t` (optional, repeated) hashtag to categorize calendar event
* `r` (optional, repeated) references / links to web pages, documents, video calls, recorded videos, etc.
The following tags are deprecated:
* `name` name of the calendar event. Use only if `title` is not available.
```json
{
"id": <32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>,
@@ -103,7 +109,7 @@ The list of tags are as follows:
"tags": [
["d", "<UUID>"],
["name", "<name of calendar event>"],
["title", "<title of calendar event>"],
// Timestamps
["start", "<Unix timestamp in seconds>"],
@@ -137,15 +143,23 @@ A calendar is a collection of calendar events, represented as a custom replaceab
### Format
The `.content` of these events should be a detailed description of the calendar. It is required but can be an empty string.
The format uses a custom replaceable list of kind `31924` with a list of tags as described below:
* `d` (required) calendar name
* `d` (required) universally unique identifier. Generated by the client creating the calendar.
* `title` (required) calendar title
* `a` (repeated) reference tag to kind `31922` or `31923` calendar event being responded to
```json
{
"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": 31924,
"content": "<description of calendar>",
"tags": [
["d", "<calendar name>"],
["d", "<UUID>"],
["title", "<calendar title>"],
["a", "<31922 or 31923>:<calendar event author pubkey>:<d-identifier of calendar event>", "<optional relay url>"],
["a", "<31922 or 31923>:<calendar event author pubkey>:<d-identifier of calendar event>", "<optional relay url>"]
]

2
53.md
View File

@@ -6,7 +6,7 @@ Live Activities
`draft` `optional`
Service providers want to offer live activities to the Nostr network in such a way that participants can easily logged and queried by clients. This NIP describes a general framework to advertise the involvement of pubkeys in such live activities.
Service providers want to offer live activities to the Nostr network in such a way that participants can easily log and query by clients. This NIP describes a general framework to advertise the involvement of pubkeys in such live activities.
## Concepts

11
57.md
View File

@@ -36,7 +36,7 @@ A `zap request` is an event of kind `9734` that is _not_ published to relays, bu
In addition, the event MAY include the following tags:
- `e` is an optional hex-encoded event id. Clients MUST include this if zapping an event rather than a person.
- `a` is an optional NIP-33 event coordinate that allows tipping parameterized replaceable events such as NIP-23 long-form notes.
- `a` is an optional event coordinate that allows tipping parameterized replaceable events such as NIP-23 long-form notes.
Example:
@@ -110,7 +110,8 @@ When a client sends a `zap request` event to a server's lnurl-pay callback URL,
4. It MUST have 0 or 1 `e` tags
5. There should be a `relays` tag with the relays to send the `zap receipt` to.
6. If there is an `amount` tag, it MUST be equal to the `amount` query parameter.
7. If there is an `a` tag, it MUST be a valid NIP-33 event coordinate
7. If there is an `a` tag, it MUST be a valid event coordinate
8. There MUST be 0 or 1 `P` tags. If there is one, it MUST be equal to the `zap receipt`'s `pubkey`.
The event MUST then be stored for use later, when the invoice is paid.
@@ -128,7 +129,7 @@ The following should be true of the `zap receipt` event:
- The `content` SHOULD be empty.
- The `created_at` date SHOULD be set to the invoice `paid_at` date for idempotency.
- `tags` MUST include the `p` tag AND optional `e` tag from the `zap request` AND optional `a` tag from the `zap request`.
- `tags` MUST include the `p` tag (zap recipient) AND optional `e` tag from the `zap request` AND optional `a` tag from the `zap request` AND optional `P` tag from the pubkey of the zap request (zap sender).
- The `zap receipt` MUST have a `bolt11` tag containing the description hash bolt11 invoice.
- The `zap receipt` MUST contain a `description` tag which is the JSON-encoded invoice description.
- `SHA256(description)` MUST match the description hash in the bolt11 invoice.
@@ -148,13 +149,13 @@ Example `zap receipt`:
"kind": 9735,
"tags": [
["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"],
["P", "97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322"],
["e", "3624762a1274dd9636e0c552b53086d70bc88c165bc4dc0f9e836a1eaf86c3b8"],
["bolt11", "lnbc10u1p3unwfusp5t9r3yymhpfqculx78u027lxspgxcr2n2987mx2j55nnfs95nxnzqpp5jmrh92pfld78spqs78v9euf2385t83uvpwk9ldrlvf6ch7tpascqhp5zvkrmemgth3tufcvflmzjzfvjt023nazlhljz2n9hattj4f8jq8qxqyjw5qcqpjrzjqtc4fc44feggv7065fqe5m4ytjarg3repr5j9el35xhmtfexc42yczarjuqqfzqqqqqqqqlgqqqqqqgq9q9qxpqysgq079nkq507a5tw7xgttmj4u990j7wfggtrasah5gd4ywfr2pjcn29383tphp4t48gquelz9z78p4cq7ml3nrrphw5w6eckhjwmhezhnqpy6gyf0"],
["description", "{\"pubkey\":\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\",\"content\":\"\",\"id\":\"d9cc14d50fcb8c27539aacf776882942c1a11ea4472f8cdec1dea82fab66279d\",\"created_at\":1674164539,\"sig\":\"77127f636577e9029276be060332ea565deaf89ff215a494ccff16ae3f757065e2bc59b2e8c113dd407917a010b3abd36c8d7ad84c0e3ab7dab3a0b0caa9835d\",\"kind\":9734,\"tags\":[[\"e\",\"3624762a1274dd9636e0c552b53086d70bc88c165bc4dc0f9e836a1eaf86c3b8\"],[\"p\",\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\"],[\"relays\",\"wss://relay.damus.io\",\"wss://nostr-relay.wlvs.space\",\"wss://nostr.fmt.wiz.biz\",\"wss://relay.nostr.bg\",\"wss://nostr.oxtr.dev\",\"wss://nostr.v0l.io\",\"wss://brb.io\",\"wss://nostr.bitcoiner.social\",\"ws://monad.jb55.com:8080\",\"wss://relay.snort.social\"]]}"],
["description", "{\"pubkey\":\"97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322\",\"content\":\"\",\"id\":\"d9cc14d50fcb8c27539aacf776882942c1a11ea4472f8cdec1dea82fab66279d\",\"created_at\":1674164539,\"sig\":\"77127f636577e9029276be060332ea565deaf89ff215a494ccff16ae3f757065e2bc59b2e8c113dd407917a010b3abd36c8d7ad84c0e3ab7dab3a0b0caa9835d\",\"kind\":9734,\"tags\":[[\"e\",\"3624762a1274dd9636e0c552b53086d70bc88c165bc4dc0f9e836a1eaf86c3b8\"],[\"p\",\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\"],[\"relays\",\"wss://relay.damus.io\",\"wss://nostr-relay.wlvs.space\",\"wss://nostr.fmt.wiz.biz\",\"wss://relay.nostr.bg\",\"wss://nostr.oxtr.dev\",\"wss://nostr.v0l.io\",\"wss://brb.io\",\"wss://nostr.bitcoiner.social\",\"ws://monad.jb55.com:8080\",\"wss://relay.snort.social\"]]}"],
["preimage", "5d006d2cf1e73c7148e7519a4c68adc81642ce0e25a432b2434c99f97344c15f"]
],
"content": "",
"sig": "b0a3c5c984ceb777ac455b2f659505df51585d5fd97a0ec1fdb5f3347d392080d4b420240434a3afd909207195dac1e2f7e3df26ba862a45afd8bfe101c2b1cc"
}
```

2
58.md
View File

@@ -62,8 +62,6 @@ Users MAY choose to decorate their profiles with badges for fame, notoriety, rec
### Recommendations
Badge issuers MAY include some Proof of Work as per [NIP-13](13.md) when minting Badge Definitions or Badge Awards to embed them with a combined energy cost, arguably making them more special and valuable for users that wish to collect them.
Clients MAY whitelist badge issuers (pubkeys) for the purpose of ensuring they retain a valuable/special factor for their users.
Badge image recommended aspect ratio is 1:1 with a high-res size of 1024x1024 pixels.

4
75.md
View File

@@ -35,6 +35,8 @@ Example event:
The following tags are OPTIONAL.
- `closed_at` - timestamp for determining which zaps are included in the tally. Zap receipts published after the `closed_at` timestamp SHOULD NOT count towards the goal progress.
- `image` - an image for the goal
- `summary` - a brief description
```json
{
@@ -43,6 +45,8 @@ The following tags are OPTIONAL.
["relays", "wss://alicerelay.example.com", "wss://bobrelay.example.com", ...],
["amount", "210000"],
["closed_at", "<unix timestamp in seconds>"],
["image", "<image URL>"],
["summary", "<description of the goal>"],
],
"content": "Nostrasia travel expenses",
...

8
89.md
View File

@@ -65,7 +65,7 @@ The third value of the tag SHOULD be the platform where this recommendation migh
* `content` is an optional `metadata`-like stringified JSON object, as described in NIP-01. This content is useful when the pubkey creating the `kind:31990` is not an application. If `content` is empty, the `kind:0` of the pubkey should be used to display application information (e.g. name, picture, web, LUD16, etc.)
* `k` tags' value is the event kind that is supported by this `kind:31990`.
Using a `k` tag(s) (instead of having the kind onf the NIP-33 `d` tag) provides:
Using a `k` tag(s) (instead of having the kind of the `d` tag) provides:
* Multiple `k` tags can exist in the same event if the application supports more than one event kind and their handler URLs are the same.
* The same pubkey can have multiple events with different apps that handle the same event kind.
* `bech32` in a URL MUST be replaced by clients with the NIP-19-encoded entity that should be loaded by the application.
@@ -74,14 +74,14 @@ Multiple tags might be registered by the app, following NIP-19 nomenclature as t
A tag without a second value in the array SHOULD be considered a generic handler for any NIP-19 entity that is not handled by a different tag.
## Client tag
When publishing events, clients MAY include a `client` tag in the same format as the recommendation event's `a` tags. This has privacy implications for users, so clients SHOULD allow users to opt-out of using this tag.
# Client tag
When publishing events, clients MAY include a `client` tag. Identifying the client that published the note. This tag is a tuple of `name`, `address` identifying a handler event and, a relay `hint` for finding the handler event. This has privacy implications for users, so clients SHOULD allow users to opt-out of using this tag.
```json
{
"kind": 1,
"tags": [
["client", "31990:app1-pubkey:<d-identifier>", "wss://relay1", "ios"]
["client", "My Client", "31990:app1-pubkey:<d-identifier>", "wss://relay1"]
]
...
}

2
90.md
View File

@@ -34,7 +34,7 @@ There are two actors in the workflow described in this NIP:
* Service providers (npubs who fulfill jobs)
## Job request (`kind:5000-5999`)
A request to process data, published by a customer. This event signals that an customer is interested in receiving the result of some kind of compute.
A request to process data, published by a customer. This event signals that a customer is interested in receiving the result of some kind of compute.
```json
{

4
94.md
View File

@@ -6,7 +6,7 @@ File Metadata
`draft` `optional`
The purpose of this NIP is to allow an organization and classification of shared files. So that relays can filter and organize in any way that is of interest. With that, multiple types of filesharing clients can be created. NIP-94 support is not expected to be implemented by "social" clients that deal with kind:1 notes or by longform clients that deal with kind:30023 articles.
The purpose of this NIP is to allow an organization and classification of shared files. So that relays can filter and organize in any way that is of interest. With that, multiple types of filesharing clients can be created. NIP-94 support is not expected to be implemented by "social" clients that deal with `kind:1` notes or by longform clients that deal with `kind:30023` articles.
## Event format
@@ -14,7 +14,6 @@ This NIP specifies the use of the `1063` event type, having in `content` a descr
* `url` the url to download the file
* `m` a string indicating the data type of the file. The [MIME types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) format must be used, and they should be lowercase.
* `"aes-256-gcm"` (optional) key and nonce for AES-GCM encryption with tagSize always 128bits
* `x` containing the SHA-256 hexencoded string of the file.
* `size` (optional) size of file in bytes
* `dim` (optional) size of file in pixels in the form `<width>x<height>`
@@ -31,7 +30,6 @@ This NIP specifies the use of the `1063` event type, having in `content` a descr
"kind": 1063,
"tags": [
["url",<string with URI of file>],
["aes-256-gcm",<key>, <iv>],
["m", <MIME type>],
["x",<Hash SHA-256>],
["size", <size of file in bytes>],

147
README.md
View File

@@ -22,9 +22,9 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
## List
- [NIP-01: Basic protocol flow description](01.md)
- [NIP-02: Contact List and Petnames](02.md)
- [NIP-02: Follow List](02.md)
- [NIP-03: OpenTimestamps Attestations for Events](03.md)
- [NIP-04: Encrypted Direct Message](04.md)
- [NIP-04: Encrypted Direct Message](04.md) --- **unrecommended**: deprecated in favor of [NIP-44](44.md)
- [NIP-05: Mapping Nostr keys to DNS-based internet identifiers](05.md)
- [NIP-06: Basic key derivation from mnemonic seed phrase](06.md)
- [NIP-07: `window.nostr` capability for web browsers](07.md)
@@ -38,7 +38,6 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
- [NIP-18: Reposts](18.md)
- [NIP-19: bech32-encoded entities](19.md)
- [NIP-21: `nostr:` URI scheme](21.md)
- [NIP-22: Event `created_at` Limits](22.md)
- [NIP-23: Long-form Content](23.md)
- [NIP-24: Extra metadata fields and tags](24.md)
- [NIP-25: Reactions](25.md)
@@ -53,6 +52,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
- [NIP-39: External Identities in Profiles](39.md)
- [NIP-40: Expiration Timestamp](40.md)
- [NIP-42: Authentication of clients to relays](42.md)
- [NIP-44: Versioned Encryption](44.md)
- [NIP-45: Counting results](45.md)
- [NIP-46: Nostr Connect](46.md)
- [NIP-47: Wallet Connect](47.md)
@@ -76,66 +76,82 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
- [NIP-99: Classified Listings](99.md)
## Event Kinds
| kind | description | NIP |
| ------------- | -------------------------- | ----------- |
| `0` | Metadata | [1](01.md) |
| `1` | Short Text Note | [1](01.md) |
| `2` | Recommend Relay | |
| `3` | Contacts | [2](02.md) |
| `4` | Encrypted Direct Messages | [4](04.md) |
| `5` | Event Deletion | [9](09.md) |
| `6` | Repost | [18](18.md) |
| `7` | Reaction | [25](25.md) |
| `8` | Badge Award | [58](58.md) |
| `16` | Generic Repost | [18](18.md) |
| `40` | Channel Creation | [28](28.md) |
| `41` | Channel Metadata | [28](28.md) |
| `42` | Channel Message | [28](28.md) |
| `43` | Channel Hide Message | [28](28.md) |
| `44` | Channel Mute User | [28](28.md) |
| `1063` | File Metadata | [94](94.md) |
| `1311` | Live Chat Message | [53](53.md) |
| `1040` | OpenTimestamps | [03](03.md) |
| `1971` | Problem Tracker | [1971](https://github.com/nostrocket/NIPS/blob/main/Problems.md) |
| `1984` | Reporting | [56](56.md) |
| `1985` | Label | [32](32.md) |
| `4550` | Community Post Approval | [72](72.md) |
| `5000`-`5999` | Job Request | [90](90.md) |
| `6000`-`6999` | Job Result | [90](90.md) |
| `7000` | Job Feedback | [90](90.md) |
| `9041` | Zap Goal | [75](75.md) |
| `9734` | Zap Request | [57](57.md) |
| `9735` | Zap | [57](57.md) |
| `9802` | Highlights | [84](84.md) |
| `10000` | Mute List | [51](51.md) |
| `10001` | Pin List | [51](51.md) |
| `10002` | Relay List Metadata | [65](65.md) |
| `13194` | Wallet Info | [47](47.md) |
| `22242` | Client Authentication | [42](42.md) |
| `23194` | Wallet Request | [47](47.md) |
| `23195` | Wallet Response | [47](47.md) |
| `24133` | Nostr Connect | [46](46.md) |
| `27235` | HTTP Auth | [98](98.md) |
| `30000` | Categorized People List | [51](51.md) |
| `30001` | Categorized Bookmark List | [51](51.md) |
| `30008` | Profile Badges | [58](58.md) |
| `30009` | Badge Definition | [58](58.md) |
| `30017` | Create or update a stall | [15](15.md) |
| `30018` | Create or update a product | [15](15.md) |
| `30023` | Long-form Content | [23](23.md) |
| `30024` | Draft Long-form Content | [23](23.md) |
| `30078` | Application-specific Data | [78](78.md) |
| `30311` | Live Event | [53](53.md) |
| `30315` | User Statuses | [38](38.md) |
| `30402` | Classified Listing | [99](99.md) |
| `30403` | Draft Classified Listing | [99](99.md) |
| `31922` | Date-Based Calendar Event | [52](52.md) |
| `31923` | Time-Based Calendar Event | [52](52.md) |
| `31924` | Calendar | [52](52.md) |
| `31925` | Calendar Event RSVP | [52](52.md) |
| `31989` | Handler recommendation | [89](89.md) |
| `31990` | Handler information | [89](89.md) |
| `34550` | Community Definition | [72](72.md) |
| kind | description | NIP |
| ------------- | -------------------------- | ------------------------ |
| `0` | Metadata | [01](01.md) |
| `1` | Short Text Note | [01](01.md) |
| `2` | Recommend Relay | 01 (deprecated) |
| `3` | Follows | [02](02.md) |
| `4` | Encrypted Direct Messages | [04](04.md) |
| `5` | Event Deletion | [09](09.md) |
| `6` | Repost | [18](18.md) |
| `7` | Reaction | [25](25.md) |
| `8` | Badge Award | [58](58.md) |
| `16` | Generic Repost | [18](18.md) |
| `40` | Channel Creation | [28](28.md) |
| `41` | Channel Metadata | [28](28.md) |
| `42` | Channel Message | [28](28.md) |
| `43` | Channel Hide Message | [28](28.md) |
| `44` | Channel Mute User | [28](28.md) |
| `1040` | OpenTimestamps | [03](03.md) |
| `1063` | File Metadata | [94](94.md) |
| `1311` | Live Chat Message | [53](53.md) |
| `1971` | Problem Tracker | [nostrocket][nostrocket] |
| `1984` | Reporting | [56](56.md) |
| `1985` | Label | [32](32.md) |
| `4550` | Community Post Approval | [72](72.md) |
| `5000`-`5999` | Job Request | [90](90.md) |
| `6000`-`6999` | Job Result | [90](90.md) |
| `7000` | Job Feedback | [90](90.md) |
| `9041` | Zap Goal | [75](75.md) |
| `9734` | Zap Request | [57](57.md) |
| `9735` | Zap | [57](57.md) |
| `9802` | Highlights | [84](84.md) |
| `10000` | Mute list | [51](51.md) |
| `10001` | Pin list | [51](51.md) |
| `10002` | Relay List Metadata | [65](65.md) |
| `10003` | Bookmark list | [51](51.md) |
| `10004` | Communities list | [51](51.md) |
| `10005` | Public chats list | [51](51.md) |
| `10006` | Blocked relays list | [51](51.md) |
| `10007` | Search relays list | [51](51.md) |
| `10015` | Interests list | [51](51.md) |
| `10030` | User emoji list | [51](51.md) |
| `13194` | Wallet Info | [47](47.md) |
| `21000` | Lightning Pub RPC | [Lightning.Pub][lnpub] |
| `22242` | Client Authentication | [42](42.md) |
| `23194` | Wallet Request | [47](47.md) |
| `23195` | Wallet Response | [47](47.md) |
| `24133` | Nostr Connect | [46](46.md) |
| `27235` | HTTP Auth | [98](98.md) |
| `30000` | Follow sets | [51](51.md) |
| `30001` | Generic lists | [51](51.md) |
| `30002` | Relay sets | [51](51.md) |
| `30003` | Bookmark sets | [51](51.md) |
| `30004` | Curation sets | [51](51.md) |
| `30008` | Profile Badges | [58](58.md) |
| `30009` | Badge Definition | [58](58.md) |
| `30015` | Interest sets | [51](51.md) |
| `30017` | Create or update a stall | [15](15.md) |
| `30018` | Create or update a product | [15](15.md) |
| `30023` | Long-form Content | [23](23.md) |
| `30024` | Draft Long-form Content | [23](23.md) |
| `30030` | Emoji sets | [51](51.md) |
| `30078` | Application-specific Data | [78](78.md) |
| `30311` | Live Event | [53](53.md) |
| `30315` | User Statuses | [38](38.md) |
| `30402` | Classified Listing | [99](99.md) |
| `30403` | Draft Classified Listing | [99](99.md) |
| `31922` | Date-Based Calendar Event | [52](52.md) |
| `31923` | Time-Based Calendar Event | [52](52.md) |
| `31924` | Calendar | [52](52.md) |
| `31925` | Calendar Event RSVP | [52](52.md) |
| `31989` | Handler recommendation | [89](89.md) |
| `31990` | Handler information | [89](89.md) |
| `34550` | Community Definition | [72](72.md) |
[nostrocket]: https://github.com/nostrocket/NIPS/blob/main/Problems.md
[lnpub]: https://github.com/shocknet/Lightning.Pub/blob/master/proto/autogenerated/client.md
## Message types
@@ -157,6 +173,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `EVENT` | used to send events requested to clients | [01](01.md) |
| `NOTICE` | used to send human-readable messages to clients | [01](01.md) |
| `OK` | used to notify clients if an EVENT was successful | [01](01.md) |
| `CLOSED` | used to notify clients that a REQ was ended and why | [01](01.md) |
| `AUTH` | used to send authentication challenges | [42](42.md) |
| `COUNT` | used to send requested event counts to clients | [45](45.md) |
@@ -170,7 +187,6 @@ Please update these lists when proposing NIPs introducing new event kinds.
| `p` | pubkey (hex) | relay URL, petname | [01](01.md), [02](02.md) |
| `a` | coordinates to an event | relay URL | [01](01.md) |
| `d` | identifier | -- | [01](01.md) |
| `alt` | summary | -- | [31](31.md) |
| `g` | geohash | -- | [52](52.md) |
| `i` | identity | proof | [39](39.md) |
| `k` | kind number (string) | -- | [18](18.md), [25](25.md), [72](72.md) |
@@ -180,13 +196,16 @@ Please update these lists when proposing NIPs introducing new event kinds.
| `r` | a reference (URL, etc) | petname | |
| `r` | relay url | marker | [65](65.md) |
| `t` | hashtag | -- | |
| `alt` | summary | -- | [31](31.md) |
| `amount` | millisatoshis, stringified | -- | [57](57.md) |
| `bolt11` | `bolt11` invoice | -- | [57](57.md) |
| `challenge` | challenge string | -- | [42](42.md) |
| `client` | name, address | relay URL | [89](89.md) |
| `content-warning` | reason | -- | [36](36.md) |
| `delegation` | pubkey, conditions, delegation token | -- | [26](26.md) |
| `description` | invoice/badge description | -- | [57](57.md), [58](58.md) |
| `emoji` | shortcode, image URL | -- | [30](30.md) |
| `encrypted` | -- | -- | [90](90.md) |
| `expiration` | unix timestamp (string) | -- | [40](40.md) |
| `goal` | event id (hex) | relay URL | [75](75.md) |
| `image` | image URL | dimensions in pixels | [23](23.md), [58](58.md) |