From df236f4ab90096b161fbc0d0bf81265209c373c5 Mon Sep 17 00:00:00 2001 From: toadlyBroodle Date: Sat, 29 Apr 2023 13:25:49 +0900 Subject: [PATCH] merge recent changes --- 01.md | 222 +++++++++++------------ 04.md | 106 +++++------ 05.md | 196 ++++++++++----------- 08.md | 42 ++--- 09.md | 96 +++++----- 11.md | 512 +++++++++++++++++++++++++++--------------------------- 13.md | 246 +++++++++++++------------- 15.md | 428 ++++++++++++++++++++++----------------------- 19.md | 140 +++++++-------- 21.md | 40 ++--- 22.md | 90 +++++----- 25.md | 98 +++++------ 26.md | 214 +++++++++++------------ 27.md | 108 ++++++------ 40.md | 118 ++++++------- 45.md | 78 ++++----- 51.md | 224 ++++++++++++------------ 78.md | 42 ++--- 94.md | 102 +++++------ README.md | 366 +++++++++++++++++++------------------- 20 files changed, 1734 insertions(+), 1734 deletions(-) diff --git a/01.md b/01.md index a07a0dff..f3d2ff1d 100644 --- a/01.md +++ b/01.md @@ -1,111 +1,111 @@ -NIP-01 -====== - -Basic protocol flow description -------------------------------- - -`draft` `mandatory` `author:fiatjaf` `author:distbit` `author:scsibug` `author:kukks` `author:jb55` `author:semisol` - -This NIP defines the basic protocol that should be implemented by everybody. New NIPs may add new optional (or mandatory) fields and messages and features to the structures and flows described here. - -## Events and signatures - -Each user has a keypair. Signatures, public key, and encodings are done according to the [Schnorr signatures standard for the curve `secp256k1`](https://bips.xyz/340). - -The only object type that exists is the `event`, which has the following format on the wire: - -```json -{ - "id": <32-bytes lowercase hex-encoded sha256 of the serialized event data> - "pubkey": <32-bytes lowercase hex-encoded public key of the event creator>, - "created_at": , - "kind": , - "tags": [ - ["e", <32-bytes hex of the id of another event>, ], - ["p", <32-bytes hex of a pubkey>, ], - ... // other kinds of tags may be included later - ], - "content": , - "sig": <64-bytes hex of the signature of the sha256 hash of the serialized event data, which is the same as the "id" field> -} -``` - -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) of the following structure: - -```json -[ - 0, - , - , - , - , - -] -``` - -## Communication between clients and relays - -Relays expose a websocket endpoint to which clients can connect. - -### 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", ]`, used to publish events. - * `["REQ", , ...]`, used to request events and subscribe to new updates. - * `["CLOSE", ]`, used to stop previous subscriptions. - -`` is an arbitrary, non-empty string of max length 64 chars, that should be used to represent a subscription. - -`` is a JSON object that determines what events will be sent in that subscription, it can have the following attributes: - -```json -{ - "ids": , - "authors": , - "kinds": , - "#e": , - "#p": , - "since": , - "until": , - "limit": -} -``` - -Upon receiving a `REQ` message, the relay SHOULD query its internal database and return events that match the filter, then store that filter and send again all future events it receives to that same websocket until the websocket is closed. The `CLOSE` event is received with the same `` or a new `REQ` is sent using the same ``, in which case it should overwrite the previous subscription. - -Filter attributes containing lists (such as `ids`, `kinds`, or `#e`) are JSON arrays with one or more values. At least one of the array's values must match the relevant field in an event for the condition itself to be considered a match. For scalar event attributes such as `kind`, the attribute from the event must be contained in the filter list. For tag attributes such as `#e`, where an event may have multiple values, the event and filter condition values must have at least one item in common. - -The `ids` and `authors` lists contain lowercase hexadecimal strings, which may either be an exact 64-character match, or a prefix of the event value. A prefix match is when the filter string is an exact string prefix of the event value. The use of prefixes allows for more compact filters where a large number of values are queried, and can provide some privacy for clients that may not want to disclose the exact authors or events they are searching for. - -All conditions of a filter that are specified must match for an event for it to pass the filter, i.e., multiple conditions are interpreted as `&&` conditions. - -A `REQ` message may contain multiple filters. In this case, events that match any of the filters are to be returned, i.e., multiple filters are to be interpreted as `||` conditions. - -The `limit` property of a filter is only valid for the initial query and can be ignored afterward. When `limit: n` is present it is assumed that the events returned in the initial query will be the latest `n` events. It is safe to return less events than `limit` specifies, but it is expected that relays do not return (much) more events than requested so clients don't get unnecessarily overwhelmed by data. - -### From relay to client: sending events and notices - -Relays can send 3 types of messages, which must also be JSON arrays, according to the following patterns: - - * `["EVENT", , ]`, used to send events requested by clients. - * `["EOSE", ]`, used to indicate the _end of stored events_ and the beginning of events newly received in real-time. - * `["NOTICE", ]`, 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). - -## Basic Event Kinds - - - `0`: `set_metadata`: the `content` is set to a stringified JSON object `{name: , about: , picture: }` describing the user who created the event. A relay may delete past `set_metadata` events once it gets a new one for the same pubkey. - - `1`: `text_note`: the `content` is set to the plaintext content of a note (anything the user wants to say). Markdown links (`[]()` stuff) are not plaintext. - - `2`: `recommend_server`: the `content` is set to the URL (e.g., `wss://somerelay.com`) of a relay the event creator wants to recommend to its followers. - -A relay may choose to treat different message kinds differently, and it may or may not choose to have a default way to handle kinds it doesn't know about. - -## Other Notes: - -- Clients should not open more than one websocket to each relay. One channel can support an unlimited number of subscriptions, so clients should do that. -- The `tags` array can store a tag identifier as the first element of each subarray, plus arbitrary information afterward (always as strings). This NIP defines `"p"` — meaning "pubkey", which points to a pubkey of someone that is referred to in the event —, and `"e"` — meaning "event", which points to the id of an event this event is quoting, replying to or referring to somehow. See [NIP-10](https://github.com/nostr-protocol/nips/blob/127d5518bfa9a4e4e7510490c0b8d95e342dfa4b/10.md) for a detailed description of "e" and "p" tags. -- The `` item present on the `"e"` and `"p"` tags is an optional (could be set to `""`) URL of a relay the client could attempt to connect to fetch the tagged event or other events from a tagged profile. It MAY be ignored, but it exists to increase censorship resistance and make the spread of relay addresses more seamless across clients. +NIP-01 +====== + +Basic protocol flow description +------------------------------- + +`draft` `mandatory` `author:fiatjaf` `author:distbit` `author:scsibug` `author:kukks` `author:jb55` `author:semisol` + +This NIP defines the basic protocol that should be implemented by everybody. New NIPs may add new optional (or mandatory) fields and messages and features to the structures and flows described here. + +## Events and signatures + +Each user has a keypair. Signatures, public key, and encodings are done according to the [Schnorr signatures standard for the curve `secp256k1`](https://bips.xyz/340). + +The only object type that exists is the `event`, which has the following format on the wire: + +```json +{ + "id": <32-bytes lowercase hex-encoded sha256 of the serialized event data> + "pubkey": <32-bytes lowercase hex-encoded public key of the event creator>, + "created_at": , + "kind": , + "tags": [ + ["e", <32-bytes hex of the id of another event>, ], + ["p", <32-bytes hex of a pubkey>, ], + ... // other kinds of tags may be included later + ], + "content": , + "sig": <64-bytes hex of the signature of the sha256 hash of the serialized event data, which is the same as the "id" field> +} +``` + +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) of the following structure: + +```json +[ + 0, + , + , + , + , + +] +``` + +## Communication between clients and relays + +Relays expose a websocket endpoint to which clients can connect. + +### 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", ]`, used to publish events. + * `["REQ", , ...]`, used to request events and subscribe to new updates. + * `["CLOSE", ]`, used to stop previous subscriptions. + +`` is an arbitrary, non-empty string of max length 64 chars, that should be used to represent a subscription. + +`` is a JSON object that determines what events will be sent in that subscription, it can have the following attributes: + +```json +{ + "ids": , + "authors": , + "kinds": , + "#e": , + "#p": , + "since": , + "until": , + "limit": +} +``` + +Upon receiving a `REQ` message, the relay SHOULD query its internal database and return events that match the filter, then store that filter and send again all future events it receives to that same websocket until the websocket is closed. The `CLOSE` event is received with the same `` or a new `REQ` is sent using the same ``, in which case it should overwrite the previous subscription. + +Filter attributes containing lists (such as `ids`, `kinds`, or `#e`) are JSON arrays with one or more values. At least one of the array's values must match the relevant field in an event for the condition itself to be considered a match. For scalar event attributes such as `kind`, the attribute from the event must be contained in the filter list. For tag attributes such as `#e`, where an event may have multiple values, the event and filter condition values must have at least one item in common. + +The `ids` and `authors` lists contain lowercase hexadecimal strings, which may either be an exact 64-character match, or a prefix of the event value. A prefix match is when the filter string is an exact string prefix of the event value. The use of prefixes allows for more compact filters where a large number of values are queried, and can provide some privacy for clients that may not want to disclose the exact authors or events they are searching for. + +All conditions of a filter that are specified must match for an event for it to pass the filter, i.e., multiple conditions are interpreted as `&&` conditions. + +A `REQ` message may contain multiple filters. In this case, events that match any of the filters are to be returned, i.e., multiple filters are to be interpreted as `||` conditions. + +The `limit` property of a filter is only valid for the initial query and can be ignored afterward. When `limit: n` is present it is assumed that the events returned in the initial query will be the latest `n` events. It is safe to return less events than `limit` specifies, but it is expected that relays do not return (much) more events than requested so clients don't get unnecessarily overwhelmed by data. + +### From relay to client: sending events and notices + +Relays can send 3 types of messages, which must also be JSON arrays, according to the following patterns: + + * `["EVENT", , ]`, used to send events requested by clients. + * `["EOSE", ]`, used to indicate the _end of stored events_ and the beginning of events newly received in real-time. + * `["NOTICE", ]`, 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). + +## Basic Event Kinds + + - `0`: `set_metadata`: the `content` is set to a stringified JSON object `{name: , about: , picture: }` describing the user who created the event. A relay may delete past `set_metadata` events once it gets a new one for the same pubkey. + - `1`: `text_note`: the `content` is set to the plaintext content of a note (anything the user wants to say). Markdown links (`[]()` stuff) are not plaintext. + - `2`: `recommend_server`: the `content` is set to the URL (e.g., `wss://somerelay.com`) of a relay the event creator wants to recommend to its followers. + +A relay may choose to treat different message kinds differently, and it may or may not choose to have a default way to handle kinds it doesn't know about. + +## Other Notes: + +- Clients should not open more than one websocket to each relay. One channel can support an unlimited number of subscriptions, so clients should do that. +- The `tags` array can store a tag identifier as the first element of each subarray, plus arbitrary information afterward (always as strings). This NIP defines `"p"` — meaning "pubkey", which points to a pubkey of someone that is referred to in the event —, and `"e"` — meaning "event", which points to the id of an event this event is quoting, replying to or referring to somehow. See [NIP-10](https://github.com/nostr-protocol/nips/blob/127d5518bfa9a4e4e7510490c0b8d95e342dfa4b/10.md) for a detailed description of "e" and "p" tags. +- The `` item present on the `"e"` and `"p"` tags is an optional (could be set to `""`) URL of a relay the client could attempt to connect to fetch the tagged event or other events from a tagged profile. It MAY be ignored, but it exists to increase censorship resistance and make the spread of relay addresses more seamless across clients. diff --git a/04.md b/04.md index 6e45b74b..c325eb1f 100644 --- a/04.md +++ b/04.md @@ -1,53 +1,53 @@ -NIP-04 -====== - -Encrypted Direct Message ------------------------- - -`final` `optional` `author:arcbtc` - -A special event with kind `4`, meaning "encrypted direct message". It is supposed to have the following attributes: - -**`content`** MUST be equal to the base64-encoded, aes-256-cbc encrypted string of anything a user wants to write, encrypted using a shared cipher generated by combining the recipient's public-key with the sender's private-key; this appended by the base64-encoded initialization vector as if it was a querystring parameter named "iv". The format is the following: `"content": "?iv="`. - -**`tags`** MUST contain an entry identifying the receiver of the message (such that relays may naturally forward this event to them), in the form `["p", ""]`. - -**`tags`** MAY contain an entry identifying the previous message in a conversation or a message we are explicitly replying to (such that contextual, more organized conversations may happen), in the form `["e", ""]`. - -**Note**: By default in the [libsecp256k1](https://github.com/bitcoin-core/secp256k1) ECDH implementation, the secret is the SHA256 hash of the shared point (both X and Y coordinates). In Nostr, only the X coordinate of the shared point is used as the secret and it is NOT hashed. If using libsecp256k1, a custom function that copies the X coordinate must be passed as the `hashfp` argument in `secp256k1_ecdh`. See [here](https://github.com/bitcoin-core/secp256k1/blob/master/src/modules/ecdh/main_impl.h#L29). - -Code sample for generating such an event in JavaScript: - -```js -import crypto from 'crypto' -import * as secp from '@noble/secp256k1' - -let sharedPoint = secp.getSharedSecret(ourPrivateKey, '02' + theirPublicKey) -let sharedX = sharedPoint.slice(1, 33) - -let iv = crypto.randomFillSync(new Uint8Array(16)) -var cipher = crypto.createCipheriv( - 'aes-256-cbc', - Buffer.from(sharedX), - iv -) -let encryptedMessage = cipher.update(text, 'utf8', 'base64') -encryptedMessage += cipher.final('base64') -let ivBase64 = Buffer.from(iv.buffer).toString('base64') - -let event = { - pubkey: ourPubKey, - created_at: Math.floor(Date.now() / 1000), - kind: 4, - tags: [['p', theirPublicKey]], - content: encryptedMessage + '?iv=' + ivBase64 -} -``` - -## Security Warning - -This standard does not go anywhere near what is considered the state-of-the-art in encrypted communication between peers, and it leaks metadata in the events, therefore it must not be used for anything you really need to keep secret, and only with relays that use `AUTH` to restrict who can fetch your `kind:4` events. - -## Client Implementation Warning - -Clients *should not* search and replace public key or note references from the `.content`. If processed like a regular text note (where `@npub...` is replaced with `#[0]` with a `["p", "..."]` tag) the tags are leaked and the mentioned user will receive the message in their inbox. +NIP-04 +====== + +Encrypted Direct Message +------------------------ + +`final` `optional` `author:arcbtc` + +A special event with kind `4`, meaning "encrypted direct message". It is supposed to have the following attributes: + +**`content`** MUST be equal to the base64-encoded, aes-256-cbc encrypted string of anything a user wants to write, encrypted using a shared cipher generated by combining the recipient's public-key with the sender's private-key; this appended by the base64-encoded initialization vector as if it was a querystring parameter named "iv". The format is the following: `"content": "?iv="`. + +**`tags`** MUST contain an entry identifying the receiver of the message (such that relays may naturally forward this event to them), in the form `["p", ""]`. + +**`tags`** MAY contain an entry identifying the previous message in a conversation or a message we are explicitly replying to (such that contextual, more organized conversations may happen), in the form `["e", ""]`. + +**Note**: By default in the [libsecp256k1](https://github.com/bitcoin-core/secp256k1) ECDH implementation, the secret is the SHA256 hash of the shared point (both X and Y coordinates). In Nostr, only the X coordinate of the shared point is used as the secret and it is NOT hashed. If using libsecp256k1, a custom function that copies the X coordinate must be passed as the `hashfp` argument in `secp256k1_ecdh`. See [here](https://github.com/bitcoin-core/secp256k1/blob/master/src/modules/ecdh/main_impl.h#L29). + +Code sample for generating such an event in JavaScript: + +```js +import crypto from 'crypto' +import * as secp from '@noble/secp256k1' + +let sharedPoint = secp.getSharedSecret(ourPrivateKey, '02' + theirPublicKey) +let sharedX = sharedPoint.slice(1, 33) + +let iv = crypto.randomFillSync(new Uint8Array(16)) +var cipher = crypto.createCipheriv( + 'aes-256-cbc', + Buffer.from(sharedX), + iv +) +let encryptedMessage = cipher.update(text, 'utf8', 'base64') +encryptedMessage += cipher.final('base64') +let ivBase64 = Buffer.from(iv.buffer).toString('base64') + +let event = { + pubkey: ourPubKey, + created_at: Math.floor(Date.now() / 1000), + kind: 4, + tags: [['p', theirPublicKey]], + content: encryptedMessage + '?iv=' + ivBase64 +} +``` + +## Security Warning + +This standard does not go anywhere near what is considered the state-of-the-art in encrypted communication between peers, and it leaks metadata in the events, therefore it must not be used for anything you really need to keep secret, and only with relays that use `AUTH` to restrict who can fetch your `kind:4` events. + +## Client Implementation Warning + +Clients *should not* search and replace public key or note references from the `.content`. If processed like a regular text note (where `@npub...` is replaced with `#[0]` with a `["p", "..."]` tag) the tags are leaked and the mentioned user will receive the message in their inbox. diff --git a/05.md b/05.md index a7b42b06..949da9da 100644 --- a/05.md +++ b/05.md @@ -1,98 +1,98 @@ -NIP-05 -====== - -Mapping Nostr keys to DNS-based internet identifiers ----------------------------------------------------- - -`final` `optional` `author:fiatjaf` `author:mikedilger` - -On events of kind `0` (`set_metadata`) one can specify the key `"nip05"` with an [internet identifier](https://datatracker.ietf.org/doc/html/rfc5322#section-3.4.1) (an email-like address) as the value. Although there is a link to a very liberal "internet identifier" specification above, NIP-05 assumes the `` part will be restricted to the characters `a-z0-9-_.`, case insensitive. - -Upon seeing that, the client splits the identifier into `` and `` and use these values to make a GET request to `https:///.well-known/nostr.json?name=`. - -The result should be a JSON document object with a key `"names"` that should then be a mapping of names to hex formatted public keys. If the public key for the given `` matches the `pubkey` from the `set_metadata` event, the client then concludes that the given pubkey can indeed be referenced by its identifier. - -### Example - -If a client sees an event like this: - -```json -{ - "pubkey": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9", - "kind": 0, - "content": "{\"name\": \"bob\", \"nip05\": \"bob@example.com\"}" - ... -} -``` - -It will make a GET request to `https://example.com/.well-known/nostr.json?name=bob` and get back a response that will look like - -```json -{ - "names": { - "bob": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9" - } -} -```` - -or with the **optional** `"relays"` attribute: - -```json -{ - "names": { - "bob": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9" - }, - "relays": { - "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9": [ "wss://relay.example.com", "wss://relay2.example.com" ] - } -} -```` - -If the pubkey matches the one given in `"names"` (as in the example above) that means the association is right and the `"nip05"` identifier is valid and can be displayed. - -The optional `"relays"` attribute may contain an object with public keys as properties and arrays of relay URLs as values. When present, that can be used to help clients learn in which relays that user may be found. Web servers which serve `/.well-known/nostr.json` files dynamically based on the query string SHOULD also serve the relays data for any name they serve in the same reply when that is available. - -## Finding users from their NIP-05 identifier - -A client may implement support for finding users' public keys from _internet identifiers_, the flow is the same as above, but reversed: first the client fetches the _well-known_ URL and from there it gets the public key of the user, then it tries to fetch the kind `0` event for that user and check if it has a matching `"nip05"`. - -## Notes - -### Clients must always follow public keys, not NIP-05 addresses - -For example, if after finding that `bob@bob.com` has the public key `abc...def`, the user clicks a button to follow that profile, the client must keep a primary reference to `abc...def`, not `bob@bob.com`. If, for any reason, the address `https://bob.com/.well-known/nostr.json?name=bob` starts returning the public key `1d2...e3f` at any time in the future, the client must not replace `abc...def` in his list of followed profiles for the user (but it should stop displaying "bob@bob.com" for that user, as that will have become an invalid `"nip05"` property). - -### Public keys must be in hex format - -Keys must be returned in hex format. Keys in NIP-19 `npub` format are only meant to be used for display in client UIs, not in this NIP. - -### User Discovery implementation suggestion - -A client can also use this to allow users to search other profiles. If a client has a search box or something like that, a user may be able to type "bob@example.com" there and the client would recognize that and do the proper queries to obtain a pubkey and suggest that to the user. - -### Showing just the domain as an identifier - -Clients may treat the identifier `_@domain` as the "root" identifier, and choose to display it as just the ``. For example, if Bob owns `bob.com`, he may not want an identifier like `bob@bob.com` as that is redundant. Instead, Bob can use the identifier `_@bob.com` and expect Nostr clients to show and treat that as just `bob.com` for all purposes. - -### Reasoning for the `/.well-known/nostr.json?name=` format - -By adding the `` as a query string instead of as part of the path the protocol can support both dynamic servers that can generate JSON on-demand and static servers with a JSON file in it that may contain multiple names. - -### Allowing access from JavaScript apps - -JavaScript Nostr apps may be restricted by browser [CORS][] policies that prevent them from accessing `/.well-known/nostr.json` on the user's domain. When CORS prevents JS from loading a resource, the JS program sees it as a network failure identical to the resource not existing, so it is not possible for a pure-JS app to tell the user for certain that the failure was caused by a CORS issue. JS Nostr apps that see network failures requesting `/.well-known/nostr.json` files may want to recommend to users that they check the CORS policy of their servers, e.g.: - -```bash -$ curl -sI https://example.com/.well-known/nostr.json?name=bob | grep -i ^Access-Control -Access-Control-Allow-Origin: * -``` - -Users should ensure that their `/.well-known/nostr.json` is served with the HTTP header `Access-Control-Allow-Origin: *` to ensure it can be validated by pure JS apps running in modern browsers. - -[CORS]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS - -### Security Constraints - -The `/.well-known/nostr.json` endpoint MUST NOT return any HTTP redirects. - -Fetchers MUST ignore any HTTP redirects given by the `/.well-known/nostr.json` endpoint. +NIP-05 +====== + +Mapping Nostr keys to DNS-based internet identifiers +---------------------------------------------------- + +`final` `optional` `author:fiatjaf` `author:mikedilger` + +On events of kind `0` (`set_metadata`) one can specify the key `"nip05"` with an [internet identifier](https://datatracker.ietf.org/doc/html/rfc5322#section-3.4.1) (an email-like address) as the value. Although there is a link to a very liberal "internet identifier" specification above, NIP-05 assumes the `` part will be restricted to the characters `a-z0-9-_.`, case insensitive. + +Upon seeing that, the client splits the identifier into `` and `` and use these values to make a GET request to `https:///.well-known/nostr.json?name=`. + +The result should be a JSON document object with a key `"names"` that should then be a mapping of names to hex formatted public keys. If the public key for the given `` matches the `pubkey` from the `set_metadata` event, the client then concludes that the given pubkey can indeed be referenced by its identifier. + +### Example + +If a client sees an event like this: + +```json +{ + "pubkey": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9", + "kind": 0, + "content": "{\"name\": \"bob\", \"nip05\": \"bob@example.com\"}" + ... +} +``` + +It will make a GET request to `https://example.com/.well-known/nostr.json?name=bob` and get back a response that will look like + +```json +{ + "names": { + "bob": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9" + } +} +```` + +or with the **optional** `"relays"` attribute: + +```json +{ + "names": { + "bob": "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9" + }, + "relays": { + "b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9": [ "wss://relay.example.com", "wss://relay2.example.com" ] + } +} +```` + +If the pubkey matches the one given in `"names"` (as in the example above) that means the association is right and the `"nip05"` identifier is valid and can be displayed. + +The optional `"relays"` attribute may contain an object with public keys as properties and arrays of relay URLs as values. When present, that can be used to help clients learn in which relays that user may be found. Web servers which serve `/.well-known/nostr.json` files dynamically based on the query string SHOULD also serve the relays data for any name they serve in the same reply when that is available. + +## Finding users from their NIP-05 identifier + +A client may implement support for finding users' public keys from _internet identifiers_, the flow is the same as above, but reversed: first the client fetches the _well-known_ URL and from there it gets the public key of the user, then it tries to fetch the kind `0` event for that user and check if it has a matching `"nip05"`. + +## Notes + +### Clients must always follow public keys, not NIP-05 addresses + +For example, if after finding that `bob@bob.com` has the public key `abc...def`, the user clicks a button to follow that profile, the client must keep a primary reference to `abc...def`, not `bob@bob.com`. If, for any reason, the address `https://bob.com/.well-known/nostr.json?name=bob` starts returning the public key `1d2...e3f` at any time in the future, the client must not replace `abc...def` in his list of followed profiles for the user (but it should stop displaying "bob@bob.com" for that user, as that will have become an invalid `"nip05"` property). + +### Public keys must be in hex format + +Keys must be returned in hex format. Keys in NIP-19 `npub` format are only meant to be used for display in client UIs, not in this NIP. + +### User Discovery implementation suggestion + +A client can also use this to allow users to search other profiles. If a client has a search box or something like that, a user may be able to type "bob@example.com" there and the client would recognize that and do the proper queries to obtain a pubkey and suggest that to the user. + +### Showing just the domain as an identifier + +Clients may treat the identifier `_@domain` as the "root" identifier, and choose to display it as just the ``. For example, if Bob owns `bob.com`, he may not want an identifier like `bob@bob.com` as that is redundant. Instead, Bob can use the identifier `_@bob.com` and expect Nostr clients to show and treat that as just `bob.com` for all purposes. + +### Reasoning for the `/.well-known/nostr.json?name=` format + +By adding the `` as a query string instead of as part of the path the protocol can support both dynamic servers that can generate JSON on-demand and static servers with a JSON file in it that may contain multiple names. + +### Allowing access from JavaScript apps + +JavaScript Nostr apps may be restricted by browser [CORS][] policies that prevent them from accessing `/.well-known/nostr.json` on the user's domain. When CORS prevents JS from loading a resource, the JS program sees it as a network failure identical to the resource not existing, so it is not possible for a pure-JS app to tell the user for certain that the failure was caused by a CORS issue. JS Nostr apps that see network failures requesting `/.well-known/nostr.json` files may want to recommend to users that they check the CORS policy of their servers, e.g.: + +```bash +$ curl -sI https://example.com/.well-known/nostr.json?name=bob | grep -i ^Access-Control +Access-Control-Allow-Origin: * +``` + +Users should ensure that their `/.well-known/nostr.json` is served with the HTTP header `Access-Control-Allow-Origin: *` to ensure it can be validated by pure JS apps running in modern browsers. + +[CORS]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS + +### Security Constraints + +The `/.well-known/nostr.json` endpoint MUST NOT return any HTTP redirects. + +Fetchers MUST ignore any HTTP redirects given by the `/.well-known/nostr.json` endpoint. diff --git a/08.md b/08.md index 6793e0bb..4b14ec3f 100644 --- a/08.md +++ b/08.md @@ -1,21 +1,21 @@ -> __Warning__ `unrecommended`: deprecated in favor of [NIP-27](27.md) - -NIP-08 -====== - -Handling Mentions ------------------ - -`final` `unrecommended` `optional` `author:fiatjaf` `author:scsibug` - -This document standardizes the treatment given by clients of inline mentions of other events and pubkeys inside the content of `text_note`s. - -Clients that want to allow tagged mentions they MUST show an autocomplete component or something analogous to that whenever the user starts typing a special key (for example, "@") or presses some button to include a mention etc -- or these clients can come up with other ways to unambiguously differentiate between mentions and normal text. - -Once a mention is identified, for example, the pubkey `27866e9d854c78ae625b867eefdfa9580434bc3e675be08d2acb526610d96fbe`, the client MUST add that pubkey to the `.tags` with the tag `p`, then replace its textual reference (inside `.content`) with the notation `#[index]` in which "index" is equal to the 0-based index of the related tag in the tags array. - -The same process applies for mentioning event IDs. - -A client that receives a `text_note` event with such `#[index]` mentions in its `.content` CAN do a search-and-replace using the actual contents from the `.tags` array with the actual pubkey or event ID that is mentioned, doing any desired context augmentation (for example, linking to the pubkey or showing a preview of the mentioned event contents) it wants in the process. - -Where `#[index]` has an `index` that is outside the range of the tags array or points to a tag that is not an `e` or `p` tag or a tag otherwise declared to support this notation, the client MUST NOT perform such replacement or augmentation, but instead display it as normal text. +> __Warning__ `unrecommended`: deprecated in favor of [NIP-27](27.md) + +NIP-08 +====== + +Handling Mentions +----------------- + +`final` `unrecommended` `optional` `author:fiatjaf` `author:scsibug` + +This document standardizes the treatment given by clients of inline mentions of other events and pubkeys inside the content of `text_note`s. + +Clients that want to allow tagged mentions they MUST show an autocomplete component or something analogous to that whenever the user starts typing a special key (for example, "@") or presses some button to include a mention etc -- or these clients can come up with other ways to unambiguously differentiate between mentions and normal text. + +Once a mention is identified, for example, the pubkey `27866e9d854c78ae625b867eefdfa9580434bc3e675be08d2acb526610d96fbe`, the client MUST add that pubkey to the `.tags` with the tag `p`, then replace its textual reference (inside `.content`) with the notation `#[index]` in which "index" is equal to the 0-based index of the related tag in the tags array. + +The same process applies for mentioning event IDs. + +A client that receives a `text_note` event with such `#[index]` mentions in its `.content` CAN do a search-and-replace using the actual contents from the `.tags` array with the actual pubkey or event ID that is mentioned, doing any desired context augmentation (for example, linking to the pubkey or showing a preview of the mentioned event contents) it wants in the process. + +Where `#[index]` has an `index` that is outside the range of the tags array or points to a tag that is not an `e` or `p` tag or a tag otherwise declared to support this notation, the client MUST NOT perform such replacement or augmentation, but instead display it as normal text. diff --git a/09.md b/09.md index 89781fbf..667c8bcd 100644 --- a/09.md +++ b/09.md @@ -1,48 +1,48 @@ -NIP-09 -====== - -Event Deletion --------------- - -`draft` `optional` `author:scsibug` - -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 intended for deletion. - -The event's `content` field MAY contain a text note describing the reason for the deletion. - -For example: - -``` -{ - "kind": 5, - "pubkey": <32-bytes hex-encoded public key of the event creator>, - "tags": [ - ["e", "dcd59..464a2"], - ["e", "968c5..ad7a4"], - ], - "content": "these posts were published by accident", - ...other fields -} -``` - -Relays SHOULD delete or stop publishing any referenced events that have an identical `pubkey` as the deletion request. Clients SHOULD hide or otherwise indicate a deletion status for referenced events. - -Relays SHOULD continue to publish/share the deletion events indefinitely, as clients may already have the event that's intended to be deleted. Additionally, clients SHOULD broadcast deletion events to other relays which don't have it. - -## Client Usage - -Clients MAY choose to fully hide any events that are referenced by valid deletion events. This includes text notes, direct messages, or other yet-to-be defined event kinds. Alternatively, they MAY show the event along with an icon or other indication that the author has "disowned" the event. The `content` field MAY also be used to replace the deleted events' own content, although a user interface should clearly indicate that this is a deletion reason, not the original content. - -A client MUST validate that each event `pubkey` referenced in the `e` tag of the deletion request is identical to the deletion request `pubkey`, before hiding or deleting any event. Relays can not, in general, perform this validation and should not be treated as authoritative. - -Clients display the deletion event itself in any way they choose, e.g., not at all, or with a prominent notice. - -## Relay Usage - -Relays MAY validate that a deletion event only references events that have the same `pubkey` as the deletion itself, however this is not required since relays may not have knowledge of all referenced events. - -## Deleting a Deletion - -Publishing a deletion event against a deletion has no effect. Clients and relays are not obliged to support "undelete" functionality. +NIP-09 +====== + +Event Deletion +-------------- + +`draft` `optional` `author:scsibug` + +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 intended for deletion. + +The event's `content` field MAY contain a text note describing the reason for the deletion. + +For example: + +``` +{ + "kind": 5, + "pubkey": <32-bytes hex-encoded public key of the event creator>, + "tags": [ + ["e", "dcd59..464a2"], + ["e", "968c5..ad7a4"], + ], + "content": "these posts were published by accident", + ...other fields +} +``` + +Relays SHOULD delete or stop publishing any referenced events that have an identical `pubkey` as the deletion request. Clients SHOULD hide or otherwise indicate a deletion status for referenced events. + +Relays SHOULD continue to publish/share the deletion events indefinitely, as clients may already have the event that's intended to be deleted. Additionally, clients SHOULD broadcast deletion events to other relays which don't have it. + +## Client Usage + +Clients MAY choose to fully hide any events that are referenced by valid deletion events. This includes text notes, direct messages, or other yet-to-be defined event kinds. Alternatively, they MAY show the event along with an icon or other indication that the author has "disowned" the event. The `content` field MAY also be used to replace the deleted events' own content, although a user interface should clearly indicate that this is a deletion reason, not the original content. + +A client MUST validate that each event `pubkey` referenced in the `e` tag of the deletion request is identical to the deletion request `pubkey`, before hiding or deleting any event. Relays can not, in general, perform this validation and should not be treated as authoritative. + +Clients display the deletion event itself in any way they choose, e.g., not at all, or with a prominent notice. + +## Relay Usage + +Relays MAY validate that a deletion event only references events that have the same `pubkey` as the deletion itself, however this is not required since relays may not have knowledge of all referenced events. + +## Deleting a Deletion + +Publishing a deletion event against a deletion has no effect. Clients and relays are not obliged to support "undelete" functionality. diff --git a/11.md b/11.md index a3773642..51d690ca 100644 --- a/11.md +++ b/11.md @@ -1,256 +1,256 @@ -NIP-11 -====== - -Relay Information Document ---------------------------- - -`draft` `optional` `author:scsibug` `author:doc-hex` `author:cameri` - -Relays may provide server metadata to clients to inform them of capabilities, administrative contacts, and various server attributes. This is made available as a JSON document over HTTP, on the same URI as the relay's websocket. - -When a relay receives an HTTP(s) request with an `Accept` header of `application/nostr+json` to a URI supporting WebSocket upgrades, they SHOULD return a document with the following structure. - -```json -{ - "name": , - "description": , - "pubkey": , - "contact": , - "supported_nips": , - "software": , - "version": -} -``` - -Any field may be omitted, and clients MUST ignore any additional fields they do not understand. Relays MUST accept CORS requests by sending `Access-Control-Allow-Origin`, `Access-Control-Allow-Headers`, and `Access-Control-Allow-Methods` headers. - -Field Descriptions ------------------ - -### Name ### - -A relay may select a `name` for use in client software. This is a string, and SHOULD be less than 30 characters to avoid client truncation. - -### Description ### - -Detailed plain-text information about the relay may be contained in the `description` string. It is recommended that this contain no markup, formatting or line breaks for word wrapping, and simply use double newline characters to separate paragraphs. There are no limitations on length. - -### Pubkey ### - -An administrative contact may be listed with a `pubkey`, in the same format as Nostr events (32-byte hex for a `secp256k1` public key). If a contact is listed, this provides clients with a recommended address to send encrypted direct messages (See `NIP-04`) to a system administrator. Expected uses of this address are to report abuse or illegal content, file bug reports, or request other technical assistance. - -Relay operators have no obligation to respond to direct messages. - -### Contact ### - -An alternative contact may be listed under the `contact` field as well, with the same purpose as `pubkey`. Use of a Nostr public key and direct message SHOULD be preferred over this. Contents of this field SHOULD be a URI, using schemes such as `mailto` or `https` to provide users with a means of contact. - -### Supported NIPs ### - -As the Nostr protocol evolves, some functionality may only be available by relays that implement a specific `NIP`. This field is an array of the integer identifiers of `NIP`s that are implemented in the relay. Examples would include `1`, for `"NIP-01"` and `9`, for `"NIP-09"`. Client-side `NIPs` SHOULD NOT be advertised, and can be ignored by clients. - -### Software ### - -The relay server implementation MAY be provided in the `software` attribute. If present, this MUST be a URL to the project's homepage. - -### Version ### - -The relay MAY choose to publish its software version as a string attribute. The string format is defined by the relay implementation. It is recommended this be a version number or commit identifier. - -Extra Fields ------------------ - -### Server Limitations ### - -These are limitations imposed by the relay on clients. Your client -should expect that requests which exceed these *practical* limitations -are rejected or fail immediately. - -```json -{ -... - "limitation": { - "max_message_length": 16384, - "max_subscriptions": 20, - "max_filters": 100, - "max_limit": 5000, - "max_subid_length": 100, - "min_prefix": 4, - "max_event_tags": 100, - "max_content_length": 8196, - "min_pow_difficulty": 30, - "auth_required": true, - "payment_required": true, - } -... -} -``` - -- `max_message_length`: this is the maximum number of bytes for incoming JSON that the relay -will attempt to decode and act upon. When you send large subscriptions, you will be -limited by this value. It also effectively limits the maximum size of any event. Value is -calculated from `[` to `]` and is after UTF-8 serialization (so some unicode characters -will cost 2-3 bytes). It is equal to the maximum size of the WebSocket message frame. - -- `max_subscriptions`: total number of subscriptions that may be -active on a single websocket connection to this relay. It's possible -that authenticated clients with a (paid) relationship to the relay -may have higher limits. - -- `max_filters`: maximum number of filter values in each subscription. -Must be one or higher. - -- `max_subid_length`: maximum length of subscription id as a string. - -- `min_prefix`: for `authors` and `ids` filters which are to match against -a hex prefix, you must provide at least this many hex digits in the prefix. - -- `max_limit`: the relay server will clamp each filter's `limit` value to this number. -This means the client won't be able to get more than this number -of events from a single subscription filter. This clamping is typically done silently -by the relay, but with this number, you can know that there are additional results -if you narrowed your filter's time range or other parameters. - -- `max_event_tags`: in any event, this is the maximum number of elements in the `tags` list. - -- `max_content_length`: maximum number of characters in the `content` -field of any event. This is a count of unicode characters. After -serializing into JSON it may be larger (in bytes), and is still -subject to the `max_message_length`, if defined. - -- `min_pow_difficulty`: new events will require at least this difficulty of PoW, -based on [NIP-13](13.md), or they will be rejected by this server. - -- `auth_required`: this relay requires [NIP-42](42.md) authentication -to happen before a new connection may perform any other action. -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. - -### Event Retention ### - -There may be a cost associated with storing data forever, so relays -may wish to state retention times. The values stated here are defaults -for unauthenticated users and visitors. Paid users would likely have -other policies. - -Retention times are given in seconds, with `null` indicating infinity. -If zero is provided, this means the event will not be stored at -all, and preferably an error will be provided when those are received. - -```json -{ -... - "retention": [ - { "kinds": [0, 1, [5, 7], [40, 49]], "time": 3600 }, - { "kinds": [[40000, 49999]], "time": 100 }, - { "kinds": [[30000, 39999]], "count": 1000 }, - { "time": 3600, "count": 10000 } - ] -... -} -``` - -`retention` is a list of specifications: each will apply to either all kinds, or -a subset of kinds. Ranges may be specified for the kind field as a tuple of inclusive -start and end values. Events of indicated kind (or all) are then limited to a `count` -and/or time period. - -It is possible to effectively blacklist Nostr-based protocols that rely on -a specific `kind` number, by giving a retention time of zero for those `kind` values. -While that is unfortunate, it does allow clients to discover servers that will -support their protocol quickly via a single HTTP fetch. - -There is no need to specify retention times for _ephemeral events_ as defined -in [NIP-16](16.md) since they are not retained. - - -### Content Limitations ### - -Some relays may be governed by the arbitrary laws of a nation state. This -may limit what content can be stored in cleartext on those relays. All -clients are encouraged to use encryption to work around this limitation. - -It is not possible to describe the limitations of each country's laws -and policies which themselves are typically vague and constantly shifting. - -Therefore, this field allows the relay operator to indicate which -countries' laws might end up being enforced on them, and then -indirectly on their users' content. - -Users should be able to avoid relays in countries they don't like, -and/or select relays in more favourable zones. Exposing this -flexibility is up to the client software. - -```json -{ -... - "relay_countries": [ "CA", "US" ], -... -} -``` - -- `relay_countries`: a list of two-level ISO country codes (ISO 3166-1 alpha-2) whose - laws and policies may affect this relay. `EU` may be used for European Union countries. - -Remember that a relay may be hosted in a country which is not the -country of the legal entities who own the relay, so it's very -likely a number of countries are involved. - - -### Community Preferences ### - -For public text notes at least, a relay may try to foster a -local community. This would encourage users to follow the global -feed on that relay, in addition to their usual individual follows. -To support this goal, relays MAY specify some of the following values. - -```json -{ -... - "language_tags": [ "en", "en-419" ], - "tags": [ "sfw-only", "bitcoin-only", "anime" ], - "posting_policy": "https://example.com/posting-policy.html", -... -} -``` - -- `language_tags` is an ordered list - of [IETF language tags](https://en.wikipedia.org/wiki/IETF_language_tag) indicating - the major languages spoken on the relay. - -- `tags` is a list of limitations on the topics to be discussed. - For example `sfw-only` indicates that only "Safe For Work" content - is encouraged on this relay. This relies on assumptions of what the - "work" "community" feels "safe" talking about. In time, a common - set of tags may emerge that allow users to find relays that suit - their needs, and client software will be able to parse these tags easily. - The `bitcoin-only` tag indicates that any *altcoin*, *"crypto"* or *blockchain* - comments will be ridiculed without mercy. - -- `posting_policy` is a link to a human-readable page which specifies the - community policies for the relay. In cases where `sfw-only` is True, it's - important to link to a page which gets into the specifics of your posting policy. - -The `description` field should be used to describe your community -goals and values, in brief. The `posting_policy` is for additional -detail and legal terms. Use the `tags` field to signify limitations -on content, or topics to be discussed, which could be machine -processed by appropriate client software. - -### Pay-To-Relay ### - -Relays that require payments may want to expose their fee schedules. - -```json -{ -... - "payments_url": "https://my-relay/payments", - "fees": { - "admission": [{ "amount": 1000000, "unit": "msats" }], - "subscription": [{ "amount": 5000000, "unit": "msats", "period": 2592000 }], - "publication": [{ "kinds": [4], "amount": 100, "unit": "msats" }], - }, -... -} -``` +NIP-11 +====== + +Relay Information Document +--------------------------- + +`draft` `optional` `author:scsibug` `author:doc-hex` `author:cameri` + +Relays may provide server metadata to clients to inform them of capabilities, administrative contacts, and various server attributes. This is made available as a JSON document over HTTP, on the same URI as the relay's websocket. + +When a relay receives an HTTP(s) request with an `Accept` header of `application/nostr+json` to a URI supporting WebSocket upgrades, they SHOULD return a document with the following structure. + +```json +{ + "name": , + "description": , + "pubkey": , + "contact": , + "supported_nips": , + "software": , + "version": +} +``` + +Any field may be omitted, and clients MUST ignore any additional fields they do not understand. Relays MUST accept CORS requests by sending `Access-Control-Allow-Origin`, `Access-Control-Allow-Headers`, and `Access-Control-Allow-Methods` headers. + +Field Descriptions +----------------- + +### Name ### + +A relay may select a `name` for use in client software. This is a string, and SHOULD be less than 30 characters to avoid client truncation. + +### Description ### + +Detailed plain-text information about the relay may be contained in the `description` string. It is recommended that this contain no markup, formatting or line breaks for word wrapping, and simply use double newline characters to separate paragraphs. There are no limitations on length. + +### Pubkey ### + +An administrative contact may be listed with a `pubkey`, in the same format as Nostr events (32-byte hex for a `secp256k1` public key). If a contact is listed, this provides clients with a recommended address to send encrypted direct messages (See `NIP-04`) to a system administrator. Expected uses of this address are to report abuse or illegal content, file bug reports, or request other technical assistance. + +Relay operators have no obligation to respond to direct messages. + +### Contact ### + +An alternative contact may be listed under the `contact` field as well, with the same purpose as `pubkey`. Use of a Nostr public key and direct message SHOULD be preferred over this. Contents of this field SHOULD be a URI, using schemes such as `mailto` or `https` to provide users with a means of contact. + +### Supported NIPs ### + +As the Nostr protocol evolves, some functionality may only be available by relays that implement a specific `NIP`. This field is an array of the integer identifiers of `NIP`s that are implemented in the relay. Examples would include `1`, for `"NIP-01"` and `9`, for `"NIP-09"`. Client-side `NIPs` SHOULD NOT be advertised, and can be ignored by clients. + +### Software ### + +The relay server implementation MAY be provided in the `software` attribute. If present, this MUST be a URL to the project's homepage. + +### Version ### + +The relay MAY choose to publish its software version as a string attribute. The string format is defined by the relay implementation. It is recommended this be a version number or commit identifier. + +Extra Fields +----------------- + +### Server Limitations ### + +These are limitations imposed by the relay on clients. Your client +should expect that requests which exceed these *practical* limitations +are rejected or fail immediately. + +```json +{ +... + "limitation": { + "max_message_length": 16384, + "max_subscriptions": 20, + "max_filters": 100, + "max_limit": 5000, + "max_subid_length": 100, + "min_prefix": 4, + "max_event_tags": 100, + "max_content_length": 8196, + "min_pow_difficulty": 30, + "auth_required": true, + "payment_required": true, + } +... +} +``` + +- `max_message_length`: this is the maximum number of bytes for incoming JSON that the relay +will attempt to decode and act upon. When you send large subscriptions, you will be +limited by this value. It also effectively limits the maximum size of any event. Value is +calculated from `[` to `]` and is after UTF-8 serialization (so some unicode characters +will cost 2-3 bytes). It is equal to the maximum size of the WebSocket message frame. + +- `max_subscriptions`: total number of subscriptions that may be +active on a single websocket connection to this relay. It's possible +that authenticated clients with a (paid) relationship to the relay +may have higher limits. + +- `max_filters`: maximum number of filter values in each subscription. +Must be one or higher. + +- `max_subid_length`: maximum length of subscription id as a string. + +- `min_prefix`: for `authors` and `ids` filters which are to match against +a hex prefix, you must provide at least this many hex digits in the prefix. + +- `max_limit`: the relay server will clamp each filter's `limit` value to this number. +This means the client won't be able to get more than this number +of events from a single subscription filter. This clamping is typically done silently +by the relay, but with this number, you can know that there are additional results +if you narrowed your filter's time range or other parameters. + +- `max_event_tags`: in any event, this is the maximum number of elements in the `tags` list. + +- `max_content_length`: maximum number of characters in the `content` +field of any event. This is a count of unicode characters. After +serializing into JSON it may be larger (in bytes), and is still +subject to the `max_message_length`, if defined. + +- `min_pow_difficulty`: new events will require at least this difficulty of PoW, +based on [NIP-13](13.md), or they will be rejected by this server. + +- `auth_required`: this relay requires [NIP-42](42.md) authentication +to happen before a new connection may perform any other action. +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. + +### Event Retention ### + +There may be a cost associated with storing data forever, so relays +may wish to state retention times. The values stated here are defaults +for unauthenticated users and visitors. Paid users would likely have +other policies. + +Retention times are given in seconds, with `null` indicating infinity. +If zero is provided, this means the event will not be stored at +all, and preferably an error will be provided when those are received. + +```json +{ +... + "retention": [ + { "kinds": [0, 1, [5, 7], [40, 49]], "time": 3600 }, + { "kinds": [[40000, 49999]], "time": 100 }, + { "kinds": [[30000, 39999]], "count": 1000 }, + { "time": 3600, "count": 10000 } + ] +... +} +``` + +`retention` is a list of specifications: each will apply to either all kinds, or +a subset of kinds. Ranges may be specified for the kind field as a tuple of inclusive +start and end values. Events of indicated kind (or all) are then limited to a `count` +and/or time period. + +It is possible to effectively blacklist Nostr-based protocols that rely on +a specific `kind` number, by giving a retention time of zero for those `kind` values. +While that is unfortunate, it does allow clients to discover servers that will +support their protocol quickly via a single HTTP fetch. + +There is no need to specify retention times for _ephemeral events_ as defined +in [NIP-16](16.md) since they are not retained. + + +### Content Limitations ### + +Some relays may be governed by the arbitrary laws of a nation state. This +may limit what content can be stored in cleartext on those relays. All +clients are encouraged to use encryption to work around this limitation. + +It is not possible to describe the limitations of each country's laws +and policies which themselves are typically vague and constantly shifting. + +Therefore, this field allows the relay operator to indicate which +countries' laws might end up being enforced on them, and then +indirectly on their users' content. + +Users should be able to avoid relays in countries they don't like, +and/or select relays in more favourable zones. Exposing this +flexibility is up to the client software. + +```json +{ +... + "relay_countries": [ "CA", "US" ], +... +} +``` + +- `relay_countries`: a list of two-level ISO country codes (ISO 3166-1 alpha-2) whose + laws and policies may affect this relay. `EU` may be used for European Union countries. + +Remember that a relay may be hosted in a country which is not the +country of the legal entities who own the relay, so it's very +likely a number of countries are involved. + + +### Community Preferences ### + +For public text notes at least, a relay may try to foster a +local community. This would encourage users to follow the global +feed on that relay, in addition to their usual individual follows. +To support this goal, relays MAY specify some of the following values. + +```json +{ +... + "language_tags": [ "en", "en-419" ], + "tags": [ "sfw-only", "bitcoin-only", "anime" ], + "posting_policy": "https://example.com/posting-policy.html", +... +} +``` + +- `language_tags` is an ordered list + of [IETF language tags](https://en.wikipedia.org/wiki/IETF_language_tag) indicating + the major languages spoken on the relay. + +- `tags` is a list of limitations on the topics to be discussed. + For example `sfw-only` indicates that only "Safe For Work" content + is encouraged on this relay. This relies on assumptions of what the + "work" "community" feels "safe" talking about. In time, a common + set of tags may emerge that allow users to find relays that suit + their needs, and client software will be able to parse these tags easily. + The `bitcoin-only` tag indicates that any *altcoin*, *"crypto"* or *blockchain* + comments will be ridiculed without mercy. + +- `posting_policy` is a link to a human-readable page which specifies the + community policies for the relay. In cases where `sfw-only` is True, it's + important to link to a page which gets into the specifics of your posting policy. + +The `description` field should be used to describe your community +goals and values, in brief. The `posting_policy` is for additional +detail and legal terms. Use the `tags` field to signify limitations +on content, or topics to be discussed, which could be machine +processed by appropriate client software. + +### Pay-To-Relay ### + +Relays that require payments may want to expose their fee schedules. + +```json +{ +... + "payments_url": "https://my-relay/payments", + "fees": { + "admission": [{ "amount": 1000000, "unit": "msats" }], + "subscription": [{ "amount": 5000000, "unit": "msats", "period": 2592000 }], + "publication": [{ "kinds": [4], "amount": 100, "unit": "msats" }], + }, +... +} +``` diff --git a/13.md b/13.md index 360bde6c..58016311 100644 --- a/13.md +++ b/13.md @@ -1,123 +1,123 @@ -NIP-13 -====== - -Proof of Work -------------- - -`draft` `optional` `author:jb55` `author:cameri` - -This NIP defines a way to generate and interpret Proof of Work for nostr notes. Proof of Work (PoW) is a way to add a proof of computational work to a note. This is a bearer proof that all relays and clients can universally validate with a small amount of code. This proof can be used as a means of spam deterrence. - -`difficulty` is defined to be the number of leading zero bits in the `NIP-01` id. For example, an id of `000000000e9d97a1ab09fc381030b346cdd7a142ad57e6df0b46dc9bef6c7e2d` has a difficulty of `36` with `36` leading 0 bits. - -`002f...` is `0000 0000 0010 1111...` in binary, which has 10 leading zeroes. Do not forget to count leading zeroes for hex digits <= `7`. - -Mining ------- - -To generate PoW for a `NIP-01` note, a `nonce` tag is used: - -```json -{"content": "It's just me mining my own business", "tags": [["nonce", "1", "21"]]} -``` - -When mining, the second entry to the nonce tag is updated, and then the id is recalculated (see [NIP-01](./01.md)). If the id has the desired number of leading zero bits, the note has been mined. It is recommended to update the `created_at` as well during this process. - -The third entry to the nonce tag `SHOULD` contain the target difficulty. This allows clients to protect against situations where bulk spammers targeting a lower difficulty get lucky and match a higher difficulty. For example, if you require 40 bits to reply to your thread and see a committed target of 30, you can safely reject it even if the note has 40 bits difficulty. Without a committed target difficulty you could not reject it. Committing to a target difficulty is something all honest miners should be ok with, and clients `MAY` reject a note matching a target difficulty if it is missing a difficulty commitment. - -Example mined note ------------------- - -```json -{ - "id": "000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358", - "pubkey": "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243", - "created_at": 1651794653, - "kind": 1, - "tags": [ - [ - "nonce", - "776797", - "21" - ] - ], - "content": "It's just me mining my own business", - "sig": "284622fc0a3f4f1303455d5175f7ba962a3300d136085b9566801bc2e0699de0c7e31e44c81fb40ad9049173742e904713c3594a1da0fc5d2382a25c11aba977" -} -``` - -Validating ----------- - -Here is some reference C code for calculating the difficulty (aka number of leading zero bits) in a nostr event id: - -```c -#include -#include -#include - -int countLeadingZeroes(const char *hex) { - int count = 0; - - for (int i = 0; i < strlen(hex); i++) { - int nibble = (int)strtol((char[]){hex[i], '\0'}, NULL, 16); - if (nibble == 0) { - count += 4; - } else { - count += __builtin_clz(nibble) - 28; - break; - } - } - - return count; -} - -int main(int argc, char *argv[]) { - if (argc != 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; - } - - const char *hex_string = argv[1]; - int result = countLeadingZeroes(hex_string); - printf("Leading zeroes in hex string %s: %d\n", hex_string, result); - - return 0; -} -``` - -Here is some JavaScript code for doing the same thing: - -```javascript -// hex should be a hexadecimal string (with no 0x prefix) -function countLeadingZeroes(hex) { - let count = 0; - - for (let i = 0; i < hex.length; i++) { - const nibble = parseInt(hex[i], 16); - if (nibble === 0) { - count += 4; - } else { - count += Math.clz32(nibble) - 28; - break; - } - } - - return count; -} -``` - -Querying relays for PoW notes ------------------------------ - -Since relays allow searching on prefixes, you can use this as a way to filter notes of a certain difficulty: - -``` -$ echo '["REQ", "subid", {"ids": ["000000000"]}]' | websocat wss://some-relay.com | jq -c '.[2]' -{"id":"000000000121637feeb68a06c8fa7abd25774bdedfa9b6ef648386fb3b70c387", ...} -``` - -Delegated Proof of Work ------------------------ - -Since the `NIP-01` note id does not commit to any signature, PoW can be outsourced to PoW providers, perhaps for a fee. This provides a way for clients to get their messages out to PoW-restricted relays without having to do any work themselves, which is useful for energy-constrained devices like mobile phones. +NIP-13 +====== + +Proof of Work +------------- + +`draft` `optional` `author:jb55` `author:cameri` + +This NIP defines a way to generate and interpret Proof of Work for nostr notes. Proof of Work (PoW) is a way to add a proof of computational work to a note. This is a bearer proof that all relays and clients can universally validate with a small amount of code. This proof can be used as a means of spam deterrence. + +`difficulty` is defined to be the number of leading zero bits in the `NIP-01` id. For example, an id of `000000000e9d97a1ab09fc381030b346cdd7a142ad57e6df0b46dc9bef6c7e2d` has a difficulty of `36` with `36` leading 0 bits. + +`002f...` is `0000 0000 0010 1111...` in binary, which has 10 leading zeroes. Do not forget to count leading zeroes for hex digits <= `7`. + +Mining +------ + +To generate PoW for a `NIP-01` note, a `nonce` tag is used: + +```json +{"content": "It's just me mining my own business", "tags": [["nonce", "1", "21"]]} +``` + +When mining, the second entry to the nonce tag is updated, and then the id is recalculated (see [NIP-01](./01.md)). If the id has the desired number of leading zero bits, the note has been mined. It is recommended to update the `created_at` as well during this process. + +The third entry to the nonce tag `SHOULD` contain the target difficulty. This allows clients to protect against situations where bulk spammers targeting a lower difficulty get lucky and match a higher difficulty. For example, if you require 40 bits to reply to your thread and see a committed target of 30, you can safely reject it even if the note has 40 bits difficulty. Without a committed target difficulty you could not reject it. Committing to a target difficulty is something all honest miners should be ok with, and clients `MAY` reject a note matching a target difficulty if it is missing a difficulty commitment. + +Example mined note +------------------ + +```json +{ + "id": "000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358", + "pubkey": "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243", + "created_at": 1651794653, + "kind": 1, + "tags": [ + [ + "nonce", + "776797", + "21" + ] + ], + "content": "It's just me mining my own business", + "sig": "284622fc0a3f4f1303455d5175f7ba962a3300d136085b9566801bc2e0699de0c7e31e44c81fb40ad9049173742e904713c3594a1da0fc5d2382a25c11aba977" +} +``` + +Validating +---------- + +Here is some reference C code for calculating the difficulty (aka number of leading zero bits) in a nostr event id: + +```c +#include +#include +#include + +int countLeadingZeroes(const char *hex) { + int count = 0; + + for (int i = 0; i < strlen(hex); i++) { + int nibble = (int)strtol((char[]){hex[i], '\0'}, NULL, 16); + if (nibble == 0) { + count += 4; + } else { + count += __builtin_clz(nibble) - 28; + break; + } + } + + return count; +} + +int main(int argc, char *argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + const char *hex_string = argv[1]; + int result = countLeadingZeroes(hex_string); + printf("Leading zeroes in hex string %s: %d\n", hex_string, result); + + return 0; +} +``` + +Here is some JavaScript code for doing the same thing: + +```javascript +// hex should be a hexadecimal string (with no 0x prefix) +function countLeadingZeroes(hex) { + let count = 0; + + for (let i = 0; i < hex.length; i++) { + const nibble = parseInt(hex[i], 16); + if (nibble === 0) { + count += 4; + } else { + count += Math.clz32(nibble) - 28; + break; + } + } + + return count; +} +``` + +Querying relays for PoW notes +----------------------------- + +Since relays allow searching on prefixes, you can use this as a way to filter notes of a certain difficulty: + +``` +$ echo '["REQ", "subid", {"ids": ["000000000"]}]' | websocat wss://some-relay.com | jq -c '.[2]' +{"id":"000000000121637feeb68a06c8fa7abd25774bdedfa9b6ef648386fb3b70c387", ...} +``` + +Delegated Proof of Work +----------------------- + +Since the `NIP-01` note id does not commit to any signature, PoW can be outsourced to PoW providers, perhaps for a fee. This provides a way for clients to get their messages out to PoW-restricted relays without having to do any work themselves, which is useful for energy-constrained devices like mobile phones. diff --git a/15.md b/15.md index 617c0115..0ff63f13 100644 --- a/15.md +++ b/15.md @@ -1,214 +1,214 @@ -NIP-15 -====== - -Nostr Marketplace (for resilient marketplaces) ------------------------------------ - -`draft` `optional` `author:fiatjaf` `author:benarc` `author:motorina0` `author:talvasconcelos` - -> Based on https://github.com/lnbits/Diagon-Alley - -> Implemented here https://github.com/lnbits/nostrmarket - -## Terms - -- `merchant` - seller of products with NOSTR key-pair -- `customer` - buyer of products with NOSTR key-pair -- `product` - item for sale by the `merchant` -- `stall` - list of products controlled by `merchant` (a `merchant` can have multiple stalls) -- `marketplace` - clientside software for searching `stalls` and purchasing `products` - -## Nostr Marketplace Clients - -### Merchant admin - -Where the `merchant` creates, updates and deletes `stalls` and `products`, as well as where they manage sales, payments and communication with `customers`. - -The `merchant` admin software can be purely clientside, but for `convenience` and uptime, implementations will likely have a server client listening for NOSTR events. - -### Marketplace - -`Marketplace` software should be entirely clientside, either as a stand-alone app, or as a purely frontend webpage. A `customer` subscribes to different merchant NOSTR public keys, and those `merchants` `stalls` and `products` become listed and searchable. The marketplace client is like any other ecommerce site, with basket and checkout. `Marketplaces` may also wish to include a `customer` support area for direct message communication with `merchants`. - -## `Merchant` publishing/updating products (event) - -A merchant can publish these events: -| Kind | | Description | NIP | -|---------|------------------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------| -| `0 ` | `set_meta` | The merchant description (similar with any `nostr` public key). | [NIP01 ](https://github.com/nostr-protocol/nips/blob/master/01.md) | -| `30017` | `set_stall` | Create or update a stall. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) | -| `30018` | `set_product` | Create or update a product. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) | -| `4 ` | `direct_message` | Communicate with the customer. The messages can be plain-text or JSON. | [NIP09](https://github.com/nostr-protocol/nips/blob/master/09.md) | -| `5 ` | `delete` | Delete a product or a stall. | [NIP05](https://github.com/nostr-protocol/nips/blob/master/05.md) | - -### Event `30017`: Create or update a stall. - -**Event Content**: -```json -{ - "id": , - "name": , - "description": , - "currency": , - "shipping": [ - { - "id": , - "name": , - "cost": , - "countries": [], - } - ] -} -``` - -Fields that are not self-explanatory: - - `shipping`: - - an array with possible shipping zones for this stall. The customer MUST choose exactly one shipping zone. - - shipping to different zones can have different costs. For some goods (digital for example) the cost can be zero. - - the `id` is an internal value used by the merchant. This value must be sent back as the customer selection. - -**Event Tags**: -```json - "tags": [["d", , - "stall_id": , - "name": , - "description": , - "images": <[String], array of image URLs, optional>, - "currency": , - "price": , - "quantity": , - "specs": [ - [ , ] - ] -} -``` - -Fields that are not self-explanatory: - - `specs`: - - an array of key pair values. It allows for the Customer UI to present 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"]]` - -_Open_: better to move `spec` in the `tags` section of the event? - -**Event Tags**: -```json - "tags": [ - ["d", , - "type": 0, - "name": , - "address": - "message": ", - "contact": { - "nostr": <32-bytes hex of a pubkey>, - "phone": , - "email": , - }, - "items": [ - { - "product_id": , - "quantity": - } - ], - "shipping_id": -} - -``` - -_Open_: is `contact.nostr` required? - - -### Step 2: `merchant` request payment (event) - -Sent back from the merchant for payment. Any payment option is valid that the merchant can check. - -The below json goes in `content` of [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md). - -`payment_options`/`type` include: - -- `url` URL to a payment page, stripe, paypal, btcpayserver, etc -- `btc` onchain bitcoin address -- `ln` bitcoin lightning invoice -- `lnurl` bitcoin lnurl-pay - -```json -{ - "id": , - "type": 1, - "message": , - "payment_options": [ - { - "type": , - "link": - }, - { - "type": , - "link": - }, - { - "type": , - "link": - } - ] -} -``` - -### Step 3: `merchant` verify payment/shipped (event) - -Once payment has been received and processed. - -The below json goes in `content` of [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md). - -```json -{ - "id": , - "type": 2, - "message": , - "paid": , - "shipped": , -} -``` - -## Customer support events - -Customer support is handled over whatever communication method was specified. If communicating via nostr, NIP-04 is used https://github.com/nostr-protocol/nips/blob/master/04.md. - -## Additional - -Standard data models can be found here here +NIP-15 +====== + +Nostr Marketplace (for resilient marketplaces) +----------------------------------- + +`draft` `optional` `author:fiatjaf` `author:benarc` `author:motorina0` `author:talvasconcelos` + +> Based on https://github.com/lnbits/Diagon-Alley + +> Implemented here https://github.com/lnbits/nostrmarket + +## Terms + +- `merchant` - seller of products with NOSTR key-pair +- `customer` - buyer of products with NOSTR key-pair +- `product` - item for sale by the `merchant` +- `stall` - list of products controlled by `merchant` (a `merchant` can have multiple stalls) +- `marketplace` - clientside software for searching `stalls` and purchasing `products` + +## Nostr Marketplace Clients + +### Merchant admin + +Where the `merchant` creates, updates and deletes `stalls` and `products`, as well as where they manage sales, payments and communication with `customers`. + +The `merchant` admin software can be purely clientside, but for `convenience` and uptime, implementations will likely have a server client listening for NOSTR events. + +### Marketplace + +`Marketplace` software should be entirely clientside, either as a stand-alone app, or as a purely frontend webpage. A `customer` subscribes to different merchant NOSTR public keys, and those `merchants` `stalls` and `products` become listed and searchable. The marketplace client is like any other ecommerce site, with basket and checkout. `Marketplaces` may also wish to include a `customer` support area for direct message communication with `merchants`. + +## `Merchant` publishing/updating products (event) + +A merchant can publish these events: +| Kind | | Description | NIP | +|---------|------------------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------| +| `0 ` | `set_meta` | The merchant description (similar with any `nostr` public key). | [NIP01 ](https://github.com/nostr-protocol/nips/blob/master/01.md) | +| `30017` | `set_stall` | Create or update a stall. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) | +| `30018` | `set_product` | Create or update a product. | [NIP33](https://github.com/nostr-protocol/nips/blob/master/33.md) (Parameterized Replaceable Event) | +| `4 ` | `direct_message` | Communicate with the customer. The messages can be plain-text or JSON. | [NIP09](https://github.com/nostr-protocol/nips/blob/master/09.md) | +| `5 ` | `delete` | Delete a product or a stall. | [NIP05](https://github.com/nostr-protocol/nips/blob/master/05.md) | + +### Event `30017`: Create or update a stall. + +**Event Content**: +```json +{ + "id": , + "name": , + "description": , + "currency": , + "shipping": [ + { + "id": , + "name": , + "cost": , + "countries": [], + } + ] +} +``` + +Fields that are not self-explanatory: + - `shipping`: + - an array with possible shipping zones for this stall. The customer MUST choose exactly one shipping zone. + - shipping to different zones can have different costs. For some goods (digital for example) the cost can be zero. + - the `id` is an internal value used by the merchant. This value must be sent back as the customer selection. + +**Event Tags**: +```json + "tags": [["d", , + "stall_id": , + "name": , + "description": , + "images": <[String], array of image URLs, optional>, + "currency": , + "price": , + "quantity": , + "specs": [ + [ , ] + ] +} +``` + +Fields that are not self-explanatory: + - `specs`: + - an array of key pair values. It allows for the Customer UI to present 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"]]` + +_Open_: better to move `spec` in the `tags` section of the event? + +**Event Tags**: +```json + "tags": [ + ["d", , + "type": 0, + "name": , + "address": + "message": ", + "contact": { + "nostr": <32-bytes hex of a pubkey>, + "phone": , + "email": , + }, + "items": [ + { + "product_id": , + "quantity": + } + ], + "shipping_id": +} + +``` + +_Open_: is `contact.nostr` required? + + +### Step 2: `merchant` request payment (event) + +Sent back from the merchant for payment. Any payment option is valid that the merchant can check. + +The below json goes in `content` of [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md). + +`payment_options`/`type` include: + +- `url` URL to a payment page, stripe, paypal, btcpayserver, etc +- `btc` onchain bitcoin address +- `ln` bitcoin lightning invoice +- `lnurl` bitcoin lnurl-pay + +```json +{ + "id": , + "type": 1, + "message": , + "payment_options": [ + { + "type": , + "link": + }, + { + "type": , + "link": + }, + { + "type": , + "link": + } + ] +} +``` + +### Step 3: `merchant` verify payment/shipped (event) + +Once payment has been received and processed. + +The below json goes in `content` of [NIP04](https://github.com/nostr-protocol/nips/blob/master/04.md). + +```json +{ + "id": , + "type": 2, + "message": , + "paid": , + "shipped": , +} +``` + +## Customer support events + +Customer support is handled over whatever communication method was specified. If communicating via nostr, NIP-04 is used https://github.com/nostr-protocol/nips/blob/master/04.md. + +## Additional + +Standard data models can be found here here diff --git a/19.md b/19.md index 6fc4523e..abfdf49e 100644 --- a/19.md +++ b/19.md @@ -1,70 +1,70 @@ -NIP-19 -====== - -bech32-encoded entities ------------------------ - -`draft` `optional` `author:jb55` `author:fiatjaf` `author:Semisol` - -This NIP standardizes bech32-formatted strings that can be used to display keys, ids and other information in clients. These formats are not meant to be used anywhere in the core protocol, they are only meant for displaying to users, copy-pasting, sharing, rendering QR codes and inputting data. - -It is recommended that ids and keys are stored in either hex or binary format, since these formats are closer to what must actually be used the core protocol. - -## Bare keys and ids - -To prevent confusion and mixing between private keys, public keys and event ids, which are all 32 byte strings. bech32-(not-m) encoding with different prefixes can be used for each of these entities. - -These are the possible bech32 prefixes: - - - `npub`: public keys - - `nsec`: private keys - - `note`: note ids - -Example: the hex public key `3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d` translates to `npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6`. - -The bech32 encodings of keys and ids are not meant to be used inside the standard NIP-01 event formats or inside the filters, they're meant for human-friendlier display and input only. Clients should still accept keys in both hex and npub format for now, and convert internally. - -## Shareable identifiers with extra metadata - -When sharing a profile or an event, an app may decide to include relay information and other metadata such that other apps can locate and display these entities more easily. - -For these events, the contents are a binary-encoded list of `TLV` (type-length-value), with `T` and `L` being 1 byte each (`uint8`, i.e. a number in the range of 0-255), and `V` being a sequence of bytes of the size indicated by `L`. - -These are the possible bech32 prefixes with `TLV`: - - - `nprofile`: a nostr profile - - `nevent`: a nostr event - - `nrelay`: a nostr relay - - `naddr`: a nostr parameterized replaceable event coordinate (NIP-33) - -These possible standardized `TLV` types are indicated here: - -- `0`: `special` - - depends on the bech32 prefix: - - for `nprofile` it will be the 32 bytes of the profile public key - - for `nevent` it will be the 32 bytes of the event id - - for `nrelay`, this is the relay URL - - for `naddr`, it is the identifier (the `"d"` tag) of the event being referenced -- `1`: `relay` - - for `nprofile`, `nevent` and `naddr`, _optionally_, a relay in which the entity (profile or event) is more likely to be found, encoded as ascii - - this may be included multiple times -- `2`: `author` - - for `naddr`, the 32 bytes of the pubkey of the event - - for `nevent`, _optionally_, the 32 bytes of the pubkey of the event -- `3`: `kind` - - for `naddr`, the 32-bit unsigned integer of the kind, big-endian - - for `nevent`, _optionally_, the 32-bit unsigned integer of the kind, big-endian - -## Examples - -- `npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg` should decode into the public key hex `7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e` and vice-versa -- `nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5` should decode into the private key hex `67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa` and vice-versa -- `nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p` should decode into a profile with the following TLV items: - - pubkey: `3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d` - - relay: `wss://r.x.com` - - relay: `wss://djbas.sadkb.com` - -## Notes - -- `npub` keys MUST NOT be used in NIP-01 events or in NIP-05 JSON responses, only the hex format is supported there. -- When decoding a bech32-formatted string, TLVs that are not recognized or supported should be ignored, rather than causing an error. +NIP-19 +====== + +bech32-encoded entities +----------------------- + +`draft` `optional` `author:jb55` `author:fiatjaf` `author:Semisol` + +This NIP standardizes bech32-formatted strings that can be used to display keys, ids and other information in clients. These formats are not meant to be used anywhere in the core protocol, they are only meant for displaying to users, copy-pasting, sharing, rendering QR codes and inputting data. + +It is recommended that ids and keys are stored in either hex or binary format, since these formats are closer to what must actually be used the core protocol. + +## Bare keys and ids + +To prevent confusion and mixing between private keys, public keys and event ids, which are all 32 byte strings. bech32-(not-m) encoding with different prefixes can be used for each of these entities. + +These are the possible bech32 prefixes: + + - `npub`: public keys + - `nsec`: private keys + - `note`: note ids + +Example: the hex public key `3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d` translates to `npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6`. + +The bech32 encodings of keys and ids are not meant to be used inside the standard NIP-01 event formats or inside the filters, they're meant for human-friendlier display and input only. Clients should still accept keys in both hex and npub format for now, and convert internally. + +## Shareable identifiers with extra metadata + +When sharing a profile or an event, an app may decide to include relay information and other metadata such that other apps can locate and display these entities more easily. + +For these events, the contents are a binary-encoded list of `TLV` (type-length-value), with `T` and `L` being 1 byte each (`uint8`, i.e. a number in the range of 0-255), and `V` being a sequence of bytes of the size indicated by `L`. + +These are the possible bech32 prefixes with `TLV`: + + - `nprofile`: a nostr profile + - `nevent`: a nostr event + - `nrelay`: a nostr relay + - `naddr`: a nostr parameterized replaceable event coordinate (NIP-33) + +These possible standardized `TLV` types are indicated here: + +- `0`: `special` + - depends on the bech32 prefix: + - for `nprofile` it will be the 32 bytes of the profile public key + - for `nevent` it will be the 32 bytes of the event id + - for `nrelay`, this is the relay URL + - for `naddr`, it is the identifier (the `"d"` tag) of the event being referenced +- `1`: `relay` + - for `nprofile`, `nevent` and `naddr`, _optionally_, a relay in which the entity (profile or event) is more likely to be found, encoded as ascii + - this may be included multiple times +- `2`: `author` + - for `naddr`, the 32 bytes of the pubkey of the event + - for `nevent`, _optionally_, the 32 bytes of the pubkey of the event +- `3`: `kind` + - for `naddr`, the 32-bit unsigned integer of the kind, big-endian + - for `nevent`, _optionally_, the 32-bit unsigned integer of the kind, big-endian + +## Examples + +- `npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg` should decode into the public key hex `7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e` and vice-versa +- `nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5` should decode into the private key hex `67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa` and vice-versa +- `nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p` should decode into a profile with the following TLV items: + - pubkey: `3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d` + - relay: `wss://r.x.com` + - relay: `wss://djbas.sadkb.com` + +## Notes + +- `npub` keys MUST NOT be used in NIP-01 events or in NIP-05 JSON responses, only the hex format is supported there. +- When decoding a bech32-formatted string, TLVs that are not recognized or supported should be ignored, rather than causing an error. diff --git a/21.md b/21.md index bfbb3ae5..05abc255 100644 --- a/21.md +++ b/21.md @@ -1,20 +1,20 @@ -NIP-21 -====== - -`nostr:` URL scheme -------------------- - -`draft` `optional` `author:fiatjaf` - -This NIP standardizes the usage of a common URL scheme for maximum interoperability and openness in the network. - -The scheme is `nostr:`. - -The identifiers that come after are expected to be the same as those defined in [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) (except `nsec`). - -## Examples - -- `nostr:npub1sn0wdenkukak0d9dfczzeacvhkrgz92ak56egt7vdgzn8pv2wfqqhrjdv9` -- `nostr:nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p` -- `nostr:note1fntxtkcy9pjwucqwa9mddn7v03wwwsu9j330jj350nvhpky2tuaspk6nqc` -- `nostr:nevent1qqstna2yrezu5wghjvswqqculvvwxsrcvu7uc0f78gan4xqhvz49d9spr3mhxue69uhkummnw3ez6un9d3shjtn4de6x2argwghx6egpr4mhxue69uhkummnw3ez6ur4vgh8wetvd3hhyer9wghxuet5nxnepm` +NIP-21 +====== + +`nostr:` URL scheme +------------------- + +`draft` `optional` `author:fiatjaf` + +This NIP standardizes the usage of a common URL scheme for maximum interoperability and openness in the network. + +The scheme is `nostr:`. + +The identifiers that come after are expected to be the same as those defined in [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) (except `nsec`). + +## Examples + +- `nostr:npub1sn0wdenkukak0d9dfczzeacvhkrgz92ak56egt7vdgzn8pv2wfqqhrjdv9` +- `nostr:nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p` +- `nostr:note1fntxtkcy9pjwucqwa9mddn7v03wwwsu9j330jj350nvhpky2tuaspk6nqc` +- `nostr:nevent1qqstna2yrezu5wghjvswqqculvvwxsrcvu7uc0f78gan4xqhvz49d9spr3mhxue69uhkummnw3ez6un9d3shjtn4de6x2argwghx6egpr4mhxue69uhkummnw3ez6ur4vgh8wetvd3hhyer9wghxuet5nxnepm` diff --git a/22.md b/22.md index 2172519e..ee16387c 100644 --- a/22.md +++ b/22.md @@ -1,45 +1,45 @@ -NIP-22 -====== - -Event `created_at` Limits ---------------------------- - -`draft` `optional` `author:jeffthibault` `author:Giszmo` - -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 a [NIP-20](20.md) command 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](16.md#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. +NIP-22 +====== + +Event `created_at` Limits +--------------------------- + +`draft` `optional` `author:jeffthibault` `author:Giszmo` + +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 a [NIP-20](20.md) command 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](16.md#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. diff --git a/25.md b/25.md index f74bcc08..248e6e97 100644 --- a/25.md +++ b/25.md @@ -1,49 +1,49 @@ - -NIP-25 -====== - -Reactions ---------- - -`draft` `optional` `author:jb55` - -A reaction is a `kind 7` note that is used to react to other notes. - -The generic reaction, represented by the `content` set to a `+` string, SHOULD -be interpreted as a "like" or "upvote". - -A reaction with `content` set to `-` SHOULD be interpreted as a "dislike" or -"downvote". It SHOULD NOT be counted as a "like", and MAY be displayed as a -downvote or dislike on a post. A client MAY also choose to tally likes against -dislikes in a reddit-like system of upvotes and downvotes, or display them as -separate tallies. - -The `content` MAY be an emoji, in this case it MAY be interpreted as a "like" or "dislike", -or the client MAY display this emoji reaction on the post. - -Tags ----- - -The reaction event SHOULD include `e` and `p` tags from the note the user is -reacting to. This allows users to be notified of reactions to posts they were -mentioned in. Including the `e` tags enables clients to pull all the reactions -associated with individual posts or all the posts in a thread. - -The last `e` tag MUST be the `id` of the note that is being reacted to. - -The last `p` tag MUST be the `pubkey` of the event being reacted to. - -Example code - -```swift -func make_like_event(pubkey: String, privkey: String, liked: NostrEvent) -> NostrEvent { - var tags: [[String]] = liked.tags.filter { - tag in tag.count >= 2 && (tag[0] == "e" || tag[0] == "p") - } - tags.append(["e", liked.id]) - tags.append(["p", liked.pubkey]) - let ev = NostrEvent(content: "+", pubkey: pubkey, kind: 7, tags: tags) - ev.calculate_id() - ev.sign(privkey: privkey) - return ev -} + +NIP-25 +====== + +Reactions +--------- + +`draft` `optional` `author:jb55` + +A reaction is a `kind 7` note that is used to react to other notes. + +The generic reaction, represented by the `content` set to a `+` string, SHOULD +be interpreted as a "like" or "upvote". + +A reaction with `content` set to `-` SHOULD be interpreted as a "dislike" or +"downvote". It SHOULD NOT be counted as a "like", and MAY be displayed as a +downvote or dislike on a post. A client MAY also choose to tally likes against +dislikes in a reddit-like system of upvotes and downvotes, or display them as +separate tallies. + +The `content` MAY be an emoji, in this case it MAY be interpreted as a "like" or "dislike", +or the client MAY display this emoji reaction on the post. + +Tags +---- + +The reaction event SHOULD include `e` and `p` tags from the note the user is +reacting to. This allows users to be notified of reactions to posts they were +mentioned in. Including the `e` tags enables clients to pull all the reactions +associated with individual posts or all the posts in a thread. + +The last `e` tag MUST be the `id` of the note that is being reacted to. + +The last `p` tag MUST be the `pubkey` of the event being reacted to. + +Example code + +```swift +func make_like_event(pubkey: String, privkey: String, liked: NostrEvent) -> NostrEvent { + var tags: [[String]] = liked.tags.filter { + tag in tag.count >= 2 && (tag[0] == "e" || tag[0] == "p") + } + tags.append(["e", liked.id]) + tags.append(["p", liked.pubkey]) + let ev = NostrEvent(content: "+", pubkey: pubkey, kind: 7, tags: tags) + ev.calculate_id() + ev.sign(privkey: privkey) + return ev +} diff --git a/26.md b/26.md index b8fa9028..c6cf2874 100644 --- a/26.md +++ b/26.md @@ -1,108 +1,108 @@ -NIP: 26 -======= - -Delegated Event Signing ------ - -`draft` `optional` `author:markharding` `author:minds` - -This NIP defines how events can be delegated so that they can be signed by other keypairs. - -Another application of this proposal is to abstract away the use of the 'root' keypairs when interacting with clients. For example, a user could generate new keypairs for each client they wish to use and authorize those keypairs to generate events on behalf of their root pubkey, where the root keypair is stored in cold storage. - -#### Introducing the 'delegation' tag - -This NIP introduces a new tag: `delegation` which is formatted as follows: - -```json -[ - "delegation", - , - , - -] -``` - -##### Delegation Token - -The **delegation token** should be a 64-byte Schnorr signature of the sha256 hash of the following string: - -``` -nostr:delegation:: -``` - -##### Conditions Query String - -The following fields and operators are supported in the above query string: - -*Fields*: -1. `kind` - - *Operators*: - - `=${KIND_NUMBER}` - delegatee may only sign events of this kind -2. `created_at` - - *Operators*: - - `<${TIMESTAMP}` - delegatee may only sign events created ***before*** the specified timestamp - - `>${TIMESTAMP}` - delegatee may only sign events created ***after*** the specified timestamp - -In order to create a single condition, you must use a supported field and operator. Multiple conditions can be used in a single query string, including on the same field. Conditions must be combined with `&`. - -For example, the following condition strings are valid: - -- `kind=1&created_at<1675721813` -- `kind=0&kind=1&created_at>1675721813` -- `kind=1&created_at>1674777689&created_at<1675721813` - -For the vast majority of use-cases, it is advisable that query strings should include a `created_at` ***after*** condition reflecting the current time, to prevent the delegatee from publishing historic notes on the delegator's behalf. - -#### Example - -``` -# Delegator: -privkey: ee35e8bb71131c02c1d7e73231daa48e9953d329a4b701f7133c8f46dd21139c -pubkey: 8e0d3d3eb2881ec137a11debe736a9086715a8c8beeeda615780064d68bc25dd - -# Delegatee: -privkey: 777e4f60b4aa87937e13acc84f7abcc3c93cc035cb4c1e9f7a9086dd78fffce1 -pubkey: 477318cfb5427b9cfc66a9fa376150c1ddbc62115ae27cef72417eb959691396 -``` - -Delegation string to grant note publishing authorization to the delegatee (477318cf) from now, for the next 30 days, given the current timestamp is `1674834236`. -```json -nostr:delegation:477318cfb5427b9cfc66a9fa376150c1ddbc62115ae27cef72417eb959691396:kind=1&created_at>1674834236&created_at<1677426236 -``` - -The delegator (8e0d3d3e) then signs a SHA256 hash of the above delegation string, the result of which is the delegation token: -``` -6f44d7fe4f1c09f3954640fb58bd12bae8bb8ff4120853c4693106c82e920e2b898f1f9ba9bd65449a987c39c0423426ab7b53910c0c6abfb41b30bc16e5f524 -``` - -The delegatee (477318cf) can now construct an event on behalf of the delegator (8e0d3d3e). The delegatee then signs the event with its own private key and publishes. -```json -{ - "id": "e93c6095c3db1c31d15ac771f8fc5fb672f6e52cd25505099f62cd055523224f", - "pubkey": "477318cfb5427b9cfc66a9fa376150c1ddbc62115ae27cef72417eb959691396", - "created_at": 1677426298, - "kind": 1, - "tags": [ - [ - "delegation", - "8e0d3d3eb2881ec137a11debe736a9086715a8c8beeeda615780064d68bc25dd", - "kind=1&created_at>1674834236&created_at<1677426236", - "6f44d7fe4f1c09f3954640fb58bd12bae8bb8ff4120853c4693106c82e920e2b898f1f9ba9bd65449a987c39c0423426ab7b53910c0c6abfb41b30bc16e5f524" - ] - ], - "content": "Hello, world!", - "sig": "633db60e2e7082c13a47a6b19d663d45b2a2ebdeaf0b4c35ef83be2738030c54fc7fd56d139652937cdca875ee61b51904a1d0d0588a6acd6168d7be2909d693" -} -``` - -The event should be considered a valid delegation if the conditions are satisfied (`kind=1`, `created_at>1674834236` and `created_at<1677426236` in this example) and, upon validation of the delegation token, are found to be unchanged from the conditions in the original delegation string. - -Clients should display the delegated note as if it was published directly by the delegator (8e0d3d3e). - - -#### Relay & Client Support - -Relays should answer requests such as `["REQ", "", {"authors": ["A"]}]` by querying both the `pubkey` and delegation tags `[1]` value. - +NIP: 26 +======= + +Delegated Event Signing +----- + +`draft` `optional` `author:markharding` `author:minds` + +This NIP defines how events can be delegated so that they can be signed by other keypairs. + +Another application of this proposal is to abstract away the use of the 'root' keypairs when interacting with clients. For example, a user could generate new keypairs for each client they wish to use and authorize those keypairs to generate events on behalf of their root pubkey, where the root keypair is stored in cold storage. + +#### Introducing the 'delegation' tag + +This NIP introduces a new tag: `delegation` which is formatted as follows: + +```json +[ + "delegation", + , + , + +] +``` + +##### Delegation Token + +The **delegation token** should be a 64-byte Schnorr signature of the sha256 hash of the following string: + +``` +nostr:delegation:: +``` + +##### Conditions Query String + +The following fields and operators are supported in the above query string: + +*Fields*: +1. `kind` + - *Operators*: + - `=${KIND_NUMBER}` - delegatee may only sign events of this kind +2. `created_at` + - *Operators*: + - `<${TIMESTAMP}` - delegatee may only sign events created ***before*** the specified timestamp + - `>${TIMESTAMP}` - delegatee may only sign events created ***after*** the specified timestamp + +In order to create a single condition, you must use a supported field and operator. Multiple conditions can be used in a single query string, including on the same field. Conditions must be combined with `&`. + +For example, the following condition strings are valid: + +- `kind=1&created_at<1675721813` +- `kind=0&kind=1&created_at>1675721813` +- `kind=1&created_at>1674777689&created_at<1675721813` + +For the vast majority of use-cases, it is advisable that query strings should include a `created_at` ***after*** condition reflecting the current time, to prevent the delegatee from publishing historic notes on the delegator's behalf. + +#### Example + +``` +# Delegator: +privkey: ee35e8bb71131c02c1d7e73231daa48e9953d329a4b701f7133c8f46dd21139c +pubkey: 8e0d3d3eb2881ec137a11debe736a9086715a8c8beeeda615780064d68bc25dd + +# Delegatee: +privkey: 777e4f60b4aa87937e13acc84f7abcc3c93cc035cb4c1e9f7a9086dd78fffce1 +pubkey: 477318cfb5427b9cfc66a9fa376150c1ddbc62115ae27cef72417eb959691396 +``` + +Delegation string to grant note publishing authorization to the delegatee (477318cf) from now, for the next 30 days, given the current timestamp is `1674834236`. +```json +nostr:delegation:477318cfb5427b9cfc66a9fa376150c1ddbc62115ae27cef72417eb959691396:kind=1&created_at>1674834236&created_at<1677426236 +``` + +The delegator (8e0d3d3e) then signs a SHA256 hash of the above delegation string, the result of which is the delegation token: +``` +6f44d7fe4f1c09f3954640fb58bd12bae8bb8ff4120853c4693106c82e920e2b898f1f9ba9bd65449a987c39c0423426ab7b53910c0c6abfb41b30bc16e5f524 +``` + +The delegatee (477318cf) can now construct an event on behalf of the delegator (8e0d3d3e). The delegatee then signs the event with its own private key and publishes. +```json +{ + "id": "e93c6095c3db1c31d15ac771f8fc5fb672f6e52cd25505099f62cd055523224f", + "pubkey": "477318cfb5427b9cfc66a9fa376150c1ddbc62115ae27cef72417eb959691396", + "created_at": 1677426298, + "kind": 1, + "tags": [ + [ + "delegation", + "8e0d3d3eb2881ec137a11debe736a9086715a8c8beeeda615780064d68bc25dd", + "kind=1&created_at>1674834236&created_at<1677426236", + "6f44d7fe4f1c09f3954640fb58bd12bae8bb8ff4120853c4693106c82e920e2b898f1f9ba9bd65449a987c39c0423426ab7b53910c0c6abfb41b30bc16e5f524" + ] + ], + "content": "Hello, world!", + "sig": "633db60e2e7082c13a47a6b19d663d45b2a2ebdeaf0b4c35ef83be2738030c54fc7fd56d139652937cdca875ee61b51904a1d0d0588a6acd6168d7be2909d693" +} +``` + +The event should be considered a valid delegation if the conditions are satisfied (`kind=1`, `created_at>1674834236` and `created_at<1677426236` in this example) and, upon validation of the delegation token, are found to be unchanged from the conditions in the original delegation string. + +Clients should display the delegated note as if it was published directly by the delegator (8e0d3d3e). + + +#### Relay & Client Support + +Relays should answer requests such as `["REQ", "", {"authors": ["A"]}]` by querying both the `pubkey` and delegation tags `[1]` value. + Relays SHOULD allow the delegator (8e0d3d3e) to delete the events published by the delegatee (477318cf). \ No newline at end of file diff --git a/27.md b/27.md index 6d761206..6b1f4e51 100644 --- a/27.md +++ b/27.md @@ -1,54 +1,54 @@ -NIP-27 -====== - -Text Note References --------------------- - -`draft` `optional` `author:arthurfranca` `author:hodlbod` `author:fiatjaf` - -This document standardizes the treatment given by clients of inline references of other events and profiles inside the `.content` of any event that has readable text in its `.content` (such as kinds 1 and 30023). - -When creating an event, clients should include mentions to other profiles and to other events in the middle of the `.content` using [NIP-21](21.md) codes, such as `nostr:nprofile1qqsw3dy8cpu...6x2argwghx6egsqstvg`. - -Including [NIP-10](10.md)-style tags (`["e", , , ]`) for each reference is optional, clients should do it whenever they want the profile being mentioned to be notified of the mention, or when they want the referenced event to recognize their mention as a reply. - -A reader client that receives an event with such `nostr:...` mentions in its `.content` can do any desired context augmentation (for example, linking to the profile or showing a preview of the mentioned event contents) it wants in the process. If turning such mentions into links, they could become internal links, [NIP-21](21.md) links or direct links to web clients that will handle these references. - ---- - -## Example of a profile mention process - -Suppose Bob is writing a note in a client that has search-and-autocomplete functionality for users that is triggered when they write the character `@`. - -As Bob types `"hello @mat"` the client will prompt him to autocomplete with [mattn's profile](https://gateway.nostr.com/p/2c7cc62a697ea3a7826521f3fd34f0cb273693cbe5e9310f35449f43622a5cdc), showing a picture and name. - -Bob presses "enter" and now he sees his typed note as `"hello @mattn"`, `@mattn` is highlighted, indicating that it is a mention. Internally, however, the event looks like this: - -```json -{ - "content": "hello nostr:nprofile1qqszclxx9f5haga8sfjjrulaxncvkfekj097t6f3pu65f86rvg49ehqj6f9dh", - "created_at": 1679790774, - "id": "f39e9b451a73d62abc5016cffdd294b1a904e2f34536a208874fe5e22bbd47cf", - "kind": 1, - "pubkey": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", - "sig": "f8c8bab1b90cc3d2ae1ad999e6af8af449ad8bb4edf64807386493163e29162b5852a796a8f474d6b1001cddbaac0de4392838574f5366f03cc94cf5dfb43f4d", - "tags": [ - [ - "p", - "2c7cc62a697ea3a7826521f3fd34f0cb273693cbe5e9310f35449f43622a5cdc" - ] - ] -} -``` - -(Alternatively, the mention could have been a `nostr:npub1...` URL.) - -After Bob publishes this event and Carol sees it, her client will initially display the `.content` as it is, but later it will parse the `.content` and see that there is a `nostr:` URL in there, decode it, extract the public key from it (and possibly relay hints), fetch that profile from its internal database or relays, then replace the full URL with the name `@mattn`, with a link to the internal page view for that profile. - -## Verbose and probably unnecessary considerations - -- The example above was very concrete, but it doesn't mean all clients have to implement the same flow. There could be clients that do not support autocomplete at all, so they just allow users to paste raw [NIP-19](19.md) codes into the body of text, then prefix these with `nostr:` before publishing the event. -- The flow for referencing other events is similar: a user could paste a `note1...` or `nevent1...` code and the client will turn that into a `nostr:note1...` or `nostr:nevent1...` URL. Then upon reading such references the client may show the referenced note in a preview box or something like that -- or nothing at all. -- Other display procedures can be employed: for example, if a client that is designed for dealing with only `kind:1` text notes sees, for example, a [`kind:30023`](23.md) `nostr:naddr1...` URL reference in the `.content`, it can, for example, decide to turn that into a link to some hardcoded webapp capable of displaying such events. -- Clients may give the user the option to include or not include tags for mentioned events or profiles. If someone wants to mention `mattn` without notifying them, but still have a nice augmentable/clickable link to their profile inside their note, they can instruct their client to _not_ create a `["p", ...]` tag for that specific mention. -- In the same way, if someone wants to reference another note but their reference is not meant to show up along other replies to that same note, their client can choose to not include a corresponding `["e", ...]` tag for any given `nostr:nevent1...` URL inside `.content`. Clients may decide to expose these advanced functionalities to users or be more opinionated about things. +NIP-27 +====== + +Text Note References +-------------------- + +`draft` `optional` `author:arthurfranca` `author:hodlbod` `author:fiatjaf` + +This document standardizes the treatment given by clients of inline references of other events and profiles inside the `.content` of any event that has readable text in its `.content` (such as kinds 1 and 30023). + +When creating an event, clients should include mentions to other profiles and to other events in the middle of the `.content` using [NIP-21](21.md) codes, such as `nostr:nprofile1qqsw3dy8cpu...6x2argwghx6egsqstvg`. + +Including [NIP-10](10.md)-style tags (`["e", , , ]`) for each reference is optional, clients should do it whenever they want the profile being mentioned to be notified of the mention, or when they want the referenced event to recognize their mention as a reply. + +A reader client that receives an event with such `nostr:...` mentions in its `.content` can do any desired context augmentation (for example, linking to the profile or showing a preview of the mentioned event contents) it wants in the process. If turning such mentions into links, they could become internal links, [NIP-21](21.md) links or direct links to web clients that will handle these references. + +--- + +## Example of a profile mention process + +Suppose Bob is writing a note in a client that has search-and-autocomplete functionality for users that is triggered when they write the character `@`. + +As Bob types `"hello @mat"` the client will prompt him to autocomplete with [mattn's profile](https://gateway.nostr.com/p/2c7cc62a697ea3a7826521f3fd34f0cb273693cbe5e9310f35449f43622a5cdc), showing a picture and name. + +Bob presses "enter" and now he sees his typed note as `"hello @mattn"`, `@mattn` is highlighted, indicating that it is a mention. Internally, however, the event looks like this: + +```json +{ + "content": "hello nostr:nprofile1qqszclxx9f5haga8sfjjrulaxncvkfekj097t6f3pu65f86rvg49ehqj6f9dh", + "created_at": 1679790774, + "id": "f39e9b451a73d62abc5016cffdd294b1a904e2f34536a208874fe5e22bbd47cf", + "kind": 1, + "pubkey": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "sig": "f8c8bab1b90cc3d2ae1ad999e6af8af449ad8bb4edf64807386493163e29162b5852a796a8f474d6b1001cddbaac0de4392838574f5366f03cc94cf5dfb43f4d", + "tags": [ + [ + "p", + "2c7cc62a697ea3a7826521f3fd34f0cb273693cbe5e9310f35449f43622a5cdc" + ] + ] +} +``` + +(Alternatively, the mention could have been a `nostr:npub1...` URL.) + +After Bob publishes this event and Carol sees it, her client will initially display the `.content` as it is, but later it will parse the `.content` and see that there is a `nostr:` URL in there, decode it, extract the public key from it (and possibly relay hints), fetch that profile from its internal database or relays, then replace the full URL with the name `@mattn`, with a link to the internal page view for that profile. + +## Verbose and probably unnecessary considerations + +- The example above was very concrete, but it doesn't mean all clients have to implement the same flow. There could be clients that do not support autocomplete at all, so they just allow users to paste raw [NIP-19](19.md) codes into the body of text, then prefix these with `nostr:` before publishing the event. +- The flow for referencing other events is similar: a user could paste a `note1...` or `nevent1...` code and the client will turn that into a `nostr:note1...` or `nostr:nevent1...` URL. Then upon reading such references the client may show the referenced note in a preview box or something like that -- or nothing at all. +- Other display procedures can be employed: for example, if a client that is designed for dealing with only `kind:1` text notes sees, for example, a [`kind:30023`](23.md) `nostr:naddr1...` URL reference in the `.content`, it can, for example, decide to turn that into a link to some hardcoded webapp capable of displaying such events. +- Clients may give the user the option to include or not include tags for mentioned events or profiles. If someone wants to mention `mattn` without notifying them, but still have a nice augmentable/clickable link to their profile inside their note, they can instruct their client to _not_ create a `["p", ...]` tag for that specific mention. +- In the same way, if someone wants to reference another note but their reference is not meant to show up along other replies to that same note, their client can choose to not include a corresponding `["e", ...]` tag for any given `nostr:nevent1...` URL inside `.content`. Clients may decide to expose these advanced functionalities to users or be more opinionated about things. diff --git a/40.md b/40.md index 32680db8..b7b26ecd 100644 --- a/40.md +++ b/40.md @@ -1,59 +1,59 @@ -NIP-40 -====== - -Expiration Timestamp ------------------------------------ - -`draft` `optional` `author:0xtlt` - -The `expiration` tag enables users to specify a unix timestamp at which the message SHOULD be considered expired (by relays and clients) and SHOULD be deleted by relays. - -#### Spec - -``` -tag: expiration -values: - - [UNIX timestamp in seconds]: required -``` - -#### Example - -```json -{ - "pubkey": "", - "created_at": 1000000000, - "kind": 1, - "tags": [ - ["expiration", "1600000000"] - ], - "content": "This message will expire at the specified timestamp and be deleted by relays.\n", - "id": "" -} -``` - -Note: The timestamp should be in the same format as the created_at timestamp and should be interpreted as the time at which the message should be deleted by relays. - -Client Behavior ---------------- - -Clients SHOULD use the `supported_nips` field to learn if a relay supports this NIP. Clients SHOULD NOT send expiration events to relays that do not support this NIP. - -Clients SHOULD ignore events that have expired. - -Relay Behavior --------------- - -Relays MAY NOT delete expired messages immediately on expiration and MAY persist them indefinitely. -Relays SHOULD NOT send expired events to clients, even if they are stored. -Relays SHOULD drop any events that are published to them if they are expired. -An expiration timestamp does not affect storage of ephemeral events. - -Suggested Use Cases -------------------- - -* Temporary announcements - This tag can be used to make temporary announcements. For example, an event organizer could use this tag to post announcements about an upcoming event. -* Limited-time offers - This tag can be used by businesses to make limited-time offers that expire after a certain amount of time. For example, a business could use this tag to make a special offer that is only available for a limited time. - -#### Warning -The events could be downloaded by third parties as they are publicly accessible all the time on the relays. -So don't consider expiring messages as a security feature for your conversations or other uses. +NIP-40 +====== + +Expiration Timestamp +----------------------------------- + +`draft` `optional` `author:0xtlt` + +The `expiration` tag enables users to specify a unix timestamp at which the message SHOULD be considered expired (by relays and clients) and SHOULD be deleted by relays. + +#### Spec + +``` +tag: expiration +values: + - [UNIX timestamp in seconds]: required +``` + +#### Example + +```json +{ + "pubkey": "", + "created_at": 1000000000, + "kind": 1, + "tags": [ + ["expiration", "1600000000"] + ], + "content": "This message will expire at the specified timestamp and be deleted by relays.\n", + "id": "" +} +``` + +Note: The timestamp should be in the same format as the created_at timestamp and should be interpreted as the time at which the message should be deleted by relays. + +Client Behavior +--------------- + +Clients SHOULD use the `supported_nips` field to learn if a relay supports this NIP. Clients SHOULD NOT send expiration events to relays that do not support this NIP. + +Clients SHOULD ignore events that have expired. + +Relay Behavior +-------------- + +Relays MAY NOT delete expired messages immediately on expiration and MAY persist them indefinitely. +Relays SHOULD NOT send expired events to clients, even if they are stored. +Relays SHOULD drop any events that are published to them if they are expired. +An expiration timestamp does not affect storage of ephemeral events. + +Suggested Use Cases +------------------- + +* Temporary announcements - This tag can be used to make temporary announcements. For example, an event organizer could use this tag to post announcements about an upcoming event. +* Limited-time offers - This tag can be used by businesses to make limited-time offers that expire after a certain amount of time. For example, a business could use this tag to make a special offer that is only available for a limited time. + +#### Warning +The events could be downloaded by third parties as they are publicly accessible all the time on the relays. +So don't consider expiring messages as a security feature for your conversations or other uses. diff --git a/45.md b/45.md index 87e80002..f89945c0 100644 --- a/45.md +++ b/45.md @@ -1,39 +1,39 @@ -NIP-45 -====== - -Event Counts --------------- - -`draft` `optional` `author:staab` - -Relays may support the `COUNT` verb, which provides a mechanism for obtaining event counts. - -## Motivation - -Some queries a client may want to execute against connected relays are prohibitively expensive, for example, in order to retrieve follower counts for a given pubkey, a client must query all kind-3 events referring to a given pubkey and count them. The result may be cached, either by a client or by a separate indexing server as an alternative, but both options erode the decentralization of the network by creating a second-layer protocol on top of Nostr. - -## Filters and return values - -This NIP defines a verb called `COUNT`, which accepts a subscription id and filters as specified in [NIP 01](01.md). - -``` -["COUNT", , ...] -``` - -Counts are returned using a `COUNT` response in the form `{count: }`. Relays may use probabilistic counts to reduce compute requirements. - -``` -["COUNT", , {"count": }] -``` - -Examples: - -``` -# Followers count -["COUNT", , {"kinds": [3], "#p": []}] -["COUNT", , {"count": 238}] - -# Count posts and reactions -["COUNT", , {"kinds": [1, 7], "authors": []}] -["COUNT", , {"count": 5}] -``` +NIP-45 +====== + +Event Counts +-------------- + +`draft` `optional` `author:staab` + +Relays may support the `COUNT` verb, which provides a mechanism for obtaining event counts. + +## Motivation + +Some queries a client may want to execute against connected relays are prohibitively expensive, for example, in order to retrieve follower counts for a given pubkey, a client must query all kind-3 events referring to a given pubkey and count them. The result may be cached, either by a client or by a separate indexing server as an alternative, but both options erode the decentralization of the network by creating a second-layer protocol on top of Nostr. + +## Filters and return values + +This NIP defines a verb called `COUNT`, which accepts a subscription id and filters as specified in [NIP 01](01.md). + +``` +["COUNT", , ...] +``` + +Counts are returned using a `COUNT` response in the form `{count: }`. Relays may use probabilistic counts to reduce compute requirements. + +``` +["COUNT", , {"count": }] +``` + +Examples: + +``` +# Followers count +["COUNT", , {"kinds": [3], "#p": []}] +["COUNT", , {"count": 238}] + +# Count posts and reactions +["COUNT", , {"kinds": [1, 7], "authors": []}] +["COUNT", , {"count": 5}] +``` diff --git a/51.md b/51.md index 80cc09ef..83f34a41 100644 --- a/51.md +++ b/51.md @@ -1,112 +1,112 @@ -NIP-51 -====== - -Lists -------------------------- - -`draft` `optional` `author:fiatjaf` `author:arcbtc` `author:monlovesmango` `author:eskema` `depends:33` - -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. - -If a list type should only be defined once per user (like the 'Mute' list), the list type's events should follow the specification for [NIP-16 - Replaceable Events](16.md). These lists may be referred to as 'replaceable lists'. - -Otherwise, the list type's events should follow the specification for [NIP-33 - Parameterized Replaceable Events](33.md), where the list name will be used as the 'd' parameter. These lists may be referred to as 'parameterized replaceable lists'. - -## Replaceable List Event Example - -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: - -```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 'Mute' list event like below: - -```json -{ - "kind": 10000, - "tags": [ - ["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"], - ["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"], - ], - "content": "VezuSvWak++ASjFMRqBPWS3mK5pZ0vRLL325iuIL4S+r8n9z+DuMau5vMElz1tGC/UqCDmbzE2kwplafaFo/FnIZMdEj4pdxgptyBV1ifZpH3TEF6OMjEtqbYRRqnxgIXsuOSXaerWgpi0pm+raHQPseoELQI/SZ1cvtFqEUCXdXpa5AYaSd+quEuthAEw7V1jP+5TDRCEC8jiLosBVhCtaPpLcrm8HydMYJ2XB6Ixs=?iv=/rtV49RFm0XyFEwG62Eo9A==", - ...other fields -} -``` - - -## 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, - "tags": [ - ["d", "nostr"], - ["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"], - ["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"], - ], - "content": "VezuSvWak++ASjFMRqBPWS3mK5pZ0vRLL325iuIL4S+r8n9z+DuMau5vMElz1tGC/UqCDmbzE2kwplafaFo/FnIZMdEj4pdxgptyBV1ifZpH3TEF6OMjEtqbYRRqnxgIXsuOSXaerWgpi0pm+raHQPseoELQI/SZ1cvtFqEUCXdXpa5AYaSd+quEuthAEw7V1jP+5TDRCEC8jiLosBVhCtaPpLcrm8HydMYJ2XB6Ixs=?iv=/rtV49RFm0XyFEwG62Eo9A==", - ...other fields -} -``` - -## List Event Kinds - -| kind | list type | -| ------ | ----------------------- | -| 10000 | Mute | -| 10001 | Pin | -| 30000 | Categorized People | -| 30001 | Categorized Bookmarks | - -### 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 with 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. Any standardized tag can be included in a Categorized Bookmarks List. +NIP-51 +====== + +Lists +------------------------- + +`draft` `optional` `author:fiatjaf` `author:arcbtc` `author:monlovesmango` `author:eskema` `depends:33` + +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. + +If a list type should only be defined once per user (like the 'Mute' list), the list type's events should follow the specification for [NIP-16 - Replaceable Events](16.md). These lists may be referred to as 'replaceable lists'. + +Otherwise, the list type's events should follow the specification for [NIP-33 - Parameterized Replaceable Events](33.md), where the list name will be used as the 'd' parameter. These lists may be referred to as 'parameterized replaceable lists'. + +## Replaceable List Event Example + +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: + +```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 'Mute' list event like below: + +```json +{ + "kind": 10000, + "tags": [ + ["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"], + ["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"], + ], + "content": "VezuSvWak++ASjFMRqBPWS3mK5pZ0vRLL325iuIL4S+r8n9z+DuMau5vMElz1tGC/UqCDmbzE2kwplafaFo/FnIZMdEj4pdxgptyBV1ifZpH3TEF6OMjEtqbYRRqnxgIXsuOSXaerWgpi0pm+raHQPseoELQI/SZ1cvtFqEUCXdXpa5AYaSd+quEuthAEw7V1jP+5TDRCEC8jiLosBVhCtaPpLcrm8HydMYJ2XB6Ixs=?iv=/rtV49RFm0XyFEwG62Eo9A==", + ...other fields +} +``` + + +## 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, + "tags": [ + ["d", "nostr"], + ["p", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"], + ["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"], + ], + "content": "VezuSvWak++ASjFMRqBPWS3mK5pZ0vRLL325iuIL4S+r8n9z+DuMau5vMElz1tGC/UqCDmbzE2kwplafaFo/FnIZMdEj4pdxgptyBV1ifZpH3TEF6OMjEtqbYRRqnxgIXsuOSXaerWgpi0pm+raHQPseoELQI/SZ1cvtFqEUCXdXpa5AYaSd+quEuthAEw7V1jP+5TDRCEC8jiLosBVhCtaPpLcrm8HydMYJ2XB6Ixs=?iv=/rtV49RFm0XyFEwG62Eo9A==", + ...other fields +} +``` + +## List Event Kinds + +| kind | list type | +| ------ | ----------------------- | +| 10000 | Mute | +| 10001 | Pin | +| 30000 | Categorized People | +| 30001 | Categorized Bookmarks | + +### 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 with 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. Any standardized tag can be included in a Categorized Bookmarks List. diff --git a/78.md b/78.md index 10ff5358..e527d6b5 100644 --- a/78.md +++ b/78.md @@ -1,21 +1,21 @@ -NIP-78 -====== - -Arbitrary custom app data -------------------------- - -`draft` `optional` `author:sandwich` `author:fiatjaf` - -The goal of this NIP is to enable [remoteStorage](https://remotestorage.io/)-like capabilities for custom applications that do not care about interoperability. - -Even though interoperability is great, some apps do not want or do not need interoperability, and it wouldn't make sense for them. Yet Nostr can still serve as a generalized data storage for these apps in a "bring your own database" way, for example: a user would open an app and somehow input their preferred relay for storage, which would then enable these apps to store application-specific data there. - -## Nostr event - -This NIP specifies the use of event kind `30078` (parameterized replaceable event) with a `d` tag containing some reference to the app name and context -- or any other arbitrary string. `content` and other `tags` can be anything or in any format. - -## Some use cases - - - User personal settings on Nostr clients (and other apps unrelated to Nostr) - - A way for client developers to propagate dynamic parameters to users without these having to update - - Personal private data generated by apps that have nothing to do with Nostr, but allow users to use Nostr relays as their personal database +NIP-78 +====== + +Arbitrary custom app data +------------------------- + +`draft` `optional` `author:sandwich` `author:fiatjaf` + +The goal of this NIP is to enable [remoteStorage](https://remotestorage.io/)-like capabilities for custom applications that do not care about interoperability. + +Even though interoperability is great, some apps do not want or do not need interoperability, and it wouldn't make sense for them. Yet Nostr can still serve as a generalized data storage for these apps in a "bring your own database" way, for example: a user would open an app and somehow input their preferred relay for storage, which would then enable these apps to store application-specific data there. + +## Nostr event + +This NIP specifies the use of event kind `30078` (parameterized replaceable event) with a `d` tag containing some reference to the app name and context -- or any other arbitrary string. `content` and other `tags` can be anything or in any format. + +## Some use cases + + - User personal settings on Nostr clients (and other apps unrelated to Nostr) + - A way for client developers to propagate dynamic parameters to users without these having to update + - Personal private data generated by apps that have nothing to do with Nostr, but allow users to use Nostr relays as their personal database diff --git a/94.md b/94.md index 24dd346f..387eab18 100644 --- a/94.md +++ b/94.md @@ -1,51 +1,51 @@ -NIP-94 -====== - -File Metadata -------------- - -`draft` `optional` `author:frbitten` `author:kieran` `author:lovvtide` `author:fiatjaf` `author:staab` - -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 - -This NIP specifies the use of the `1063` event type, having in `content` a description of the file content, and a list of tags described below: - -* `url` the url to download the file -* `m` a string indicating the data type of the file. The MIME types format must be used (https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) -* `"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 `x` -* `magnet` (optional) URI to magnet file -* `i` (optional) torrent infohash -* `blurhash`(optional) the [blurhash](https://github.com/woltapp/blurhash) to show while the file is being loaded by the client - -```json -{ - "id": <32-bytes lowercase hex-encoded sha256 of the the serialized event data>, - "pubkey": <32-bytes lowercase hex-encoded public key of the event creator>, - "created_at": , - "kind": 1063, - "tags": [ - ["url",], - ["aes-256-gcm",, ], - ["m", ], - ["x",], - ["size", ], - ["dim", ], - ["magnet", ], - ["i",], - ["blurhash", ] - ], - "content": , - "sig": <64-bytes hex of the signature of the sha256 hash of the serialized event data, which is the same as the "id" field> -} -``` - -## Suggested use cases - -* A relay for indexing shared files. For example, to promote torrents. -* A pinterest-like client where people can share their portfolio and inspire others. -* A simple way to distribute configurations and software updates. +NIP-94 +====== + +File Metadata +------------- + +`draft` `optional` `author:frbitten` `author:kieran` `author:lovvtide` `author:fiatjaf` `author:staab` + +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 + +This NIP specifies the use of the `1063` event type, having in `content` a description of the file content, and a list of tags described below: + +* `url` the url to download the file +* `m` a string indicating the data type of the file. The MIME types format must be used (https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) +* `"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 `x` +* `magnet` (optional) URI to magnet file +* `i` (optional) torrent infohash +* `blurhash`(optional) the [blurhash](https://github.com/woltapp/blurhash) to show while the file is being loaded by the client + +```json +{ + "id": <32-bytes lowercase hex-encoded sha256 of the the serialized event data>, + "pubkey": <32-bytes lowercase hex-encoded public key of the event creator>, + "created_at": , + "kind": 1063, + "tags": [ + ["url",], + ["aes-256-gcm",, ], + ["m", ], + ["x",], + ["size", ], + ["dim", ], + ["magnet", ], + ["i",], + ["blurhash", ] + ], + "content": , + "sig": <64-bytes hex of the signature of the sha256 hash of the serialized event data, which is the same as the "id" field> +} +``` + +## Suggested use cases + +* A relay for indexing shared files. For example, to promote torrents. +* A pinterest-like client where people can share their portfolio and inspire others. +* A simple way to distribute configurations and software updates. diff --git a/README.md b/README.md index 90be4f52..5c93e234 100644 --- a/README.md +++ b/README.md @@ -1,183 +1,183 @@ -# NIPs - -NIPs stand for **Nostr Implementation Possibilities**. -They exist to document what may be implemented by [Nostr](https://github.com/fiatjaf/nostr)-compatible _relay_ and _client_ software. - ---- - -- [List](#list) -- [Event Kinds](#event-kinds) - - [Event Kind Ranges](#event-kind-ranges) -- [Message Types](#message-types) - - [Client to Relay](#client-to-relay) - - [Relay to Client](#relay-to-client) -- [Standardized Tags](#standardized-tags) -- [Criteria for acceptance of NIPs](#criteria-for-acceptance-of-nips) -- [License](#license) - ---- - -## List - -- [NIP-01: Basic protocol flow description](01.md) -- [NIP-02: Contact List and Petnames](02.md) -- [NIP-03: OpenTimestamps Attestations for Events](03.md) -- [NIP-04: Encrypted Direct Message](04.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) -- [NIP-08: Handling Mentions](08.md) --- **unrecommended**: deprecated in favor of [NIP-27](27.md) -- [NIP-09: Event Deletion](09.md) -- [NIP-10: Conventions for clients' use of `e` and `p` tags in text events](10.md) -- [NIP-11: Relay Information Document](11.md) -- [NIP-12: Generic Tag Queries](12.md) -- [NIP-13: Proof of Work](13.md) -- [NIP-14: Subject tag in text events.](14.md) -- [NIP-15: Nostr Marketplace (for resilient marketplaces)](15.md) -- [NIP-16: Event Treatment](16.md) -- [NIP-18: Reposts](18.md) -- [NIP-19: bech32-encoded entities](19.md) -- [NIP-20: Command Results](20.md) -- [NIP-21: `nostr:` URL scheme](21.md) -- [NIP-22: Event `created_at` Limits](22.md) -- [NIP-23: Long-form Content](23.md) -- [NIP-25: Reactions](25.md) -- [NIP-26: Delegated Event Signing](26.md) -- [NIP-27: Text Note References](27.md) -- [NIP-28: Public Chat](28.md) -- [NIP-33: Parameterized Replaceable Events](33.md) -- [NIP-36: Sensitive Content](36.md) -- [NIP-39: External Identities in Profiles](39.md) -- [NIP-40: Expiration Timestamp](40.md) -- [NIP-42: Authentication of clients to relays](42.md) -- [NIP-45: Counting results](45.md) -- [NIP-46: Nostr Connect](46.md) -- [NIP-50: Keywords filter](50.md) -- [NIP-51: Lists](51.md) -- [NIP-56: Reporting](56.md) -- [NIP-57: Lightning Zaps](57.md) -- [NIP-58: Badges](58.md) -- [NIP-65: Relay List Metadata](65.md) -- [NIP-69: Polls](69.md) -- [NIP-78: Application-specific data](78.md) -- [NIP-94: File Metadata](94.md) - -## Event Kinds - -| kind | description | NIP | -| ------- | -------------------------- | ----------- | -| `0` | Metadata | [1](01.md) | -| `1` | Short Text Note | [1](01.md) | -| `2` | Recommend Relay | [1](01.md) | -| `3` | Contacts | [2](02.md) | -| `4` | Encrypted Direct Messages | [4](04.md) | -| `5` | Event Deletion | [9](09.md) | -| `6` | Reposts | [18](18.md) | -| `7` | Reaction | [25](25.md) | -| `8` | Badge Award | [58](58.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) | -| `1984` | Reporting | [56](56.md) | -| `6969` | Poll | [69](69.md) | -| `9734` | Zap Request | [57](57.md) | -| `9735` | Zap | [57](57.md) | -| `10000` | Mute List | [51](51.md) | -| `10001` | Pin List | [51](51.md) | -| `10002` | Relay List Metadata | [65](65.md) | -| `22242` | Client Authentication | [42](42.md) | -| `24133` | Nostr Connect | [46](46.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) | -| `30078` | Application-specific Data | [78](78.md) | - -### Event Kind Ranges - -| range | description | NIP | -| ---------------- | -------------------------------- | ----------- | -| `1000`--`9999` | Regular Events | [16](16.md) | -| `10000`--`19999` | Replaceable Events | [16](16.md) | -| `20000`--`29999` | Ephemeral Events | [16](16.md) | -| `30000`--`39999` | Parameterized Replaceable Events | [33](33.md) | - -## Message types - -### Client to Relay - -| type | description | NIP | -| ------- | --------------------------------------------------- | ----------- | -| `AUTH` | used to send authentication events | [42](42.md) | -| `CLOSE` | used to stop previous subscriptions | [1](01.md) | -| `COUNT` | used to request event counts | [45](45.md) | -| `EVENT` | used to publish events | [1](01.md) | -| `REQ` | used to request events and subscribe to new updates | [1](01.md) | - -### Relay to Client - -| type | description | NIP | -| -------- | ------------------------------------------------------- | ----------- | -| `AUTH` | used to send authentication challenges | [42](42.md) | -| `COUNT` | used to send requested event counts to clients | [45](45.md) | -| `EOSE` | used to notify clients all stored events have been sent | [1](01.md) | -| `EVENT` | used to send events requested to clients | [1](01.md) | -| `NOTICE` | used to send human-readable messages to clients | [1](01.md) | -| `OK` | used to notify clients if an EVENT was successful | [20](20.md) | - -Please update these lists when proposing NIPs introducing new event kinds. - -When experimenting with kinds, keep in mind the classification introduced by [NIP-16](16.md) and [NIP-33](33.md). - -## Standardized Tags - -| name | value | other parameters | NIP | -|-------------------|--------------------------------------|----------------------| ------------------------ | -| `a` | coordinates to an event | relay URL | [33](33.md), [23](23.md) | -| `d` | identifier | -- | [33](33.md) | -| `e` | event id (hex) | relay URL, marker | [1](01.md), [10](10.md) | -| `g` | geohash | -- | [12](12.md) | -| `i` | identity | proof | [39](39.md) | -| `p` | pubkey (hex) | relay URL | [1](01.md) | -| `r` | a reference (URL, etc) | -- | [12](12.md) | -| `t` | hashtag | -- | [12](12.md) | -| `amount` | millisats | -- | [57](57.md) | -| `bolt11` | `bolt11` invoice | -- | [57](57.md) | -| `challenge` | challenge string | -- | [42](42.md) | -| `content-warning` | reason | -- | [36](36.md) | -| `delegation` | pubkey, conditions, delegation token | -- | [26](26.md) | -| `description` | badge description | -- | [58](58.md) | -| `description` | invoice description | -- | [57](57.md) | -| `expiration` | unix timestamp (string) | -- | [40](40.md) | -| `image` | image URL | dimensions in pixels | [23](23.md), [58](58.md) | -| `lnurl` | `bech32` encoded `lnurl` | -- | [57](57.md) | -| `name` | badge name | -- | [58](58.md) | -| `nonce` | random | -- | [13](13.md) | -| `poll_option` | option key value (string) | -- | [57](57.md), [69](69.md) | -| `preimage` | hash of `bolt11` invoice | -- | [57](57.md) | -| `published_at` | unix timestamp (string) | -- | [23](23.md) | -| `relay` | relay url | -- | [42](42.md) | -| `relays` | relay list | -- | [57](57.md) | -| `subject` | subject | -- | [14](14.md) | -| `summary` | article summary | -- | [23](23.md) | -| `thumb` | badge thumbnail | dimensions in pixels | [58](58.md) | -| `title` | article title | -- | [23](23.md) | -| `zap` | profile name | type of value | [57](57.md) | - -## Criteria for acceptance of NIPs - -1. They should be implemented in at least two clients and one relay -- when applicable. -2. They should make sense. -3. They should be optional and backwards-compatible: care must be taken such that clients and relays that choose to not implement them do not stop working when interacting with the ones that choose to. -4. There should be no more than one way of doing the same thing. -5. Other rules will be made up when necessary. - -## License - -All NIPs are public domain. +# NIPs + +NIPs stand for **Nostr Implementation Possibilities**. +They exist to document what may be implemented by [Nostr](https://github.com/fiatjaf/nostr)-compatible _relay_ and _client_ software. + +--- + +- [List](#list) +- [Event Kinds](#event-kinds) + - [Event Kind Ranges](#event-kind-ranges) +- [Message Types](#message-types) + - [Client to Relay](#client-to-relay) + - [Relay to Client](#relay-to-client) +- [Standardized Tags](#standardized-tags) +- [Criteria for acceptance of NIPs](#criteria-for-acceptance-of-nips) +- [License](#license) + +--- + +## List + +- [NIP-01: Basic protocol flow description](01.md) +- [NIP-02: Contact List and Petnames](02.md) +- [NIP-03: OpenTimestamps Attestations for Events](03.md) +- [NIP-04: Encrypted Direct Message](04.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) +- [NIP-08: Handling Mentions](08.md) --- **unrecommended**: deprecated in favor of [NIP-27](27.md) +- [NIP-09: Event Deletion](09.md) +- [NIP-10: Conventions for clients' use of `e` and `p` tags in text events](10.md) +- [NIP-11: Relay Information Document](11.md) +- [NIP-12: Generic Tag Queries](12.md) +- [NIP-13: Proof of Work](13.md) +- [NIP-14: Subject tag in text events.](14.md) +- [NIP-15: Nostr Marketplace (for resilient marketplaces)](15.md) +- [NIP-16: Event Treatment](16.md) +- [NIP-18: Reposts](18.md) +- [NIP-19: bech32-encoded entities](19.md) +- [NIP-20: Command Results](20.md) +- [NIP-21: `nostr:` URL scheme](21.md) +- [NIP-22: Event `created_at` Limits](22.md) +- [NIP-23: Long-form Content](23.md) +- [NIP-25: Reactions](25.md) +- [NIP-26: Delegated Event Signing](26.md) +- [NIP-27: Text Note References](27.md) +- [NIP-28: Public Chat](28.md) +- [NIP-33: Parameterized Replaceable Events](33.md) +- [NIP-36: Sensitive Content](36.md) +- [NIP-39: External Identities in Profiles](39.md) +- [NIP-40: Expiration Timestamp](40.md) +- [NIP-42: Authentication of clients to relays](42.md) +- [NIP-45: Counting results](45.md) +- [NIP-46: Nostr Connect](46.md) +- [NIP-50: Keywords filter](50.md) +- [NIP-51: Lists](51.md) +- [NIP-56: Reporting](56.md) +- [NIP-57: Lightning Zaps](57.md) +- [NIP-58: Badges](58.md) +- [NIP-65: Relay List Metadata](65.md) +- [NIP-69: Polls](69.md) +- [NIP-78: Application-specific data](78.md) +- [NIP-94: File Metadata](94.md) + +## Event Kinds + +| kind | description | NIP | +| ------- | -------------------------- | ----------- | +| `0` | Metadata | [1](01.md) | +| `1` | Short Text Note | [1](01.md) | +| `2` | Recommend Relay | [1](01.md) | +| `3` | Contacts | [2](02.md) | +| `4` | Encrypted Direct Messages | [4](04.md) | +| `5` | Event Deletion | [9](09.md) | +| `6` | Reposts | [18](18.md) | +| `7` | Reaction | [25](25.md) | +| `8` | Badge Award | [58](58.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) | +| `1984` | Reporting | [56](56.md) | +| `6969` | Poll | [69](69.md) | +| `9734` | Zap Request | [57](57.md) | +| `9735` | Zap | [57](57.md) | +| `10000` | Mute List | [51](51.md) | +| `10001` | Pin List | [51](51.md) | +| `10002` | Relay List Metadata | [65](65.md) | +| `22242` | Client Authentication | [42](42.md) | +| `24133` | Nostr Connect | [46](46.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) | +| `30078` | Application-specific Data | [78](78.md) | + +### Event Kind Ranges + +| range | description | NIP | +| ---------------- | -------------------------------- | ----------- | +| `1000`--`9999` | Regular Events | [16](16.md) | +| `10000`--`19999` | Replaceable Events | [16](16.md) | +| `20000`--`29999` | Ephemeral Events | [16](16.md) | +| `30000`--`39999` | Parameterized Replaceable Events | [33](33.md) | + +## Message types + +### Client to Relay + +| type | description | NIP | +| ------- | --------------------------------------------------- | ----------- | +| `AUTH` | used to send authentication events | [42](42.md) | +| `CLOSE` | used to stop previous subscriptions | [1](01.md) | +| `COUNT` | used to request event counts | [45](45.md) | +| `EVENT` | used to publish events | [1](01.md) | +| `REQ` | used to request events and subscribe to new updates | [1](01.md) | + +### Relay to Client + +| type | description | NIP | +| -------- | ------------------------------------------------------- | ----------- | +| `AUTH` | used to send authentication challenges | [42](42.md) | +| `COUNT` | used to send requested event counts to clients | [45](45.md) | +| `EOSE` | used to notify clients all stored events have been sent | [1](01.md) | +| `EVENT` | used to send events requested to clients | [1](01.md) | +| `NOTICE` | used to send human-readable messages to clients | [1](01.md) | +| `OK` | used to notify clients if an EVENT was successful | [20](20.md) | + +Please update these lists when proposing NIPs introducing new event kinds. + +When experimenting with kinds, keep in mind the classification introduced by [NIP-16](16.md) and [NIP-33](33.md). + +## Standardized Tags + +| name | value | other parameters | NIP | +|-------------------|--------------------------------------|----------------------| ------------------------ | +| `a` | coordinates to an event | relay URL | [33](33.md), [23](23.md) | +| `d` | identifier | -- | [33](33.md) | +| `e` | event id (hex) | relay URL, marker | [1](01.md), [10](10.md) | +| `g` | geohash | -- | [12](12.md) | +| `i` | identity | proof | [39](39.md) | +| `p` | pubkey (hex) | relay URL | [1](01.md) | +| `r` | a reference (URL, etc) | -- | [12](12.md) | +| `t` | hashtag | -- | [12](12.md) | +| `amount` | millisats | -- | [57](57.md) | +| `bolt11` | `bolt11` invoice | -- | [57](57.md) | +| `challenge` | challenge string | -- | [42](42.md) | +| `content-warning` | reason | -- | [36](36.md) | +| `delegation` | pubkey, conditions, delegation token | -- | [26](26.md) | +| `description` | badge description | -- | [58](58.md) | +| `description` | invoice description | -- | [57](57.md) | +| `expiration` | unix timestamp (string) | -- | [40](40.md) | +| `image` | image URL | dimensions in pixels | [23](23.md), [58](58.md) | +| `lnurl` | `bech32` encoded `lnurl` | -- | [57](57.md) | +| `name` | badge name | -- | [58](58.md) | +| `nonce` | random | -- | [13](13.md) | +| `poll_option` | option key value (string) | -- | [57](57.md), [69](69.md) | +| `preimage` | hash of `bolt11` invoice | -- | [57](57.md) | +| `published_at` | unix timestamp (string) | -- | [23](23.md) | +| `relay` | relay url | -- | [42](42.md) | +| `relays` | relay list | -- | [57](57.md) | +| `subject` | subject | -- | [14](14.md) | +| `summary` | article summary | -- | [23](23.md) | +| `thumb` | badge thumbnail | dimensions in pixels | [58](58.md) | +| `title` | article title | -- | [23](23.md) | +| `zap` | profile name | type of value | [57](57.md) | + +## Criteria for acceptance of NIPs + +1. They should be implemented in at least two clients and one relay -- when applicable. +2. They should make sense. +3. They should be optional and backwards-compatible: care must be taken such that clients and relays that choose to not implement them do not stop working when interacting with the ones that choose to. +4. There should be no more than one way of doing the same thing. +5. Other rules will be made up when necessary. + +## License + +All NIPs are public domain.