Compare commits

..

86 Commits

Author SHA1 Message Date
fiatjaf
8a2977bcc5 rename argument. 2023-08-15 11:53:25 -03:00
fiatjaf
2239f96541 remove these TLV functions. 2023-08-15 11:50:39 -03:00
fiatjaf
c78bd4cef1 encoding implementation doesn't require complicate TLV parsing. 2023-08-15 11:40:54 -03:00
Jonathan Staab
a1f8a82e73 Switch from JSON to custom TLV for nip 44 2023-08-14 17:42:33 -07:00
Jonathan Staab
3a37d7c8b9 Tweak some wording 2023-08-12 14:08:25 -07:00
Jon Staab
06f8dbadc8 Update 44.md
Typo

Co-authored-by: Alex Gleason <alex@alexgleason.me>
2023-08-12 12:01:36 -07:00
Jonathan Staab
4d95efcf87 Rename nip44 ciphertext to payload in nip 07 2023-08-11 17:43:58 -07:00
Jonathan Staab
e2eeda5200 Add version param to 07 and 46 2023-08-11 17:37:29 -07:00
Jonathan Staab
94330ffb10 Fix typo 2023-08-11 13:24:29 -07:00
Jonathan Staab
30696049cc Deprecate NIP 04 2023-08-11 13:14:52 -07:00
Jonathan Staab
00a8f9532e Small wording tweaks 2023-08-11 09:23:56 -07:00
Jonathan Staab
95103569b5 Introduce NIP-44 encryption standard 2023-08-11 08:34:18 -07:00
Vitor Pamplona
a5047326d4 Simplifies NIP-65 (#700)
Co-authored-by: fiatjaf_ <fiatjaf@gmail.com>
Co-authored-by: Jon Staab <jstaab@protonmail.com>
2023-08-11 07:12:50 -03:00
benthecarman
d87f86178b NIP-25: Handle empty string as + 2023-08-10 23:32:54 -03:00
Jon Staab
a4b35284c9 Merge pull request #705 from mattn/fix-typo
fix typos
2023-08-09 09:39:32 -07:00
Yasuhiro Matsumoto
f91cb5ce66 fix typos 2023-08-10 01:34:49 +09:00
Semisol
ce7e6b2100 wrong branch 2023-08-09 00:56:48 +02:00
Semisol
77b626d748 nips over nostr 2023-08-09 00:55:21 +02:00
fiatjaf
892fe9e400 move 172 to 72 and update indexes. 2023-08-08 13:31:54 -03:00
Vitor Pamplona
507b0c20a5 added a third option to approve replaceable events. 2023-08-08 13:27:32 -03:00
Vitor Pamplona
7957880b48 Adds relay markers 2023-08-08 13:27:32 -03:00
Vitor Pamplona
f8aa3f4e51 Allows replaceable events to be part of communities as well. 2023-08-08 13:27:32 -03:00
Vitor Pamplona
73f2f24bbf Better describes the use of a, e and p tags in the post approval event. 2023-08-08 13:27:32 -03:00
Vitor Pamplona
135a2f5338 Rewriting to use specification verbs SHOULD, MAY, etc 2023-08-08 13:27:32 -03:00
Vitor Pamplona
d42fc18fa5 Addressing how to unapprove a post. 2023-08-08 13:27:32 -03:00
Vitor Pamplona
3a01861ade Adds references to other NIPs with more information and standardizes citations to event kinds 2023-08-08 13:27:32 -03:00
Vitor Pamplona
63441099be Fixes typos & clarifies the text. 2023-08-08 13:27:32 -03:00
Vitor Pamplona
2b53049c1a Adds arthurfranca as author 2023-08-08 13:27:32 -03:00
Vitor Pamplona
c07b5fa9b0 Moves post approval to a regular (non-replaceable event) 2023-08-08 13:27:32 -03:00
Vitor Pamplona
3ff40201bd Fixes double-quoted kind number
Co-authored-by: arthurfranca <arthur.a.franca@gmail.com>
2023-08-08 13:27:32 -03:00
Vitor Pamplona
4c7b728be1 Fix formatting
Co-authored-by: arthurfranca <arthur.a.franca@gmail.com>
2023-08-08 13:27:32 -03:00
Vitor Pamplona
856ed84776 Fix typos in community image
Co-authored-by: arthurfranca <arthur.a.franca@gmail.com>
2023-08-08 13:27:32 -03:00
Vitor Pamplona
0c3df0ee30 Removes quotes from kind
Co-authored-by: arthurfranca <arthur.a.franca@gmail.com>
2023-08-08 13:27:32 -03:00
Vitor Pamplona
4e61eb4e46 Update 172.md
Adds kind to the post approval.

Co-authored-by: arthurfranca <arthur.a.franca@gmail.com>
2023-08-08 13:27:32 -03:00
Vitor Pamplona
bf84e733f3 Adds preferred relay information to tags. 2023-08-08 13:27:32 -03:00
Vitor Pamplona
6fbe488504 Fixing indentation 2023-08-08 13:27:32 -03:00
Vitor Pamplona
7f4970bb10 Typos 2023-08-08 13:27:32 -03:00
Vitor Pamplona
1912dacd33 Simple moderated communities flow. 2023-08-08 13:27:32 -03:00
Alex Gleason
33fcb7c9bc Merge pull request #693 from alexgleason/proxy
NIP-48: Proxy Tags
2023-08-07 14:11:13 -05:00
Alex Gleason
064a79f614 NIP-48: Proxy Tags 2023-08-03 14:40:45 -05:00
Terry Yiu
b4cdc1a73d NIP-52: Calendar Events 2023-08-01 12:28:54 -03:00
Jon Staab
5d63b1570c Merge pull request #552 from vitorpamplona/zap-plits
Adds Zap splits to NIP-57
2023-07-31 08:26:15 -07:00
Alex Gleason
c2907f836d Merge pull request #684 from lyager/patch-1
Update 01.md
2023-07-26 09:09:41 -05:00
Jesper L. Nielsen
9e0be8467d Update 01.md
Spelling
2023-07-26 15:59:57 +02:00
Alex Gleason
7c5728e3b1 Merge pull request #681 from AsaiToshiya/patch-17
Minor JSON fix
2023-07-24 23:20:25 -05:00
Asai Toshiya
e58a40d2e7 Minor JSON fix 2023-07-25 12:33:16 +09:00
Asai Toshiya
4a386e645c Add draft kinds to list 2023-07-23 10:30:13 -03:00
benthecarman
461b4bb16f Merge pull request #676 from nostr-protocol/benthecarman-patch-1 2023-07-22 12:52:55 -05:00
Asai Toshiya
b503f8a92b Clarify sort order when specified limit 2023-07-21 10:33:05 -03:00
benthecarman
2af496e363 Fix event kind in example in NIP 53 2023-07-21 03:12:02 -05:00
Jon Staab
fe2009b459 Merge pull request #675 from erechorse/inline-codes
Fix typos about inline code
2023-07-20 09:57:52 -07:00
erechorse
d0cb9d0c24 Fix typos about inline code 2023-07-21 00:48:55 +09:00
William Casarin
ad39e1f3ca readme: add mailing lists 2023-07-19 20:46:23 -03:00
Pablo Fernandez
00f9f5b049 Merge pull request #662 from erskingardner/new-event-for-classifieds 2023-07-18 22:53:30 +02:00
Jeff Gardner
8efa0e76b4 Update title 2023-07-18 22:52:18 +02:00
Jeff Gardner
859bd471fe Update readme, change NIP number 2023-07-18 22:48:23 +02:00
William Casarin
afcbef2bb0 nip23: add some formatting guidelines to maximize compatibility
Link: https://groups.google.com/g/nostr-protocol/c/tnrcWTIAzdU
2023-07-18 15:52:39 -03:00
Terry Yiu
b31d3077f6 Fix content field in example in NIP-56 Reporting to replace deprecated NIP-08 indexed mentions with NIP-27 bech32 mentions 2023-07-18 15:50:52 -03:00
Jeff Gardner
b480624ac2 Improve formatting 2023-07-18 15:22:56 +02:00
Jeff Gardner
63718d6d89 Update price detail 2023-07-18 15:20:32 +02:00
Jeff Gardner
629c787d28 Update for feedback on format of price array 2023-07-17 12:34:24 +02:00
jiftechnify
d1814405be add detailed description of since/until 2023-07-14 12:45:46 -03:00
Jon Staab
20b9bb787b Merge pull request #664 from AsaiToshiya/patch-14
Fix typo
2023-07-13 17:29:30 -07:00
Asai Toshiya
dd4caf9c4c Fix typo 2023-07-14 09:08:03 +09:00
Jeff Gardner
f065a40ee6 Add image tags and change format of the price tag. 2023-07-14 00:05:02 +02:00
Jeff Gardner
451c06a3c5 Add initial draft for classifieds NIP 2023-07-13 15:14:19 +02:00
Yoji Shidara
0b08cf545b Explicitly state that the subscription_id is treated per connection 2023-07-13 07:08:26 -03:00
Asai Toshiya
1889ac792b Add k tag to list 2023-07-13 07:07:48 -03:00
Asai Toshiya
f5a930c824 Fix NIP-21 title 2023-07-12 07:28:49 -03:00
Asai Toshiya
ed32c93c9f Adjust NIP-14 title to other NIPs 2023-07-12 07:28:49 -03:00
Yasuhiro Matsumoto
00ec0c83ac Add new section Custom Emoji Reaction 2023-07-11 19:59:00 -03:00
Yasuhiro Matsumoto
a1cd2bd809 NIP-25 content might be NIP-30 custom emoji 2023-07-11 19:59:00 -03:00
Asai Toshiya
7cd861c4d3 Update tag list 2023-07-10 09:21:27 -03:00
jiftechnify
20b22e7079 add NIP-53 to the list 2023-07-09 07:17:43 -03:00
jiftechnify
52edccbbe3 standardize the terminology 2023-07-09 07:17:08 -03:00
jiftechnify
3a32c0fd78 clarify case-sensitiveness of tag names 2023-07-09 07:17:08 -03:00
Vitor Pamplona
141197c564 NIP-53 Live Activities (#498) 2023-07-08 09:11:22 -03:00
Vitor Pamplona
4d0929b278 Merge branch 'master' into zap-plits 2023-06-10 19:56:03 -04:00
Vitor Pamplona
91bdf63b13 grammar 2023-05-24 12:22:16 -04:00
Vitor Pamplona
5834c05439 Dropping lud06 and lud16 from the zap tag 2023-05-24 12:20:53 -04:00
Vitor Pamplona
2f8be7c32b better example of weights. 2023-05-24 10:41:14 -04:00
Vitor Pamplona
9c97736066 minor text/grammar adjustments 2023-05-24 10:10:18 -04:00
Vitor Pamplona
7c3e590247 Moves weights to be a string as tags can only contain strings 2023-05-24 09:46:36 -04:00
Vitor Pamplona
dd5c9c54ae Changes the client guidance 2023-05-23 13:50:55 -04:00
Vitor Pamplona
a56d650333 fixing json formatting 2023-05-23 13:46:05 -04:00
Vitor Pamplona
e4937befd6 Zap splits 2023-05-23 13:44:58 -04:00
19 changed files with 888 additions and 180 deletions

8
01.md
View File

@@ -55,7 +55,7 @@ Clients can send 3 types of messages, which must be JSON arrays, according to th
* `["REQ", <subscription_id>, <filters JSON>...]`, used to request events and subscribe to new updates.
* `["CLOSE", <subscription_id>]`, used to stop previous subscriptions.
`<subscription_id>` is an arbitrary, non-empty string of max length 64 chars, that should be used to represent a subscription.
`<subscription_id>` is an arbitrary, non-empty string of max length 64 chars, that should be used to represent a subscription. Relays should manage `<subscription_id>`s independently for each WebSocket connection; even if `<subscription_id>`s are the same string, they should be treated as different subscriptions for different connections.
`<filters>` is a JSON object that determines what events will be sent in that subscription, it can have the following attributes:
@@ -78,11 +78,13 @@ Filter attributes containing lists (such as `ids`, `kinds`, or `#e`) are JSON ar
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.
The `since` and `until` properties can be used to specify the time range of events returned in the subscription. If a filter includes the `since` property, events with `created_at` greater than or equal to `since` are considered to match the filter. The `until` property is similar except that `created_at` must be less than or equal to `until`. In short, an event matches a filter if `since <= created_at <= until` holds.
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.
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 last `n` events ordered by the `created_at`. 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
@@ -107,7 +109,7 @@ A relay may choose to treat different message kinds differently, and it may or m
## 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](10.md) for a detailed description of "e" and "p" tags.
- The `tags` array can store a case-sensitive tag name as the first element of each subarray, plus arbitrary information afterward (always as strings). This NIP defines `"p"` — meaning "pubkey", which points to a pubkey of someone that is referred to in the event —, and `"e"` — meaning "event", which points to the id of an event this event is quoting, replying to or referring to somehow. See [NIP-10](10.md) for a detailed description of "e" and "p" tags.
- The `<recommended relay URL>` item present on the `"e"` and `"p"` tags is an optional (could be set to `""`) URL of a relay the client could attempt to connect to fetch the tagged event or other events from a tagged profile. It MAY be ignored, but it exists to increase censorship resistance and make the spread of relay addresses more seamless across clients.
- Clients should use the created_at field to judge the age of a metadata event and completely replace older metadata events with newer metadata events regardless of the order in which they arrive. Clients should not merge any filled fields within older metadata events into empty fields of newer metadata events.
- When a websocket is closed by the relay with a status code 4000 that means the client shouldn't try to connect again.

4
04.md
View File

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

6
07.md
View File

@@ -18,8 +18,10 @@ async window.nostr.signEvent(event: Event): Event // takes an event object, adds
Aside from these two basic above, the following functions can also be implemented optionally:
```
async window.nostr.getRelays(): { [url: string]: {read: boolean, write: boolean} } // returns a basic map of relay urls to relay policies
async window.nostr.nip04.encrypt(pubkey, plaintext): string // returns ciphertext and iv as specified in nip-04
async window.nostr.nip04.decrypt(pubkey, ciphertext): string // takes ciphertext and iv as specified in nip-04
async window.nostr.nip04.encrypt(pubkey, plaintext): string // returns ciphertext and iv as specified in nip-04 (deprecated)
async window.nostr.nip04.decrypt(pubkey, ciphertext): string // takes ciphertext and iv as specified in nip-04 (deprecated)
async window.nostr.nip44.encrypt(pubkey, plaintext, v): string // returns encrypted payload as specified in nip-44
async window.nostr.nip44.decrypt(pubkey, payload): string // takes encrypted payload as specified in nip-44
```
### Implementation

2
12.md
View File

@@ -8,7 +8,7 @@ Generic Tag Queries
Relays may support subscriptions over arbitrary tags. `NIP-01` requires relays to respond to queries for `e` and `p` tags. This NIP allows any single-letter tag present in an event to be queried.
The `<filters>` object described in `NIP-01` is expanded to contain arbitrary keys with a `#` prefix. Any single-letter key in a filter beginning with `#` is a tag query, and MUST have a value of an array of strings. The filter condition matches if the event has a tag with the same name, and there is at least one tag value in common with the filter and event. The tag name is the letter without the `#`, and the tag value is the second element. Subsequent elements are ignored for the purposes of tag queries.
The `<filters>` object described in `NIP-01` is expanded to contain arbitrary keys with a `#` prefix. Any single-letter key in a filter beginning with `#` is a tag query, and MUST have a value of an array of strings. The filter condition matches if the event has a tag with the same name, and there is at least one tag value in common with the filter and event. The tag name is the letter without the `#`, and the tag value is the second element. Subsequent elements are ignored for the purposes of tag queries. Note that tag names are case-sensitive.
Example Subscription Filter
---------------------------

4
14.md
View File

@@ -1,8 +1,8 @@
NIP-14
======
Subject tag in Text events.
---------------------------
Subject tag in Text events
--------------------------
`draft` `optional` `author:unclebobmartin`

6
23.md
View File

@@ -12,7 +12,11 @@ This NIP defines `kind:30023` (a parameterized replaceable event according to [N
### Format
The `.content` of these events should be a string text in Markdown syntax.
The `.content` of these events should be a string text in Markdown syntax. To maximize compatibility and readability between different clients and devices, any client that is creating long form notes:
- MUST NOT hard line-break paragraphs of text, such as arbitrary line breaks at 80 column boundaries.
- MUST NOT support adding HTML to Markdown.
### Metadata

27
25.md
View File

@@ -18,8 +18,9 @@ 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.
The `content` MAY be an emoji, or [NIP-30](30.md) custom emoji in this case it MAY be interpreted as a "like" or "dislike",
or the client MAY display this emoji reaction on the post. If the `content` is an empty string then the client should
consider it a "+".
Tags
----
@@ -47,3 +48,25 @@ func make_like_event(pubkey: String, privkey: String, liked: NostrEvent) -> Nost
ev.sign(privkey: privkey)
return ev
}
```
Custom Emoji Reaction
---------------------
The client may specify a custom emoji ([NIP-30](30.md)) `:shortcode:` in the
reaction content. The client should refer to the emoji tag and render the
content as an emoji if shortcode is specified.
```json
{
"kind": 7,
"content": ":soapbox:",
"tags": [
["emoji", "soapbox", "https://gleasonator.com/emoji/Gleasonator/soapbox.png"]
],
"pubkey": "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6",
"created_at": 1682790000
}
```
The content can be set only one `:shortcode:`. And emoji tag should be one.

95
37.md
View File

@@ -1,95 +0,0 @@
NIP-37
======
Methods for dealing with lost or compromised keys
-------------------------------------------------
`draft` `optional` `author:fiatjaf`
This NIP defines a series of methods that can be used to make catastrophic key loss events less horrible.
The intention here is not to claim that there is a generalized "key rotation" scheme on Nostr, but still make it
so that in most cases people are able to move to new keys and not lost 100% of their followers and reputation
when that happens, and also that it is possible to prevent most of the reputational damage that could be
caused by having an evil hacker take control of someone else's keys.
The implementation of this NIP could be done directly in social clients or in dedicated clients that users would
visit from time to time in order to have an automated scan be done on their contacts and then the dedicated app
could suggest migrating from old keys to new keys, or suggest stop following compromised keys, or even perform
these migrations automatically.
### Method A: Simple Key Deletion
Based on https://github.com/nostr-protocol/nips/pull/377, the idea is that someone, after noticing that their key
has been compromised and deciding that it is not possible to use any of the other methods here described, can
proceed to publish the following event:
```js
{
"kind": 10529,
"pubkey": "<compromised-pubkey>",
"tags": [
["reason", "I typed my key on anigma"]
// this is unnecessary, but it's an extra requirement to prevent people from making a 10529 event by chance
["key-compromised"],
],
"content": "",
// ...
}
```
Upon receiving this, relays implementing this NIP SHOULD somehow mark it as invalid and refuse to store any more
events from this key. Relays MAY also decide to also delete all events from this key.
Upon receiving this, clients implementing this NIP SHOULD stop following the key.
Clients may give users the option to "delete your public key" with this type of event. Clients SHOULD display a
prominent message explaining that the action is not reversible, SHOULD explain that this does not guarantee their
posts are deleted forever, and SHOULD require special confirmation such as requiring the user to type a message.
### Method B: Social Key Migration
The idea here is that users could pick some friends from their contact list and a threshold number to signal to
the rest that the old key has been lost and what is the new key -- e.g. Alice can determine that, from Bob, Carol
and Derek, if any 2 of these 3 agree, their recommendation is authoritative.
This is done by publishing an event like this (for example):
```js
{
"kind": 10520,
"pubkey": "<user-pubkey-that-will-be-compromised>",
"tags": [
["p", "<friend1-pubkey>"],
["p", "<friend2-pubkey>"],
["p", "<friend3-pubkey>"],
["threshold", "2"]
],
"content": "",
// ...
}
```
And then, when the friends want to recommend the switch to a new pubkey, they can publish events like
```js
{
"kind": 1521,
"pubkey": "<friend1-pubkey>",
"tags": [
["p", "<compromised-pubkey>"],
["e", "<event-of-kind-10520-that-this-refers-to>"],
["new-key", "<new-key>"]
],
"content": "",
// ...
}
```
Very importantly, when evaluating events of kind `1521` for a given `p`, clients should ensure that their corresponding
event `10520` is at least 3 months older **and that an OpenTimestamps proof exists for that**.
### Method C: Hierarchically Pregenerated Keys
I'll write this later, just a placeholder. It's probably better to see if methods A and B work first.

162
44.md Normal file
View File

@@ -0,0 +1,162 @@
NIP-44
======
Encrypted Payloads (Versioned)
------------------------------
`optional` `author:paulmillr` `author:staab`
The NIP introduces a new data format for keypair-based encryption. This NIP is versioned to allow multiple algorithm choices to exist simultaneously.
An encrypted payload MUST be encoded as a JSON object. Different versions may have different parameters. Every format has a `v` field specifying its version.
Currently defined encryption algorithms:
- `0x00` - Reserved
- `0x01` - XChaCha with same key `sha256(ecdh)` per conversation
# Version 1
Params:
1. `nonce`: base64-encoded xchacha nonce
2. `ciphertext`: base64-encoded xchacha ciphertext, created from (key, nonce) against `plaintext`.
Example:
- Alice's private key: `5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a`
- Bob's private key: `4b22aa260e4acb7021e32f38a6cdf4b673c6a277755bfce287e370c924dc936d`
Encrypting the message `hello` from Alice to Bob results in the base-64 encoded tlv payload:
```
AZKyMIHbfVYFlAAK7Ci5wuM5GFOLaeI7LQKDzWJY
```
# Other Notes
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). We are using this exact implementation. In NIP-94, unhashed shared point was used.
This encryption scheme replaces the one described in NIP-04, which is not secure. It used bad cryptographic building blocks and must not be used.
# Code Samples
## Javascript
```javascript
import {xchacha20} from "@noble/ciphers/chacha"
import {secp256k1} from "@noble/curves/secp256k1"
import {sha256} from "@noble/hashes/sha256"
import {randomBytes} from "@noble/hashes/utils"
import {base64} from "@scure/base"
export const utf8Decoder = new TextDecoder()
export const utf8Encoder = new TextEncoder()
export const getSharedSecret = (privkey: string, pubkey: string): Uint8Array =>
sha256(secp256k1.getSharedSecret(privkey, "02" + pubkey).subarray(1, 33))
export function encrypt(privkey: string, pubkey: string, text: string, v = 1) {
if (v !== 1) {
throw new Error('NIP44: unknown encryption version')
}
const key = getSharedSecret(privkey, pubkey)
const nonce = randomBytes(24)
const plaintext = utf8Encoder.encode(text)
const ciphertext = xchacha20(key, nonce, plaintext)
const payload = new Uint8Array(1 + 24 + ciphertext.length)
payload.set([version], 0)
payload.set(nonce, 1)
payload.set(ciphertext, 1 + 24)
return base64.encode(payload)
}
export function decrypt(privkey: string, pubkey: string, blob: string) {
const payload = base64.decode(blob)
if (payload[0] !== 1) {
throw new Error('NIP44: unknown encryption version')
}
const nonce = payload.subarray(1, 25)
const ciphertext = payload.subarray(25)
const key = getSharedSecret(privkey, pubkey)
const plaintext = xchacha20(key, nonce, ciphertext)
return utf8Decoder.decode(plaintext)
}
```
## Kotlin
```kotlin
// implementation 'fr.acinq.secp256k1:secp256k1-kmp-jni-android:0.10.1'
// implementation "com.goterl:lazysodium-android:5.1.0@aar"
// implementation "net.java.dev.jna:jna:5.12.1@aar"
fun getSharedSecretNIP44(privKey: ByteArray, pubKey: ByteArray): ByteArray =
MessageDigest.getInstance("SHA-256").digest(
Secp256k1.get().pubKeyTweakMul(
Hex.decode("02") + pubKey,
privKey
).copyOfRange(1, 33)
)
fun encryptNIP44(msg: String, privKey: ByteArray, pubKey: ByteArray): EncryptedInfo {
val nonce = ByteArray(24).apply {
SecureRandom.getInstanceStrong().nextBytes(this)
}
val cipher = streamXChaCha20Xor(
message = msg.toByteArray(),
nonce = nonce,
key = getSharedSecretNIP44(privKey, pubKey)
)
return EncryptedInfo(
ciphertext = Base64.getEncoder().encodeToString(cipher),
nonce = Base64.getEncoder().encodeToString(nonce),
v = Nip24Version.XChaCha20.code
)
}
fun decryptNIP44(encInfo: EncryptedInfo, privKey: ByteArray, pubKey: ByteArray): String? {
require(encInfo.v == Nip24Version.XChaCha20.code) { "NIP44: unknown encryption version" }
return streamXChaCha20Xor(
message = Base64.getDecoder().decode(encInfo.ciphertext),
nonce = Base64.getDecoder().decode(encInfo.nonce),
key = getSharedSecretNIP44(privKey, pubKey)
)?.decodeToString()
}
// This method is not exposed in AndroidSodium yet, but it will be in the next version.
fun streamXChaCha20Xor(message: ByteArray, nonce: ByteArray, key: ByteArray): ByteArray? {
return with (SodiumAndroid()) {
val resultCipher = ByteArray(message.size)
val isSuccessful = crypto_stream_chacha20_xor_ic(
resultCipher,
message,
message.size.toLong(),
nonce.drop(16).toByteArray(), // chacha nonce is just the last 8 bytes.
0,
ByteArray(32).apply {
crypto_core_hchacha20(this, nonce, key, null)
}
) == 0
if (isSuccessful) resultCipher else null
}
}
data class EncryptedInfo(val ciphertext: String, val nonce: String, val v: Int)
enum class Nip24Version(val code: Int) {
Reserved(0),
XChaCha20(1)
}

10
46.md
View File

@@ -82,12 +82,18 @@ These are mandatory methods the remote signer app MUST implement:
- **get_relays**
- params []
- result `{ [url: string]: {read: boolean, write: boolean} }`
- **nip04_encrypt**
- **nip04_encrypt** (deprecated)
- params [`pubkey`, `plaintext`]
- result `nip4 ciphertext`
- **nip04_decrypt**
- **nip04_decrypt** (deprecated)
- params [`pubkey`, `nip4 ciphertext`]
- result [`plaintext`]
- **nip44_encrypt**
- params [`pubkey`, `plaintext`, `v`]
- result `nip44 encrypted payload`
- **nip44_decrypt**
- params [`pubkey`, `nip44 encrypted payload`]
- result [`plaintext`]
NOTICE: `pubkey` and `signature` are hex-encoded strings.

60
48.md Normal file
View File

@@ -0,0 +1,60 @@
NIP-48
======
Proxy Tags
----------
`draft` `optional` `author:alexgleason`
Nostr events bridged from other protocols such as ActivityPub can link back to the source object by including a `"proxy"` tag, in the form:
```
["proxy", <id>, <protocol>]
```
Where:
- `<id>` is the ID of the source object. The ID format varies depending on the protocol. The ID must be universally unique, regardless of the protocol.
- `<protocol>` is the name of the protocol, e.g. `"activitypub"`.
Clients may use this information to reconcile duplicated content bridged from other protocols, or to display a link to the source object.
Proxy tags may be added to any event kind, and doing so indicates that the event did not originate on the Nostr protocol, and instead originated elsewhere on the web.
### Supported protocols
This list may be extended in the future.
| Protocol | ID format | Example |
| -------- | --------- | ------- |
| `activitypub` | URL | `https://gleasonator.com/objects/9f524868-c1a0-4ee7-ad51-aaa23d68b526` |
| `atproto` | AT URI | `at://did:plc:zhbjlbmir5dganqhueg7y4i3/app.bsky.feed.post/3jt5hlibeol2i` |
| `rss` | URL with guid fragment | `https://soapbox.pub/rss/feed.xml#https%3A%2F%2Fsoapbox.pub%2Fblog%2Fmostr-fediverse-nostr-bridge` |
| `web` | URL | `https://twitter.com/jack/status/20` |
### Examples
ActivityPub object:
```json
{
"kind": 1,
"content": "I'm vegan btw",
"tags": [
[
"proxy",
"https://gleasonator.com/objects/8f6fac53-4f66-4c6e-ac7d-92e5e78c3e79",
"activitypub"
]
],
"pubkey": "79c2cae114ea28a981e7559b4fe7854a473521a8d22a66bbab9fa248eb820ff6",
"created_at": 1691091365,
"id": "55920b758b9c7b17854b6e3d44e6a02a83d1cb49e1227e75a30426dea94d4cb2",
"sig": "a72f12c08f18e85d98fb92ae89e2fe63e48b8864c5e10fbdd5335f3c9f936397a6b0a7350efe251f8168b1601d7012d4a6d0ee6eec958067cf22a14f5a5ea579"
}
```
### See also
- [FEP-fffd: Proxy Objects](https://codeberg.org/fediverse/fep/src/branch/main/fep/fffd/fep-fffd.md)
- [Mostr bridge](https://mostr.pub/)

209
52.md Normal file
View File

@@ -0,0 +1,209 @@
NIP-52
======
Calendar Events
---------------
`draft` `optional` `author:tyiu`
This specification defines calendar events representing an occurrence at a specific moment or between moments. These calendar events are replaceable and referenceable per [NIP-33](33.md) and deletable per [NIP-09](09.md).
Unlike the term `calendar event` specific to this NIP, the term `event` is used broadly in all the NIPs to describe any Nostr event. The distinction is being made here to discern between the two terms.
## Calendar Events
There are two types of calendar events represented by different kinds: date-based and time-based calendar events. Calendar events are not required to be part of a [calendar](#calendar).
### Date-Based Calendar Event
This kind of calendar event starts on a date and ends before a different date in the future. Its use is appropriate for all-day or multi-day events where time and time zone hold no significance. e.g., anniversary, public holidays, vacation days.
#### Format
The format uses a parameterized replaceable event kind `31922`.
The `.content` of these events is optional and should be a detailed description of the calendar event.
The list of tags are as follows:
* `d` (required) universally unique identifier (UUID). Generated by the client creating the calendar event.
* `name` (required) name of the calendar event
* `start` (required) inclusive start date in ISO 8601 format (YYYY-MM-DD). Must be less than `end`, if it exists.
* `end` (optional) exclusive end date in ISO 8601 format (YYYY-MM-DD). If omitted, the calendar event ends on the same date as `start`.
* `location` (optional) location of the calendar event. e.g. address, GPS coordinates, meeting room name, link to video call
* `g` (optional) [geohash](https://en.wikipedia.org/wiki/Geohash) to associate calendar event with a searchable physical location as suggested as an example by [NIP-12](12.md)
* `p` (optional, repeated) 32-bytes hex pubkey of a participant, optional recommended relay URL, and participant's role in the meeting
* `t` (optional, repeated) hashtag to categorize calendar event
* `r` (optional, repeated) references / links to web pages, documents, video calls, recorded videos, etc.
```json
{
"id": <32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>,
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
"created_at": <Unix timestamp in seconds>,
"kind": "31922",
"content": "<description of calendar event>",
"tags": [
["d", "<UUID>"],
["name", "<name of calendar event>"],
// Dates
["start", "<YYYY-MM-DD>"],
["end", "<YYYY-MM-DD>"],
// Location
["location", "<location>"],
["g", "<geohash>"],
// Participants
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>", "<role>"],
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>", "<role>"],
// Hashtags
["t", "<tag>"],
["t", "<tag>"],
// Reference links
["r", "<url>"],
["r", "<url>"]
]
}
```
### Time-Based Calendar Event
This kind of calendar event spans between a start time and end time.
#### Format
The format uses a parameterized replaceable event kind `31923`.
The `.content` of these events is optional and should be a detailed description of the calendar event.
The list of tags are as follows:
* `d` (required) universally unique identifier (UUID). Generated by the client creating the calendar event.
* `name` (required) name of the calendar event
* `start` (required) inclusive start Unix timestamp in seconds. Must be less than `end`, if it exists.
* `end` (optional) exclusive end Unix timestamp in seconds. If omitted, the calendar event ends instantaneously.
* `start_tzid` (optional) time zone of the start timestamp, as defined by the IANA Time Zone Database. e.g., `America/Costa_Rica`
* `end_tzid` (optional) time zone of the end timestamp, as defined by the IANA Time Zone Database. e.g., `America/Costa_Rica`. If omitted and `start_tzid` is provided, the time zone of the end timestamp is the same as the start timestamp.
* `location` (optional) location of the calendar event. e.g. address, GPS coordinates, meeting room name, link to video call
* `g` (optional) [geohash](https://en.wikipedia.org/wiki/Geohash) to associate calendar event with a searchable physical location as suggested as an example by [NIP-12](12.md)
* `p` (optional, repeated) 32-bytes hex pubkey of a participant, optional recommended relay URL, and participant's role in the meeting
* `t` (optional, repeated) hashtag to categorize calendar event
* `r` (optional, repeated) references / links to web pages, documents, video calls, recorded videos, etc.
```json
{
"id": <32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>,
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
"created_at": <Unix timestamp in seconds>,
"kind": "31923",
"content": "<description of calendar event>",
"tags": [
["d", "<UUID>"],
["name", "<name of calendar event>"],
// Timestamps
["start", "<Unix timestamp in seconds>"],
["end", "<Unix timestamp in seconds>"],
["start_tzid", "<IANA Time Zone Database identifier>"],
["end_tzid", "<IANA Time Zone Database identifier>"],
// Location
["location", "<location>"],
["g", "<geohash>"],
// Participants
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>", "<role>"],
["p", "<32-bytes hex of a pubkey>", "<optional recommended relay URL>", "<role>"],
// Hashtags
["t", "<tag>"],
["t", "<tag>"],
// Reference links
["r", "<url>"],
["r", "<url>"]
]
}
```
## Calendar
A calendar is a collection of calendar events, represented as a custom replaceable list event using kind `31924`. A user can have multiple calendars. One may create a calendar to segment calendar events for specific purposes. e.g., personal, work, travel, meetups, and conferences.
### Format
The format uses a custom replaceable list of kind `31924` with a list of tags as described below:
* `d` (required) calendar name
* `a` (repeated) reference tag to kind `31922` or `31923` calendar event being responded to per [NIP-33](33.md)
```json
{
"kind": 31924,
"tags": [
["d", "<calendar name>"],
["a", "<31922 or 31923>:<calendar event author pubkey>:<d-identifier of calendar event>", "<optional relay url>"],
["a", "<31922 or 31923>:<calendar event author pubkey>:<d-identifier of calendar event>", "<optional relay url>"]
]
}
```
## Calendar Event RSVP
A calendar event RSVP is a response to a calendar event to indicate a user's attendance intention.
If a calendar event tags a pubkey, that can be interpreted as the calendar event creator inviting that user to attend. Clients MAY choose to prompt the user to RSVP for the calendar event.
Any user may RSVP, even if they were not tagged on the calendar event. Clients MAY choose to prompt the calendar event creator to invite the user who RSVP'd. Clients also MAY choose to ignore these RSVPs.
This NIP is intentionally not defining who is authorized to attend a calendar event if the user who RSVP'd has not been tagged. It is up to the calendar event creator to determine the semantics.
This NIP is also intentionally not defining what happens if a calendar event changes after an RSVP is submitted.
### Format
The format uses a parameterized replaceable event kind `31925`.
The `.content` of these events is optional and should be a free-form note that adds more context to this calendar event response.
The list of tags are as follows:
* `a` (required) reference tag to kind `31922` or `31923` calendar event being responded to per [NIP-33](33.md)
* `d` (required) universally unique identifier. Generated by the client creating the calendar event RSVP.
* `L` (required) label namespace of `status` per [NIP-32](32.md)
* `l` (required) label of `accepted`, `declined`, or `tentative` under the label namespace of `status` per [NIP-32](32.md). Determines attendance status to the referenced calendar event.
* `L` (optional) label namespace of `freebusy` per [NIP-32](32.md). Exists if and only if corresponding `l` tag under the same label namespace exists.
* `l` (optional) label of `free` or `busy` under the label namespace of `freebusy` per [NIP-32](32.md). Determines if the user would be free or busy for the duration of the calendar event. This tag must be omitted or ignored if the `status` label is set to `declined`. Exists if and only if corresponding `l` tag under the same label namespace exists.
```json
{
"id": <32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>,
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
"created_at": <Unix timestamp in seconds>,
"kind": "31925",
"content": "<note>",
"tags": [
["a", "<31922 or 31923>:<calendar event author pubkey>:<d-identifier of calendar event>", "<optional relay url>"],
["d", "<UUID>"],
["L", "status"],
["l", "<accepted/declined/tentative>", "status"],
["L", "freebusy"],
["l", "<free/busy>", "freebusy"]
]
}
```
## Unsolved Limitations
* No private events
## Intentionally Unsupported Scenarios
### Recurring Calendar Events
Recurring calendar events come with a lot of complexity, making it difficult for software and humans to deal with. This complexity includes time zone differences between invitees, daylight savings, leap years, multiple calendar systems, one-off changes in schedule or other metadata, etc.
This NIP intentionally omits support for recurring calendar events and pushes that complexity up to clients to manually implement if they desire. i.e., individual calendar events with duplicated metadata represent recurring calendar events.

125
53.md Normal file
View File

@@ -0,0 +1,125 @@
NIP-53
======
Live Activities
---------------
`draft` `optional` `author:vitorpamplona` `author:v0l`
## Abstract
Service providers want to offer live activities to the Nostr network in such a way that participants can easily logged and queried by clients. This NIP describes a general framework to advertise the involvement of pubkeys in such live activities.
# Live Event
A special event with `kind:30311` "Live Event" is defined as a [NIP-33: Parameterized Replaceable Events](33.md) of public `p` tags. Each `p` tag SHOULD have a **displayable** marker name for the current role (e.g. `Host`, `Speaker`, `Participant`) of the user in the event and the relay information MAY be empty. This event will be constantly updated as participants join and leave the activity.
For example:
```js
{
"kind": 30311,
"tags": [
["d", "<unique identifier>"],
["title", "<name of the event>"],
["summary", "<description>"],
["image", "<preview image url>"],
["t", "hashtag"]
["streaming", "<url>"],
["recording", "<url>"], // used to place the edited video once the activity is over
["starts", "<unix timestamp in seconds>"],
["ends", "<unix timestamp in seconds>"],
["status", "<planned, live, ended>"],
["current_participants", "<number>"],
["total_participants", "<number>"],
["p", "91cf9..4e5ca", "wss://provider1.com/", "Host", "<proof>"],
["p", "14aeb..8dad4", "wss://provider2.com/nostr", "Speaker"],
["p", "612ae..e610f", "ws://provider3.com/ws", "Participant"],
["relays", "wss://one.com", "wss://two.com", ...]
],
"content": "",
...other fields
}
```
A distinct `d` tag should be used for each activity. All other tags are optional.
Providers SHOULD keep the participant list small (e.g. under 1000 users) and, when limits are reached, Providers SHOULD select which participants get named in the event. Clients should not expect a comprehensive list. Once the activity ends, the event can be deleted or updated to summarize the activity and provide async content (e.g. recording of the event).
Clients are expected to subscribe to `kind:30311` events in general or for given follow lists and statuses. Clients MAY display participants' roles in activities as well as access points to join the activity.
Live Activity management clients are expected to constantly update `kind:30311` during the event. Clients MAY choose to consider `status=live` events after 1hr without any update as `ended`. The `starts` and `ends` timestamp SHOULD be updated when the status changes to and from `live`
The activity MUST be linked to using the NIP-19 naddr code along with the "a" tag (see [NIP-33](33.md) and [NIP-19](19.md)).
## Proof of Agreement to Participate
Event owners can add proof as the 5th term in each `p` tag to clarify the participant's agreement in joining the event. The proof is a signed SHA256 of the complete `a` Tag of the event (`kind:pubkey:dTag`) by each `p`'s private key, encoded in hex.
Clients MAY only display participants if the proof is available or MAY display participants as "invited" if the proof is not available.
This feature is important to avoid malicious event owners adding large account holders to the event, without their knowledge, to lure their followers into the malicious owner's trap.
# Live Chat Message
Event `kind:1311` is live chat's channel message. Clients MUST include the `a` tag of the activity with a `root` marker. Other Kind-1 tags such as `reply` and `mention` can also be used.
```js
{
"id": "<32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>",
"pubkey": "<32-bytes lowercase hex-encoded public key of the event creator>",
"created_at": "<Unix timestamp in seconds>",
"kind": 1311,
"tags": [
["a", "30311:<Community event author pubkey>:<d-identifier of the community>", "<Optional relay url>", "root"],
],
"content": "Zaps to live streams is beautiful."
}
```
# Use Cases
Common use cases include meeting rooms/workshops, watch-together activities, or event spaces, such as [live.snort.social](https://live.snort.social) and [nostrnests.com](https://nostrnests.com).
# Example
Live Streaming
```json
{
"id": "57f28dbc264990e2c61e80a883862f7c114019804208b14da0bff81371e484d2",
"pubkey": "1597246ac22f7d1375041054f2a4986bd971d8d196d7997e48973263ac9879ec",
"created_at": 1687182672,
"kind": 30311,
"tags": [
["d", "demo-cf-stream"],
["title", "Adult Swim Metalocalypse"],
["summary", "Live stream from IPTV-ORG collection"],
["streaming", "https://adultswim-vodlive.cdn.turner.com/live/metalocalypse/stream.m3u8"],
["starts", "1687182672"]
["status", "live"],
["t", "animation"],
["t", "iptv"],
["image", "https://i.imgur.com/CaKq6Mt.png"]
],
"content": "",
"sig": "5bc7a60f5688effa5287244a24768cbe0dcd854436090abc3bef172f7f5db1410af4277508dbafc4f70a754a891c90ce3b966a7bc47e7c1eb71ff57640f3d389"
}
```
Live Streaming chat message
```json
{
"id": "97aa81798ee6c5637f7b21a411f89e10244e195aa91cb341bf49f718e36c8188",
"pubkey": "3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24",
"created_at": 1687286726,
"kind": 1311,
"tags": [
["a", "30311:1597246ac22f7d1375041054f2a4986bd971d8d196d7997e48973263ac9879ec:demo-cf-stream", "", "root"]
],
"content": "Zaps to live streams is beautiful.",
"sig": "997f62ddfc0827c121043074d50cfce7a528e978c575722748629a4137c45b75bdbc84170bedc723ef0a5a4c3daebf1fef2e93f5e2ddb98e5d685d022c30b622"
}
````

13
56.md
View File

@@ -10,7 +10,7 @@ Reporting
A report is a `kind 1984` note that is used to report other notes for spam,
illegal and explicit content.
The content MAY contain additional information submitted by the entity
The `content` MAY contain additional information submitted by the entity
reporting the content.
Tags
@@ -42,9 +42,9 @@ Example events
{
"kind": 1984,
"tags": [
["p", <pubkey>, "nudity"]
["p", <pubkey>, "nudity"],
["L", "social.nos.ontology"],
["l", "NS-nud", "social.nos.ontology"],
["l", "NS-nud", "social.nos.ontology"]
],
"content": "",
...
@@ -63,10 +63,9 @@ Example events
{
"kind": 1984,
"tags": [
["p", <impersonator pubkey>, "impersonation"],
["p", <victim pubkey>]
["p", <impersonator pubkey>, "impersonation"]
],
"content": "Profile is imitating #[1]",
"content": "Profile is impersonating nostr:<victim bech32 pubkey>",
...
}
```
@@ -75,7 +74,7 @@ Client behavior
---------------
Clients can use reports from friends to make moderation decisions if they
choose to. For instance, if 3+ of your friends report a profile as explicit,
choose to. For instance, if 3+ of your friends report a profile for `nudity`,
clients can have an option to automatically blur photos from said account.

14
57.md
View File

@@ -126,7 +126,7 @@ When receiving a payment, the following steps are executed:
The following should be true of the `zap receipt` event:
- The content SHOULD be empty.
- The `content` SHOULD be empty.
- The `created_at` date SHOULD be set to the invoice `paid_at` date for idempotency.
- `tags` MUST include the `p` tag AND optional `e` tag from the `zap request`.
- The `zap receipt` MUST have a `bolt11` tag containing the description hash bolt11 invoice.
@@ -166,18 +166,22 @@ A client can retrieve `zap receipt`s on events and pubkeys using a NIP-01 filter
- The `invoiceAmount` contained in the `bolt11` tag of the `zap receipt` MUST equal the `amount` tag of the `zap request` (if present).
- The `lnurl` tag of the `zap request` (if present) SHOULD equal the recipient's `lnurl`.
### Appendix G: `zap` tag on zapped event
### Appendix G: `zap` tag on other events
When an event includes a `zap` tag, clients SHOULD calculate the lnurl pay request based on its value instead of the profile's field. An optional third argument on the tag specifies the type of value, either `lud06` or `lud16`.
When an event includes one or more `zap` tags, clients wishing to zap it SHOULD calculate the lnurl pay request based on the tags value instead of the event author's profile field. The tag's second argument is the `hex` string of the receiver's pub key and the third argument is the relay to download the receiver's metadata (Kind-0). An optional fourth parameter specifies the weight (a generalization of a percentage) assigned to the respective receiver. Clients should parse all weights, calculate a sum, and then a percentage to each receiver. If weights are not present, CLIENTS should equally divide the zap amount to all receivers. If weights are only partially present, receivers without a weight should not be zapped (`weight = 0`).
```json
```js
{
"tags": [
[ "zap", "pablo@f7z.io", "lud16" ]
[ "zap", "82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2", "wss://nostr.oxtr.dev", "1" ], // 25%
[ "zap", "fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52", "wss://nostr.wine/", "1" ], // 25%
[ "zap", "460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c", "wss://nos.lol/", "2" ] // 50%
]
}
```
Clients MAY display the zap split configuration in the note.
## Future Work
Zaps can be extended to be more private by encrypting `zap request` notes to the target user, but for simplicity it has been left out of this initial draft.

93
65.md
View File

@@ -4,63 +4,13 @@ NIP-65
Relay List Metadata
-------------------
`draft` `optional` `author:mikedilger`
`draft` `optional` `author:mikedilger` `author:vitorpamplona`
A special replaceable event meaning "Relay List Metadata" is defined as an event with kind `10002` having a list of `r` tags, one for each relay the author uses to either read or write to.
Defines a replaceable event using `kind:10002` to advertise preferred relays for discovering a user's content and receiving fresh content from others.
The primary purpose of this relay list is to advertise to others, not for configuring one's client.
The event MUST include a list of `r` tags with relay URIs and a `read` or `write` marker. If the marker is omitted, the relay is used for both purposes.
The content is not used and SHOULD be an empty string.
The `r` tags can have a second parameter as either `read` or `write`. If it is omitted, it means the author uses the relay for both purposes.
Clients SHOULD, as with all replaceable events, use only the most recent kind-10002 event they can find.
### The meaning of read and write
Write relays are for events that are intended for anybody (e.g. your followers). Read relays are for events that address a particular person.
Clients SHOULD write feed-related events created by their user to their user's write relays.
Clients SHOULD read feed-related events created by another from at least some of that other person's write relays. Explicitly, they SHOULD NOT expect them to be available at their user's read relays. It SHOULD NOT be presumed that the user's read relays coincide with the write relays of the people the user follows.
Clients SHOULD read events that tag their user from their user's read relays.
Clients SHOULD write events that tag a person to at least some of that person's read relays. Explicitly, they SHOULD NOT expect that person will pick them up from their user's write relays. It SHOULD NOT be presumed that the user's write relays coincide with the read relays of the person being tagged.
Clients SHOULD presume that if their user has a pubkey in their ContactList (kind 3) that it is because they wish to see that author's feed-related events. But clients MAY presume otherwise.
### Motivation
There is a common nostr use case where users wish to follow the content produced by other users. This is evidenced by the implicit meaning of the Contact List in [NIP-02](02.md)
Because users don't often share the same sets of relays, ad-hoc solutions have arisen to get that content, but these solutions negatively impact scalability and decentralization:
- Most people are sending their posts to the same most popular relays in order to be more widely seen
- Many people are pulling from a large number of relays (including many duplicate events) in order to get more data
- Events are being copied between relays, oftentimes to many different relays
### Purposes
The purpose of this NIP is to help clients find the events of the people they follow, to help tagged events get to the people tagged, and to help nostr scale better.
### Suggestions
It is suggested that people spread their kind `10002` events to many relays, but write their normal feed-related events to a much smaller number of relays (between 2 to 6 relays). It is suggested that clients offer a way for users to spread their kind `10002` events to many more relays than they normally post to.
Authors may post events outside of the feed that they wish their followers to follow by posting them to relays outside of those listed in their "Relay List Metadata". For example, an author may want to reply to someone without all of their followers watching.
It is suggested that relays allow any user to write their own kind `10002` event (optionally with AUTH to verify it is their own) even if they are not otherwise subscribed to the relay because
- finding where someone posts is rather important
- these events do not have content that needs management
- relays only need to store one replaceable event per pubkey to offer this service
### Why not in kind `0` Metadata
Even though this is user related metadata, it is a separate event from kind `0` in order to keep it small (as it should be widely spread) and to not have content that may require moderation by relay operators so that it is more acceptable to relays.
### Example
The `.content` is not used.
```json
{
@@ -74,3 +24,38 @@ Even though this is user related metadata, it is a separate event from kind `0`
"content": "",
...other fields
```
This NIP doesn't fully replace relay lists that are designed to configure a client's usage of relays (such as `kind:3` style relay lists). Clients MAY use other relay lists in situations where a `kind:10002` relay list cannot be found.
## When to Use Read and Write
When seeking events **from** a user, Clients SHOULD use the WRITE relays of the user's `kind:10002`
When seeking events **about** a user, where the user was tagged, Clients SHOULD use the READ relays of the user's `kind:10002`
When broadcasting an event, Clients SHOULD:
- Broadcast the event to the WRITE relays of the author
- Broadcast the event all READ relays of each tagged user.
## Motivation
The old model of using a fixed relay list per user centralizes in large relay operators:
- Most users submit their posts to the same highly popular relays, aiming to achieve greater visibility among a broader audience.
- Many users are pulling events from a large number of relays in order to get more data at the expense of duplication
- Events are being copied between relays, oftentimes to many different relays
This NIP allows Clients to connect directly with the most up-to-date relay set from each individual user, eliminating the need of broadcasting events to popular relays.
## Final Considerations
1. Clients SHOULD guide users to keep `kind:10002` lists small (2-4 relays).
2. Clients SHOULD spread an author's `kind:10002` events to as many relays as viable.
3. `kind:10002` events should primarily be used to advertise the user's preferred relays to others. A user's own client may use other heuristics for selecting relays for fetching data.
4. DMs SHOULD only be broadcasted to the author's WRITE relays and to the receiver's READ relays to keep maximum privacy.
5. If a relay signals support for this NIP in their [NIP-11](11.md) document that means they're willing to accept kind 10002 events from a broad range of users, not only their paying customers or whitelisted group.

101
72.md Normal file
View File

@@ -0,0 +1,101 @@
NIP-72
======
Moderated Communities (Reddit Style)
------------------------------------
`draft` `optional` `author:vitorpamplona` `author:arthurfranca`
The goal of this NIP is to create moderator-approved public communities around a topic. It defines the replaceable event `kind:34550` to define the community and the current list of moderators/administrators. Users that want to post into the community, simply tag any Nostr event with the community's `a` tag (See [NIP-33](33.md)). Moderators issue an approval event `kind:4550` that links the community with the new post.
# Community Definition
`Kind:34550` SHOULD include any field that helps define the community and the set of moderators. `relay` tags MAY be used to describe the preferred relay to download requests and approvals.
```json
{
"id": "<32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>",
"pubkey": "<32-bytes lowercase hex-encoded public key of the event creator>",
"created_at": <Unix timestamp in seconds>,
"kind": 34550,
"tags": [
["d", "<Community name>"],
["description", "<Community description>"],
["image", "<Community image url>", "<Width>x<Height>"],
//.. other tags relevant to defining the community
// moderators
["p", "<32-bytes hex of a pubkey1>", "<optional recommended relay URL>", "moderator"],
["p", "<32-bytes hex of a pubkey2>", "<optional recommended relay URL>", "moderator"],
["p", "<32-bytes hex of a pubkey3>", "<optional recommended relay URL>", "moderator"],
// relays used by the community (w/optional marker)
["relay", "<relay hosting author kind 0>", "author"],
["relay", "<relay where to send and receive requests>", "requests"],
["relay", "<relay where to send and receive approvals>", "approvals"],
["relay", "<relay where to post requests to and fetch approvals from>"]
]
}
```
# New Post Request
Any Nostr event can be a post request. Clients MUST add the community's `a` tag to the new post event in order to be presented for the moderator's approval.
```json
{
"id": "<32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>",
"pubkey": "<32-bytes lowercase hex-encoded public key of the event creator>",
"created_at": <Unix timestamp in seconds>,
"kind": 1,
"tags": [
["a", "34550:<Community event author pubkey>:<d-identifier of the community>", "<Optional relay url>"],
],
"content": "<My content>"
}
```
Community management clients MAY filter all mentions to a given `kind:34550` event and request moderators to approve each submission. Moderators MAY delete his/her approval of a post at any time using event deletions (See [NIP-09](09.md)).
# Post Approval by moderators
The post-approval event MUST include `a` tags of the communities the moderator is posting into (one or more), the `e` tag of the post and `p` tag of the author of the post (for approval notifications). The event SHOULD also include the stringified `post request` event inside the `.content` ([NIP-18-style](18.md)) and a `k` tag with the original post's event kind to allow filtering of approved posts by kind.
```json
{
"id": "<32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>",
"pubkey": "<32-bytes lowercase hex-encoded public key of the event creator>",
"created_at": <Unix timestamp in seconds>,
"kind": 4550,
"tags": [
["a", "34550:<Community event author pubkey>:<d-identifier of the community>", "<Optional relay url>"],
["e", "<Post Request ID>", "<Optional relay url>"],
["p", "<Post Request Author ID>", "<Optional relay url>"],
["k", "<New Post Request kind>"],
],
"content": "<New Post Request JSON>"
}
```
It's recommended that multiple moderators approve posts to avoid deleting them from the community when a moderator is removed from the owner's list. In case the full list of moderators must be rotated, the new moderator set must sign new approvals for posts in the past or the community will restart. The owner can also periodically copy and re-sign of each moderator's approval events to make sure posts don't disappear with moderators.
Post Approvals of replaceable events can be created in three ways: (i) by tagging the replaceable event as an `e` tag if moderators want to approve each individual change to the repleceable event; (ii) by tagging the replaceable event as an `a` tag if the moderator authorizes the replaceable event author to make changes without additional approvals and (iii) by tagging the replaceable event with both its `e` and `a` tag which empowers clients to display the original and updated versions of the event, with appropriate remarks in the UI. Since relays are instructed to delete old versions of a replaceable event, the `.content` of an `e`-approval MUST have the specific version of the event or Clients might not be able to find that version of the content anywhere.
Clients SHOULD evaluate any non-`34550:*` `a` tag as posts to be included in all `34550:*` `a` tags.
# Displaying
Community clients SHOULD display posts that have been approved by at least 1 moderator or by the community owner.
The following filter displays the approved posts.
```js
{
"authors": ["<Author pubkey>", "<Moderator1 pubkey>", "<Moderator2 pubkey>", "<Moderator3 pubkey>", ...],
"kinds": [4550],
"#a": ["34550:<Community event author pubkey>:<d-identifier of the community>"],
}
```
Clients MAY hide approvals by blocked moderators at the user's request.

83
99.md Normal file
View File

@@ -0,0 +1,83 @@
# NIP-99
## Classified Listings
`draft` `optional` `author:erskingardner`
This NIP defines `kind:30402`: a parameterized replaceable event to describe classified listings that list any arbitrary product, service, or other thing for sale or offer and includes enough structured metadata to make them useful.
The category of classifieds includes a very broad range of physical goods, services, work opportunities, rentals, free giveaways, personals, etc. and is distinct from the more strictly structured marketplaces defined in [NIP-15](https://github.com/nostr-protocol/nips/blob/master/15.md) that often sell many units of specific products through very specific channels.
The structure of these events is very similar to [NIP-23](https://github.com/nostr-protocol/nips/blob/master/23.md) long-form content events.
### Draft / Inactive Listings
`kind:30403` has the same structure as `kind:30402` and is used to save draft or inactive classified listings.
### Content
The `.content` field should be a description of what is being offered and by whom. These events should be a string in Markdown syntax.
### Author
The `.pubkey` field of these events are treated as the party creating the listing.
### Metadata
- For "tags"/"hashtags" (i.e. categories or keywords of relevance for the listing) the `"t"` event tag should be used, as per [NIP-12](https://github.com/nostr-protocol/nips/blob/master/12.md).
- For images, whether included in the markdown content or not, clients SHOULD use `image` tags as described in [NIP-58](https://github.com/nostr-protocol/nips/blob/master/58.md). This allows clients to display images in carousel format more easily.
The following tags, used for structured metadata, are standardized and SHOULD be included. Other tags may be added as necessary.
- `"title"`, a title for the listing
- `"summary"`, for short tagline or summary for the listing
- `"published_at"`, for the timestamp (in unix seconds converted to string) of the first time the listing was published.
- `"location"`, for the location.
- `"price"`, for the price of the thing being listed. This is an array in the format `[ "price", "<number>", "<currency>", "<frequency>" ]`.
- `"price"` is the name of the tag
- `"<number>"` is the amount in numeric format (but included in the tag as a string)
- `"<currency>"` is the currency unit in 3-character ISO 4217 format or ISO 4217-like currency code (e.g. `"btc"`, `"eth"`).
- `"<frequency>"` is optional and can be used to describe recurring payments. SHOULD be in noun format (hour, day, week, month, year, etc.)
#### `price` examples
- $50 one-time payment `["price", "50", "USD"]`
- €15 per month `["price", "15", "EUR", "month"]`
- £50,000 per year `["price", "50000", "GBP", "year"]`
Other standard tags that might be useful.
- `"g"`, a geohash for more precise location
## Example Event
```json
{
"kind": 30402,
"created_at": 1675642635,
// Markdown content
"content": "Lorem [ipsum][nostr:nevent1qqst8cujky046negxgwwm5ynqwn53t8aqjr6afd8g59nfqwxpdhylpcpzamhxue69uhhyetvv9ujuetcv9khqmr99e3k7mg8arnc9] dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\nRead more at nostr:naddr1qqzkjurnw4ksz9thwden5te0wfjkccte9ehx7um5wghx7un8qgs2d90kkcq3nk2jry62dyf50k0h36rhpdtd594my40w9pkal876jxgrqsqqqa28pccpzu.",
"tags": [
["d", "lorem-ipsum"],
["title", "Lorem Ipsum"],
["published_at", "1296962229"],
["t", "electronics"],
["image", "https://url.to.img", "256x256"],
["summary", "More lorem ipsum that is a little more than the title"],
["location", "NYC"],
["price", "100", "USD"],
[
"e",
"b3e392b11f5d4f28321cedd09303a748acfd0487aea5a7450b3481c60b6e4f87",
"wss://relay.example.com"
],
[
"a",
"30023:a695f6b60119d9521934a691347d9f78e8770b56da16bb255ee286ddf9fda919:ipsum",
"wss://relay.nostr.org"
]
],
"pubkey": "...",
"id": "..."
}
```

View File

@@ -22,7 +22,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
- [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-04: Encrypted Direct Message](04.md) --- **unrecommended**: deprecated in favor of [NIP-44](44.md)
- [NIP-05: Mapping Nostr keys to DNS-based internet identifiers](05.md)
- [NIP-06: Basic key derivation from mnemonic seed phrase](06.md)
- [NIP-07: `window.nostr` capability for web browsers](07.md)
@@ -32,13 +32,13 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
- [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-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-21: `nostr:` URI scheme](21.md)
- [NIP-22: Event `created_at` Limits](22.md)
- [NIP-23: Long-form Content](23.md)
- [NIP-25: Reactions](25.md)
@@ -56,16 +56,21 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
- [NIP-45: Counting results](45.md)
- [NIP-46: Nostr Connect](46.md)
- [NIP-47: Wallet Connect](47.md)
- [NIP-48: Proxy Tags](48.md)
- [NIP-50: Keywords filter](50.md)
- [NIP-51: Lists](51.md)
- [NIP-52: Calendar Events](52.md)
- [NIP-53: Live Activities](53.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-72: Moderated Communities](72.md)
- [NIP-78: Application-specific data](78.md)
- [NIP-89: Recommended Application Handlers](89.md)
- [NIP-94: File Metadata](94.md)
- [NIP-98: HTTP Auth](98.md)
- [NIP-99: Classified Listings](99.md)
## Event Kinds
@@ -87,8 +92,10 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `43` | Channel Hide Message | [28](28.md) |
| `44` | Channel Mute User | [28](28.md) |
| `1063` | File Metadata | [94](94.md) |
| `1311` | Live Chat Message | [53](53.md) |
| `1984` | Reporting | [56](56.md) |
| `1985` | Label | [32](32.md) |
| `4550` | Community Post Approval | [72](72.md) |
| `9734` | Zap Request | [57](57.md) |
| `9735` | Zap | [57](57.md) |
| `10000` | Mute List | [51](51.md) |
@@ -107,9 +114,19 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `30017` | Create or update a stall | [15](15.md) |
| `30018` | Create or update a product | [15](15.md) |
| `30023` | Long-form Content | [23](23.md) |
| `30024` | Draft Long-form Content | [23](23.md) |
| `30078` | Application-specific Data | [78](78.md) |
| `30311` | Live Event | [53](53.md) |
| `30402` | Classified Listing | [99](99.md) |
| `30403` | Draft Classified Listing | [99](99.md) |
| `31922` | Date-Based Calendar Event | [52](52.md) |
| `31923` | Time-Based Calendar Event | [52](52.md) |
| `31924` | Calendar | [52](52.md) |
| `31925` | Calendar Event RSVP | [52](52.md) |
| `31989` | Handler recommendation | [89](89.md) |
| `31990` | Handler information | [89](89.md) |
| `34550` | Community Definition | [72](72.md) |
### Event Kind Ranges
@@ -155,8 +172,9 @@ When experimenting with kinds, keep in mind the classification introduced by [NI
| `alt` | Alt tag | -- | [31](31.md) |
| `d` | identifier | -- | [33](33.md) |
| `e` | event id (hex) | relay URL, marker | [1](01.md), [10](10.md) |
| `g` | geohash | -- | [12](12.md) |
| `g` | geohash | -- | [12](12.md), [52](52.md) |
| `i` | identity | proof | [39](39.md) |
| `k` | kind number (string) | -- | [18](18.md), [72](72.md) |
| `l` | label, label namespace | annotations | [32](32.md) |
| `L` | label namespace | -- | [32](32.md) |
| `p` | pubkey (hex) | relay URL | [1](01.md) |
@@ -169,13 +187,16 @@ When experimenting with kinds, keep in mind the classification introduced by [NI
| `delegation` | pubkey, conditions, delegation token | -- | [26](26.md) |
| `description` | badge description | -- | [58](58.md) |
| `description` | invoice description | -- | [57](57.md) |
| `emoji` | shortcode | image URL | [30](30.md) |
| `emoji` | shortcode, image URL | -- | [30](30.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) |
| `location` | location string | -- | [52](52.md), [99](99.md) |
| `name` | badge name | -- | [58](58.md) |
| `nonce` | random | -- | [13](13.md) |
| `preimage` | hash of `bolt11` invoice | -- | [57](57.md) |
| `price` | price | currency, frequency | [99](99.md) |
| `proxy` | external ID | protocol | [48](48.md) |
| `published_at` | unix timestamp (string) | -- | [23](23.md) |
| `relay` | relay url | -- | [42](42.md) |
| `relays` | relay list | -- | [57](57.md) |
@@ -193,6 +214,21 @@ When experimenting with kinds, keep in mind the classification introduced by [NI
4. There should be no more than one way of doing the same thing.
5. Other rules will be made up when necessary.
## Mailing Lists
The nostr ecosystem is getting large with many different organizations, relays
and clients. Following the nips repo on github is becoming more difficult and
noisy. To coordinate on protocol development outside of github, there are
mailing lists where you can work on NIPs before submitting them here:
* [w3c nostr community group][w3-nostr] - [public-nostr@w3.org][mailto-w3] - requires signup
* [nostr-protocol google group][nostr-google-group] - [nostr-protocol@googlegroups.com][mailto-google] - no signup required
[w3-nostr]: https://www.w3.org/community/nostr/
[mailto-w3]: mailto:public-nostr@w3.org
[nostr-google-group]: https://groups.google.com/g/nostr-protocol
[mailto-google]: mailto:nostr-protocol@googlegroups.com
## License
All NIPs are public domain.