mirror of
https://github.com/nostr-protocol/nips.git
synced 2025-12-11 09:38:50 +00:00
Compare commits
2 Commits
video-phot
...
bigger-nip
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8782df594 | ||
|
|
4de6a69931 |
4
01.md
4
01.md
@@ -14,7 +14,7 @@ Each user has a keypair. Signatures, public key, and encodings are done accordin
|
|||||||
|
|
||||||
The only object type that exists is the `event`, which has the following format on the wire:
|
The only object type that exists is the `event`, which has the following format on the wire:
|
||||||
|
|
||||||
```yaml
|
```jsonc
|
||||||
{
|
{
|
||||||
"id": <32-bytes lowercase hex-encoded sha256 of the serialized event data>,
|
"id": <32-bytes lowercase hex-encoded sha256 of the serialized event data>,
|
||||||
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
|
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
|
||||||
@@ -120,7 +120,7 @@ Clients can send 3 types of messages, which must be JSON arrays, according to th
|
|||||||
|
|
||||||
`<filtersX>` is a JSON object that determines what events will be sent in that subscription, it can have the following attributes:
|
`<filtersX>` is a JSON object that determines what events will be sent in that subscription, it can have the following attributes:
|
||||||
|
|
||||||
```yaml
|
```json
|
||||||
{
|
{
|
||||||
"ids": <a list of event ids>,
|
"ids": <a list of event ids>,
|
||||||
"authors": <a list of lowercase pubkeys, the pubkey of an event must be one of these>,
|
"authors": <a list of lowercase pubkeys, the pubkey of an event must be one of these>,
|
||||||
|
|||||||
75
17.md
75
17.md
@@ -15,17 +15,17 @@ Kind `14` is a chat message. `p` tags identify one or more receivers of the mess
|
|||||||
```jsonc
|
```jsonc
|
||||||
{
|
{
|
||||||
"id": "<usual hash>",
|
"id": "<usual hash>",
|
||||||
"pubkey": "<sender-pubkey>",
|
"pubkey": "<sender-pubkey>",
|
||||||
"created_at": "<current-time>",
|
"created_at": "<current-time>",
|
||||||
"kind": 14,
|
"kind": 14,
|
||||||
"tags": [
|
"tags": [
|
||||||
["p", "<receiver-1-pubkey>", "<relay-url>"],
|
["p", "<receiver-1-pubkey>", "<relay-url>"],
|
||||||
["p", "<receiver-2-pubkey>", "<relay-url>"],
|
["p", "<receiver-2-pubkey>", "<relay-url>"],
|
||||||
["e", "<kind-14-id>", "<relay-url>"] // if this is a reply
|
["e", "<kind-14-id>", "<relay-url>"] // if this is a reply
|
||||||
["subject", "<conversation-title>"],
|
["subject", "<conversation-title>"],
|
||||||
// rest of tags...
|
// rest of tags...
|
||||||
],
|
],
|
||||||
"content": "<message-in-plain-text>",
|
"content": "<message-in-plain-text>",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -65,22 +65,21 @@ Kind `14`s MUST never be signed. If it is signed, the message might leak to rela
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Kind `15` is used for sending encrypted file event messages:
|
Kind 15 is used for sending encrypted file event messages:
|
||||||
|
|
||||||
- `file-type`: Specifies the MIME type of the attached file (e.g., `image/jpeg`, `audio/mpeg`, etc.) before encryption.
|
- `file-type`: Specifies the MIME type of the attached file (e.g., `image/jpeg`, `audio/mpeg`, etc.).
|
||||||
- `encryption-algorithm`: Indicates the encryption algorithm used for encrypting the file. Supported algorithms: `aes-gcm`.
|
- `encryption-algorithm`: Indicates the encryption algorithm used for encrypting the file. Supported algorithms may include `aes-gcm`, `chacha20-poly1305`,`aes-cbc` etc.
|
||||||
- `decryption-key`: The decryption key that will be used by the recipient to decrypt the file.
|
- `decryption-key`: The decryption key that will be used by the recipient to decrypt the file.
|
||||||
- `decryption-nonce`: The decryption nonce that will be used by the recipient to decrypt the file.
|
- `decryption-nonce`: The decryption nonce that will be used by the recipient to decrypt the file.
|
||||||
- `content`: The URL of the file (`<file-url>`).
|
- `content`: The URL of the file (`<file-url>`).
|
||||||
- `x` containing the SHA-256 hexencoded string of the encrypted file.
|
- `x` containing the SHA-256 hexencoded string of the file.
|
||||||
- `ox` containing the SHA-256 hexencoded string of the file before encryption.
|
- `size` (optional) size of file in bytes
|
||||||
- `size` (optional) size of the encrypted file in bytes
|
- `dim` (optional) size of the file in pixels in the form `<width>x<height>`
|
||||||
- `dim` (optional) size in pixels in the form `<width>x<height>`
|
|
||||||
- `blurhash`(optional) the [blurhash](https://github.com/woltapp/blurhash) to show while the client is loading the file
|
- `blurhash`(optional) the [blurhash](https://github.com/woltapp/blurhash) to show while the client is loading the file
|
||||||
- `thumb` (optional) URL of thumbnail with same aspect ratio (encrypted with the same key, nonce)
|
- `thumb` (optional) URL of thumbnail with same aspect ratio (encrypted with the same key, nonce)
|
||||||
- `fallback` (optional) zero or more fallback file sources in case `url` fails (encrypted with the same key, nonce)
|
- `fallback` (optional) zero or more fallback file sources in case `url` fails
|
||||||
|
|
||||||
Just like kind `14`, kind `15`s MUST never be signed.
|
Just like kind 14, kind `15`s MUST never be signed.
|
||||||
|
|
||||||
## Chat Rooms
|
## Chat Rooms
|
||||||
|
|
||||||
@@ -88,34 +87,34 @@ The set of `pubkey` + `p` tags defines a chat room. If a new `p` tag is added or
|
|||||||
|
|
||||||
Clients SHOULD render messages of the same room in a continuous thread.
|
Clients SHOULD render messages of the same room in a continuous thread.
|
||||||
|
|
||||||
An optional `subject` tag defines the current name/topic of the conversation. Any member can change the topic by simply submitting a new `subject` to an existing `pubkey` + `p` tags room. There is no need to send `subject` in every message. The newest `subject` in the chat room is the subject of the conversation.
|
An optional `subject` tag defines the current name/topic of the conversation. Any member can change the topic by simply submitting a new `subject` to an existing `pubkey` + `p`-tags room. There is no need to send `subject` in every message. The newest `subject` in the thread is the subject of the conversation.
|
||||||
|
|
||||||
## Encrypting
|
## Encrypting
|
||||||
|
|
||||||
Following [NIP-59](59.md), the **unsigned** `kind:14` & `kind:15` chat messages must be sealed (`kind:13`) and then gift-wrapped (`kind:1059`) to each receiver and the sender individually.
|
Following [NIP-59](59.md), the **unsigned** `kind:14` & `kind:15` chat messages must be sealed (`kind:13`) and then gift-wrapped (`kind:1059`) to each receiver and the sender individually.
|
||||||
|
|
||||||
```js
|
```jsonc
|
||||||
{
|
{
|
||||||
"id": "<usual hash>",
|
"id": "<usual hash>",
|
||||||
"pubkey": randomPublicKey,
|
"pubkey": randomPublicKey,
|
||||||
"created_at": randomTimeUpTo2DaysInThePast(),
|
"created_at": randomTimeUpTo2DaysInThePast(),
|
||||||
"kind": 1059, // gift wrap
|
"kind": 1059, // gift wrap
|
||||||
"tags": [
|
"tags": [
|
||||||
["p", receiverPublicKey, "<relay-url>"] // receiver
|
["p", receiverPublicKey, "<relay-url>"] // receiver
|
||||||
],
|
],
|
||||||
"content": nip44Encrypt(
|
"content": nip44Encrypt(
|
||||||
{
|
{
|
||||||
"id": "<usual hash>",
|
"id": "<usual hash>",
|
||||||
"pubkey": senderPublicKey,
|
"pubkey": senderPublicKey,
|
||||||
"created_at": randomTimeUpTo2DaysInThePast(),
|
"created_at": randomTimeUpTo2DaysInThePast(),
|
||||||
"kind": 13, // seal
|
"kind": 13, // seal
|
||||||
"tags": [], // no tags
|
"tags": [], // no tags
|
||||||
"content": nip44Encrypt(unsignedKind14, senderPrivateKey, receiverPublicKey),
|
"content": nip44Encrypt(unsignedKind14, senderPrivateKey, receiverPublicKey),
|
||||||
"sig": "<signed by senderPrivateKey>"
|
"sig": "<signed by senderPrivateKey>"
|
||||||
},
|
},
|
||||||
randomPrivateKey, receiverPublicKey
|
randomPrivateKey, receiverPublicKey
|
||||||
),
|
),
|
||||||
"sig": "<signed by randomPrivateKey>"
|
"sig": "<signed by randomPrivateKey>"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -125,7 +124,7 @@ Clients MUST verify if pubkey of the `kind:13` is the same pubkey on the `kind:1
|
|||||||
|
|
||||||
Clients SHOULD randomize `created_at` in up to two days in the past in both the seal and the gift wrap to make sure grouping by `created_at` doesn't reveal any metadata.
|
Clients SHOULD randomize `created_at` in up to two days in the past in both the seal and the gift wrap to make sure grouping by `created_at` doesn't reveal any metadata.
|
||||||
|
|
||||||
The gift wrap's `p` tag can be the receiver's main pubkey or an alias key created to receive DMs without exposing the receiver's identity.
|
The gift wrap's `p`-tag can be the receiver's main pubkey or an alias key created to receive DMs without exposing the receiver's identity.
|
||||||
|
|
||||||
Clients CAN offer disappearing messages by setting an `expiration` tag in the gift wrap of each receiver or by not generating a gift wrap to the sender's public key
|
Clients CAN offer disappearing messages by setting an `expiration` tag in the gift wrap of each receiver or by not generating a gift wrap to the sender's public key
|
||||||
|
|
||||||
|
|||||||
27
44.md
27
44.md
@@ -84,10 +84,12 @@ NIP-44 version 2 has the following design characteristics:
|
|||||||
- Slice 76-byte HKDF output into: `chacha_key` (bytes 0..32), `chacha_nonce` (bytes 32..44), `hmac_key` (bytes 44..76)
|
- Slice 76-byte HKDF output into: `chacha_key` (bytes 0..32), `chacha_nonce` (bytes 32..44), `hmac_key` (bytes 44..76)
|
||||||
4. Add padding
|
4. Add padding
|
||||||
- Content must be encoded from UTF-8 into byte array
|
- Content must be encoded from UTF-8 into byte array
|
||||||
- Validate plaintext length. Minimum is 1 byte, maximum is 65535 bytes
|
- Validate plaintext length. Minimum is 1 byte, maximum is 4294967296 bytes
|
||||||
- Padding format is: `[plaintext_length: u16][plaintext][zero_bytes]`
|
- Padding format is: `[plaintext_length: u16][plaintext][zero_bytes]`
|
||||||
- Padding algorithm is related to powers-of-two, with min padded msg size of 32 bytes
|
- Padding algorithm is related to powers-of-two, with min padded msg size of 32 bytes
|
||||||
- Plaintext length is encoded in big-endian as first 2 bytes of the padded blob
|
- Plaintext length is encoded in big-endian:
|
||||||
|
- if smaller than 65536, as a u16 in the first 2 bytes of the padded blob;
|
||||||
|
- if greater than 65536, the first 6 bytes of the padded blob, the first 2 being zero and the other 4 being the actual encoded length as u32
|
||||||
5. Encrypt padded content
|
5. Encrypt padded content
|
||||||
- Use ChaCha20, with key and nonce from step 3
|
- Use ChaCha20, with key and nonce from step 3
|
||||||
6. Calculate MAC (message authentication code)
|
6. Calculate MAC (message authentication code)
|
||||||
@@ -124,7 +126,9 @@ validation rules, refer to BIP-340.
|
|||||||
6. Decrypt ciphertext
|
6. Decrypt ciphertext
|
||||||
- Use ChaCha20 with key and nonce from step 3
|
- Use ChaCha20 with key and nonce from step 3
|
||||||
7. Remove padding
|
7. Remove padding
|
||||||
- Read the first two BE bytes of plaintext that correspond to plaintext length
|
- Read the first 2 bytes,
|
||||||
|
- if they're zero, read the next 4 bytes as the u32 big-endian plaintext length;
|
||||||
|
- otherwise interpret those 2 bytes as the u16 plaintext length
|
||||||
- Verify that the length of sliced plaintext matches the value of the two BE bytes
|
- Verify that the length of sliced plaintext matches the value of the two BE bytes
|
||||||
- Verify that calculated padding from step 3 of the [encryption](#Encryption) process matches the actual padding
|
- Verify that calculated padding from step 3 of the [encryption](#Encryption) process matches the actual padding
|
||||||
|
|
||||||
@@ -148,8 +152,6 @@ validation rules, refer to BIP-340.
|
|||||||
- `x[i:j]`, where `x` is a byte array and `i, j <= 0` returns a `(j - i)`-byte array with a copy of the
|
- `x[i:j]`, where `x` is a byte array and `i, j <= 0` returns a `(j - i)`-byte array with a copy of the
|
||||||
`i`-th byte (inclusive) to the `j`-th byte (exclusive) of `x`.
|
`i`-th byte (inclusive) to the `j`-th byte (exclusive) of `x`.
|
||||||
- Constants `c`:
|
- Constants `c`:
|
||||||
- `min_plaintext_size` is 1. 1 byte msg is padded to 32 bytes.
|
|
||||||
- `max_plaintext_size` is 65535 (64kB - 1). It is padded to 65536 bytes.
|
|
||||||
- Functions
|
- Functions
|
||||||
- `base64_encode(string)` and `base64_decode(bytes)` are Base64 ([RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648), with padding)
|
- `base64_encode(string)` and `base64_decode(bytes)` are Base64 ([RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648), with padding)
|
||||||
- `concat` refers to byte array concatenation
|
- `concat` refers to byte array concatenation
|
||||||
@@ -182,8 +184,14 @@ def calc_padded_len(unpadded_len):
|
|||||||
def pad(plaintext):
|
def pad(plaintext):
|
||||||
unpadded = utf8_encode(plaintext)
|
unpadded = utf8_encode(plaintext)
|
||||||
unpadded_len = len(plaintext)
|
unpadded_len = len(plaintext)
|
||||||
if (unpadded_len < c.min_plaintext_size or
|
if (unpadded_len < 1 or
|
||||||
unpadded_len > c.max_plaintext_size): raise Exception('invalid plaintext length')
|
unpadded_len > 4294967295): raise Exception('invalid plaintext length')
|
||||||
|
if unpadded_len > 65536:
|
||||||
|
prefix = concat(
|
||||||
|
[0, 0],
|
||||||
|
write_u32_be(unpadded_len),
|
||||||
|
)
|
||||||
|
else:
|
||||||
prefix = write_u16_be(unpadded_len)
|
prefix = write_u16_be(unpadded_len)
|
||||||
suffix = zeros(calc_padded_len(unpadded_len) - unpadded_len)
|
suffix = zeros(calc_padded_len(unpadded_len) - unpadded_len)
|
||||||
return concat(prefix, unpadded, suffix)
|
return concat(prefix, unpadded, suffix)
|
||||||
@@ -191,7 +199,12 @@ def pad(plaintext):
|
|||||||
# Converts padded bytearray to unpadded plaintext
|
# Converts padded bytearray to unpadded plaintext
|
||||||
def unpad(padded):
|
def unpad(padded):
|
||||||
unpadded_len = read_uint16_be(padded[0:2])
|
unpadded_len = read_uint16_be(padded[0:2])
|
||||||
|
if unpadded_len == 0:
|
||||||
|
unpadded_len = read_uint32_be(padded[2:6])
|
||||||
|
unpadded = padded[6:6+unpadded_len]
|
||||||
|
else:
|
||||||
unpadded = padded[2:2+unpadded_len]
|
unpadded = padded[2:2+unpadded_len]
|
||||||
|
|
||||||
if (unpadded_len == 0 or
|
if (unpadded_len == 0 or
|
||||||
len(unpadded) != unpadded_len or
|
len(unpadded) != unpadded_len or
|
||||||
len(padded) != 2 + calc_padded_len(unpadded_len)): raise Exception('invalid padding')
|
len(padded) != 2 + calc_padded_len(unpadded_len)): raise Exception('invalid padding')
|
||||||
|
|||||||
9
51.md
9
51.md
@@ -22,7 +22,6 @@ For example, _mute list_ can contain the public keys of spammers and bad actors
|
|||||||
|
|
||||||
| name | kind | description | expected tag items |
|
| name | kind | description | expected tag items |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| Follow list | 3 | microblogging basic follow list, see [NIP-02](02.md) | `"p"` (pubkeys -- with optional relay hint and petname) |
|
|
||||||
| Mute list | 10000 | things the user doesn't want to see in their feeds | `"p"` (pubkeys), `"t"` (hashtags), `"word"` (lowercase string), `"e"` (threads) |
|
| Mute list | 10000 | things the user doesn't want to see in their feeds | `"p"` (pubkeys), `"t"` (hashtags), `"word"` (lowercase string), `"e"` (threads) |
|
||||||
| Pinned notes | 10001 | events the user intends to showcase in their profile page | `"e"` (kind:1 notes) |
|
| Pinned notes | 10001 | events the user intends to showcase in their profile page | `"e"` (kind:1 notes) |
|
||||||
| Read/write relays | 10002 | where a user publishes to and where they expect mentions | see [NIP-65](65.md) |
|
| Read/write relays | 10002 | where a user publishes to and where they expect mentions | see [NIP-65](65.md) |
|
||||||
@@ -32,17 +31,11 @@ For example, _mute list_ can contain the public keys of spammers and bad actors
|
|||||||
| Blocked relays | 10006 | relays clients should never connect to | `"relay"` (relay URLs) |
|
| Blocked relays | 10006 | relays clients should never connect to | `"relay"` (relay URLs) |
|
||||||
| Search relays | 10007 | relays clients should use when performing search queries | `"relay"` (relay URLs) |
|
| Search relays | 10007 | relays clients should use when performing search queries | `"relay"` (relay URLs) |
|
||||||
| Simple groups | 10009 | [NIP-29](29.md) groups the user is in | `"group"` ([NIP-29](29.md) group id + relay URL + optional group name), `"r"` for each relay in use |
|
| Simple groups | 10009 | [NIP-29](29.md) groups the user is in | `"group"` ([NIP-29](29.md) group id + relay URL + optional group name), `"r"` for each relay in use |
|
||||||
| Favorite relays | 10012 | user favorite relays and pointers to relay sets | `"relay"` (relay URLs) and `"a"` (kind:30002 relay set) |
|
|
||||||
| Interests | 10015 | topics a user may be interested in and pointers | `"t"` (hashtags) and `"a"` (kind:30015 interest set) |
|
| Interests | 10015 | topics a user may be interested in and pointers | `"t"` (hashtags) and `"a"` (kind:30015 interest set) |
|
||||||
| Media follows | 10020 | multimedia (photos, short video) follow list | `"p"` (pubkeys -- with optional relay hint and petname) |
|
|
||||||
| Emojis | 10030 | user preferred emojis and pointers to emoji sets | `"emoji"` (see [NIP-30](30.md)) and `"a"` (kind:30030 emoji set) |
|
| Emojis | 10030 | user preferred emojis and pointers to emoji sets | `"emoji"` (see [NIP-30](30.md)) and `"a"` (kind:30030 emoji set) |
|
||||||
| DM relays | 10050 | Where to receive [NIP-17](17.md) direct messages | `"relay"` (see [NIP-17](17.md)) |
|
| DM relays | 10050 | Where to receive [NIP-17](17.md) direct messages | `"relay"` (see [NIP-17](17.md)) |
|
||||||
| Good wiki authors | 10101 | [NIP-54](54.md) user recommended wiki authors | `"p"` (pubkeys) |
|
| Good wiki authors | 10101 | [NIP-54](54.md) user recommended wiki authors | `"p"` (pubkeys) |
|
||||||
| Good wiki relays | 10102 | [NIP-54](54.md) relays deemed to only host useful articles | `"relay"` (relay URLs) |
|
| Good wiki relays | 10102 | [NIP-54](54.md) relays deemed to only host useful articles | `"relay"` (relay URLs) |
|
||||||
| Video subscriptions | 10111 | [NIP-71](71.md) video-exclusive follows | `"p"` (pubkeys) |
|
|
||||||
| Good video relays | 10112 | relays deemed to only host good [NIP-71](71.md) videos | `"relay"` (relay URLs) |
|
|
||||||
| Photo subscriptions | 10111 | [NIP-68](71.md) photo-exclusive follows | `"p"` (pubkeys) |
|
|
||||||
| Good photo relays | 10112 | relays deemed to only host good [NIP-68](71.md) photos | `"relay"` (relay URLs) |
|
|
||||||
|
|
||||||
### Sets
|
### Sets
|
||||||
|
|
||||||
@@ -64,8 +57,6 @@ Aside from their main identifier, the `"d"` tag, sets can optionally have a `"ti
|
|||||||
| Emoji sets | 30030 | categorized emoji groups | `"emoji"` (see [NIP-30](30.md)) |
|
| Emoji sets | 30030 | categorized emoji groups | `"emoji"` (see [NIP-30](30.md)) |
|
||||||
| Release artifact sets | 30063 | group of artifacts of a software release | `"e"` (kind:1063 [file metadata](94.md) events), `"a"` (software application event) |
|
| Release artifact sets | 30063 | group of artifacts of a software release | `"e"` (kind:1063 [file metadata](94.md) events), `"a"` (software application event) |
|
||||||
| App curation sets | 30267 | references to multiple software applications | `"a"` (software application event) |
|
| App curation sets | 30267 | references to multiple software applications | `"a"` (software application event) |
|
||||||
| Starter packs | 39089 | a named set of profiles to be shared around with the goal of being followed together | `"p"` (pubkeys) |
|
|
||||||
| Media starter packs | 39092 | same as above, but specific to multimedia (photos, short video) clients | `"p"` (pubkeys) |
|
|
||||||
|
|
||||||
### Deprecated standard lists
|
### Deprecated standard lists
|
||||||
|
|
||||||
|
|||||||
@@ -187,7 +187,6 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
|
|||||||
| `10006` | Blocked relays list | [51](51.md) |
|
| `10006` | Blocked relays list | [51](51.md) |
|
||||||
| `10007` | Search relays list | [51](51.md) |
|
| `10007` | Search relays list | [51](51.md) |
|
||||||
| `10009` | User groups | [51](51.md), [29](29.md) |
|
| `10009` | User groups | [51](51.md), [29](29.md) |
|
||||||
| `10012` | Favorite relays list | [51](51.md) |
|
|
||||||
| `10013` | Private event relay list | [37](37.md) |
|
| `10013` | Private event relay list | [37](37.md) |
|
||||||
| `10015` | Interests list | [51](51.md) |
|
| `10015` | Interests list | [51](51.md) |
|
||||||
| `10019` | Nutzap Mint Recommendation | [61](61.md) |
|
| `10019` | Nutzap Mint Recommendation | [61](61.md) |
|
||||||
|
|||||||
Reference in New Issue
Block a user