Skip to content

Commit df30012

Browse files
NIP-17 (old 24) Sealed Gift-Wrapped Messages for Private DMs and Small Group Chats (#686)
1 parent cab47cf commit df30012

File tree

4 files changed

+162
-5
lines changed

4 files changed

+162
-5
lines changed

04.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
> __Warning__ `unrecommended`: deprecated in favor of [NIP-44](44.md)
1+
> __Warning__ `unrecommended`: deprecated in favor of [NIP-17](17.md)
22
33
NIP-04
44
======

11.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Detailed plain-text information about the relay may be contained in the `descrip
3737

3838
### Pubkey
3939

40-
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.
40+
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-17](17.md)) to a system administrator. Expected uses of this address are to report abuse or illegal content, file bug reports, or request other technical assistance.
4141

4242
Relay operators have no obligation to respond to direct messages.
4343

17.md

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
NIP-17
2+
======
3+
4+
Private Direct Messages
5+
-----------------------
6+
7+
`draft` `optional`
8+
9+
This NIP defines an encrypted direct messaging scheme using [NIP-44](44.md) encryption and [NIP-59](59.md) seals and gift wraps.
10+
11+
## Direct Message Kind
12+
13+
Kind `14` is a chat message. `p` tags identify one or more receivers of the message.
14+
15+
```js
16+
{
17+
"id": "<usual hash>",
18+
  "pubkey": "<sender-pubkey>",
19+
"created_at": now(),
20+
  "kind": 14,
21+
  "tags": [
22+
    ["p", "<receiver-1-pubkey>", "<relay-url>"],
23+
    ["p", "<receiver-2-pubkey>", "<relay-url>"],
24+
    ["e", "<kind-14-id>", "<relay-url>", "reply"] // if this is a reply
25+
["subject", "<conversation-title>"],
26+
    ...
27+
  ],
28+
  "content": "<message-in-plain-text>",
29+
}
30+
```
31+
32+
`.content` MUST be plain text. Fields `id` and `created_at` are required.
33+
34+
Tags that mention, quote and assemble threading structures MUST follow [NIP-10](10.md).
35+
36+
Kind `14`s MUST never be signed. If it is signed, the message might leak to relays and become **fully public**.
37+
38+
## Chat Rooms
39+
40+
The set of `pubkey` + `p` tags defines a chat room. If a new `p` tag is added or a current one is removed, a new room is created with clean message history.
41+
42+
Clients SHOULD render messages of the same room in a continuous thread.
43+
44+
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.
45+
46+
## Encrypting
47+
48+
Following [NIP-59](59.md), the **unsigned** `kind:14` chat message must be sealed (`kind:13`) and then gift-wrapped (`kind:1059`) to each receiver and the sender individually.
49+
50+
```js
51+
{
52+
"id": "<usual hash>",
53+
  "pubkey": randomPublicKey,
54+
  "created_at": randomTimeUpTo2DaysInThePast(),
55+
"kind": 1059, // gift wrap
56+
  "tags": [
57+
    ["p", receiverPublicKey, "<relay-url>"] // receiver
58+
  ],
59+
  "content": nip44Encrypt(
60+
    {
61+
"id": "<usual hash>",
62+
      "pubkey": senderPublicKey,
63+
      "created_at": randomTimeUpTo2DaysInThePast(),
64+
      "kind": 13, // seal
65+
      "tags": [], // no tags
66+
      "content": nip44Encrypt(unsignedKind14, senderPrivateKey, receiverPublicKey),
67+
      "sig": "<signed by senderPrivateKey>"
68+
    },
69+
    randomPrivateKey, receiverPublicKey
70+
  ),
71+
  "sig": "<signed by randomPrivateKey>"
72+
}
73+
```
74+
75+
The encryption algorithm MUST use the latest version of [NIP-44](44.md).
76+
77+
Clients MUST verify if pubkey of the `kind:13` is the same pubkey on the `kind:14`, otherwise any sender can impersonate others by simply changing the pubkey on `kind:14`.
78+
79+
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.
80+
81+
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.
82+
83+
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
84+
85+
## Publishing
86+
87+
Kind `10050` indicates the user's preferred relays to receive DMs. The event MUST include a list of `relay` tags with relay URIs.
88+
89+
```js
90+
{
91+
"kind": 10050,
92+
"tags": [
93+
["relay", "wss://inbox.nostr.wine"],
94+
["relay", "wss://myrelay.nostr1.com"],
95+
],
96+
"content": "",
97+
//...other fields
98+
}
99+
```
100+
101+
Clients SHOULD publish kind `14` events to the `10050`-listed relays, falling back to `read` relays of [NIP-65](65.md) if `kind:10050` is not available.
102+
103+
Clients SHOULD guide users to keep `kind:10050` lists small (1-3 relays) and SHOULD spread it to as many relays as viable.
104+
105+
## Benefits & Limitations
106+
107+
This NIP offers the following privacy and security features:
108+
109+
1. **No Metadata Leak**: Participant identities, each message's real date and time, event kinds, and other event tags are all hidden from the public. Senders and receivers cannot be linked with public information alone.
110+
2. **No Public Group Identifiers**: There is no public central queue, channel or otherwise converging identifier to correlate or count all messages in the same group.
111+
3. **No Moderation**: There are no group admins: no invitations or bans.
112+
4. **No Shared Secrets**: No secret must be known to all members that can leak or be mistakenly shared
113+
5. **Fully Recoverable**: Messages can be fully recoverable by any client with the user's private key
114+
6. **Optional Forward Secrecy**: Users and clients can opt-in for "disappearing messages".
115+
7. **Uses Public Relays**: Messages can flow through public relays without loss of privacy. Private relays can increase privacy further, but they are not required.
116+
8. **Cold Storage**: Users can unilaterally opt-in to sharing their messages with a separate key that is exclusive for DM backup and recovery.
117+
118+
The main limitation of this approach is having to send a separate encrypted event to each receiver. Group chats with more than 100 participants should find a more suitable messaging scheme.
119+
120+
----
121+
122+
## Examples
123+
124+
This example sends the message `Hola, que tal?` from `nsec1w8udu59ydjvedgs3yv5qccshcj8k05fh3l60k9x57asjrqdpa00qkmr89m` to `nsec12ywtkplvyq5t6twdqwwygavp5lm4fhuang89c943nf2z92eez43szvn4dt`.
125+
126+
The two final GiftWraps, one to the receiver and the other to the sender, are:
127+
128+
```json
129+
{
130+
"id":"2886780f7349afc1344047524540ee716f7bdc1b64191699855662330bf235d8",
131+
"pubkey":"8f8a7ec43b77d25799281207e1a47f7a654755055788f7482653f9c9661c6d51",
132+
"created_at":1703128320,
133+
"kind":1059,
134+
"tags":[
135+
[ "p", "918e2da906df4ccd12c8ac672d8335add131a4cf9d27ce42b3bb3625755f0788"]
136+
],
137+
"content":"AsqzdlMsG304G8h08bE67dhAR1gFTzTckUUyuvndZ8LrGCvwI4pgC3d6hyAK0Wo9gtkLqSr2rT2RyHlE5wRqbCOlQ8WvJEKwqwIJwT5PO3l2RxvGCHDbd1b1o40ZgIVwwLCfOWJ86I5upXe8K5AgpxYTOM1BD+SbgI5jOMA8tgpRoitJedVSvBZsmwAxXM7o7sbOON4MXHzOqOZpALpS2zgBDXSAaYAsTdEM4qqFeik+zTk3+L6NYuftGidqVluicwSGS2viYWr5OiJ1zrj1ERhYSGLpQnPKrqDaDi7R1KrHGFGyLgkJveY/45y0rv9aVIw9IWF11u53cf2CP7akACel2WvZdl1htEwFu/v9cFXD06fNVZjfx3OssKM/uHPE9XvZttQboAvP5UoK6lv9o3d+0GM4/3zP+yO3C0NExz1ZgFmbGFz703YJzM+zpKCOXaZyzPjADXp8qBBeVc5lmJqiCL4solZpxA1865yPigPAZcc9acSUlg23J1dptFK4n3Tl5HfSHP+oZ/QS/SHWbVFCtq7ZMQSRxLgEitfglTNz9P1CnpMwmW/Y4Gm5zdkv0JrdUVrn2UO9ARdHlPsW5ARgDmzaxnJypkfoHXNfxGGXWRk0sKLbz/ipnaQP/eFJv/ibNuSfqL6E4BnN/tHJSHYEaTQ/PdrA2i9laG3vJti3kAl5Ih87ct0w/tzYfp4SRPhEF1zzue9G/16eJEMzwmhQ5Ec7jJVcVGa4RltqnuF8unUu3iSRTQ+/MNNUkK6Mk+YuaJJs6Fjw6tRHuWi57SdKKv7GGkr0zlBUU2Dyo1MwpAqzsCcCTeQSv+8qt4wLf4uhU9Br7F/L0ZY9bFgh6iLDCdB+4iABXyZwT7Ufn762195hrSHcU4Okt0Zns9EeiBOFxnmpXEslYkYBpXw70GmymQfJlFOfoEp93QKCMS2DAEVeI51dJV1e+6t3pCSsQN69Vg6jUCsm1TMxSs2VX4BRbq562+VffchvW2BB4gMjsvHVUSRl8i5/ZSDlfzSPXcSGALLHBRzy+gn0oXXJ/447VHYZJDL3Ig8+QW5oFMgnWYhuwI5QSLEyflUrfSz+Pdwn/5eyjybXKJftePBD9Q+8NQ8zulU5sqvsMeIx/bBUx0fmOXsS3vjqCXW5IjkmSUV7q54GewZqTQBlcx+90xh/LSUxXex7UwZwRnifvyCbZ+zwNTHNb12chYeNjMV7kAIr3cGQv8vlOMM8ajyaZ5KVy7HpSXQjz4PGT2/nXbL5jKt8Lx0erGXsSsazkdoYDG3U",
138+
"sig":"a3c6ce632b145c0869423c1afaff4a6d764a9b64dedaf15f170b944ead67227518a72e455567ca1c2a0d187832cecbde7ed478395ec4c95dd3e71749ed66c480"
139+
}
140+
```
141+
142+
```json
143+
{
144+
"id":"162b0611a1911cfcb30f8a5502792b346e535a45658b3a31ae5c178465509721",
145+
"pubkey":"626be2af274b29ea4816ad672ee452b7cf96bbb4836815a55699ae402183f512",
146+
"created_at":1702711587,
147+
"kind":1059,
148+
"tags":[
149+
[ "p", "44900586091b284416a0c001f677f9c49f7639a55c3f1e2ec130a8e1a7998e1b"]
150+
],
151+
"content":"AsTClTzr0gzXXji7uye5UB6LYrx3HDjWGdkNaBS6BAX9CpHa+Vvtt5oI2xJrmWLen+Fo2NBOFazvl285Gb3HSM82gVycrzx1HUAaQDUG6HI7XBEGqBhQMUNwNMiN2dnilBMFC3Yc8ehCJT/gkbiNKOpwd2rFibMFRMDKai2mq2lBtPJF18oszKOjA+XlOJV8JRbmcAanTbEK5nA/GnG3eGUiUzhiYBoHomj3vztYYxc0QYHOx0WxiHY8dsC6jPsXC7f6k4P+Hv5ZiyTfzvjkSJOckel1lZuE5SfeZ0nduqTlxREGeBJ8amOykgEIKdH2VZBZB+qtOMc7ez9dz4wffGwBDA7912NFS2dPBr6txHNxBUkDZKFbuD5wijvonZDvfWq43tZspO4NutSokZB99uEiRH8NAUdGTiNb25m9JcDhVfdmABqTg5fIwwTwlem5aXIy8b66lmqqz2LBzJtnJDu36bDwkILph3kmvaKPD8qJXmPQ4yGpxIbYSTCohgt2/I0TKJNmqNvSN+IVoUuC7ZOfUV9lOV8Ri0AMfSr2YsdZ9ofV5o82ClZWlWiSWZwy6ypa7CuT1PEGHzywB4CZ5ucpO60Z7hnBQxHLiAQIO/QhiBp1rmrdQZFN6PUEjFDloykoeHe345Yqy9Ke95HIKUCS9yJurD+nZjjgOxZjoFCsB1hQAwINTIS3FbYOibZnQwv8PXvcSOqVZxC9U0+WuagK7IwxzhGZY3vLRrX01oujiRrevB4xbW7Oxi/Agp7CQGlJXCgmRE8Rhm+Vj2s+wc/4VLNZRHDcwtfejogjrjdi8p6nfUyqoQRRPARzRGUnnCbh+LqhigT6gQf3sVilnydMRScEc0/YYNLWnaw9nbyBa7wFBAiGbJwO40k39wj+xT6HTSbSUgFZzopxroO3f/o4+ubx2+IL3fkev22mEN38+dFmYF3zE+hpE7jVxrJpC3EP9PLoFgFPKCuctMnjXmeHoiGs756N5r1Mm1ffZu4H19MSuALJlxQR7VXE/LzxRXDuaB2u9days/6muP6gbGX1ASxbJd/ou8+viHmSC/ioHzNjItVCPaJjDyc6bv+gs1NPCt0qZ69G+JmgHW/PsMMeL4n5bh74g0fJSHqiI9ewEmOG/8bedSREv2XXtKV39STxPweceIOh0k23s3N6+wvuSUAJE7u1LkDo14cobtZ/MCw/QhimYPd1u5HnEJvRhPxz0nVPz0QqL/YQeOkAYk7uzgeb2yPzJ6DBtnTnGDkglekhVzQBFRJdk740LEj6swkJ",
152+
"sig":"c94e74533b482aa8eeeb54ae72a5303e0b21f62909ca43c8ef06b0357412d6f8a92f96e1a205102753777fd25321a58fba3fb384eee114bd53ce6c06a1c22bab"
153+
}
154+
```

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
2525
- [NIP-01: Basic protocol flow description](01.md)
2626
- [NIP-02: Follow List](02.md)
2727
- [NIP-03: OpenTimestamps Attestations for Events](03.md)
28-
- [NIP-04: Encrypted Direct Message](04.md) --- **unrecommended**: deprecated in favor of [NIP-44](44.md)
28+
- [NIP-04: Encrypted Direct Message](04.md) --- **unrecommended**: deprecated in favor of [NIP-17](17.md)
2929
- [NIP-05: Mapping Nostr keys to DNS-based internet identifiers](05.md)
3030
- [NIP-06: Basic key derivation from mnemonic seed phrase](06.md)
3131
- [NIP-07: `window.nostr` capability for web browsers](07.md)
@@ -36,6 +36,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
3636
- [NIP-13: Proof of Work](13.md)
3737
- [NIP-14: Subject tag in text events](14.md)
3838
- [NIP-15: Nostr Marketplace (for resilient marketplaces)](15.md)
39+
- [NIP-17: Private Direct Messages](17.md)
3940
- [NIP-18: Reposts](18.md)
4041
- [NIP-19: bech32-encoded entities](19.md)
4142
- [NIP-21: `nostr:` URI scheme](21.md)
@@ -99,6 +100,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
99100
| `11` | Group Thread | [29](29.md) |
100101
| `12` | Group Thread Reply | [29](29.md) |
101102
| `13` | Seal | [59](59.md) |
103+
| `14` | Direct Message | [17](17.md) |
102104
| `16` | Generic Repost | [18](18.md) |
103105
| `40` | Channel Creation | [28](28.md) |
104106
| `41` | Channel Metadata | [28](28.md) |
@@ -138,6 +140,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
138140
| `10009` | User groups | [51](51.md), [29](29.md) |
139141
| `10015` | Interests list | [51](51.md) |
140142
| `10030` | User emoji list | [51](51.md) |
143+
| `10050` | Relay list to receive DMs | [17](17.md) |
141144
| `10096` | File storage server list | [96](96.md) |
142145
| `13194` | Wallet Info | [47](47.md) |
143146
| `21000` | Lightning Pub RPC | [Lightning.Pub][lnpub] |
@@ -247,10 +250,10 @@ Please update these lists when proposing NIPs introducing new event kinds.
247250
| `price` | price | currency, frequency | [99](99.md) |
248251
| `proxy` | external ID | protocol | [48](48.md) |
249252
| `published_at` | unix timestamp (string) | -- | [23](23.md) |
250-
| `relay` | relay url | -- | [42](42.md) |
253+
| `relay` | relay url | -- | [42](42.md), [17](17.md) |
251254
| `relays` | relay list | -- | [57](57.md) |
252255
| `server` | file storage server url | -- | [96](96.md) |
253-
| `subject` | subject | -- | [14](14.md) |
256+
| `subject` | subject | -- | [14](14.md), [17](17.md) |
254257
| `summary` | article summary | -- | [23](23.md) |
255258
| `thumb` | badge thumbnail | dimensions in pixels | [58](58.md) |
256259
| `title` | article title | -- | [23](23.md) |

0 commit comments

Comments
 (0)