clone

every nostr nip as a chapter of an illustrated, early-2000s printed game manual

main
nip.guide / site / nips / 17.html
17.html 32.4K
1<!DOCTYPE html>2<html lang="en">3<head>4<meta charset="utf-8">5<meta name="viewport" content="width=device-width, initial-scale=1">6<title>NIP-17 - Private Direct Messages — Nostr Instruction Manual</title>7<meta name="description" content="NIP-17: private direct messages. Kind 14 chat and kind 15 file rumors, sealed and gift-wrapped with NIP-44 and NIP-59, routed through kind 10050 DM relay lists. Complete manual page.">8<link rel="preload" href="../assets/fonts/michroma-400-latin.woff2" as="font" type="font/woff2" crossorigin>9<link rel="preload" href="../assets/fonts/exo2-var-latin.woff2" as="font" type="font/woff2" crossorigin>10<link rel="preload" href="../assets/fonts/sairacondensed-600-latin.woff2" as="font" type="font/woff2" crossorigin>11<link rel="preload" href="../assets/fonts/sairacondensed-700-latin.woff2" as="font" type="font/woff2" crossorigin>12<link rel="preload" href="../assets/fonts/sairacondensed-500-latin.woff2" as="font" type="font/woff2" crossorigin>13<link rel="preload" href="../assets/fonts/sharetechmono-400-latin.woff2" as="font" type="font/woff2" crossorigin>14<link rel="preload" href="../assets/fonts/exo2-400i-latin.woff2" as="font" type="font/woff2" crossorigin>15<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml">16<link rel="stylesheet" href="../assets/manual.css">17</head>18<body>1920<svg width="0" height="0" style="position:absolute">21 <defs>22 <marker id="arr" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">23 <path d="M1,1 L8,4.5 L1,8 Z" fill="#f26a00"/>24 </marker>25 <marker id="arrb" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">26 <path d="M1,1 L8,4.5 L1,8 Z" fill="#2e5f9e"/>27 </marker>28 </defs>29</svg>3031<div class="page">32 <span class="staple s1"></span><span class="staple s2"></span>3334 <header class="band">35 <a class="logo" href="../index.html">NOSTR<b>▸</b>PROTOCOL MANUAL</a>36 <span class="booklet-note">Chapter 17 - Encryption</span>37 </header>38 <nav class="tabs">39 <a href="../index.html">Contents</a>40 <a href="#overview" class="on">Overview</a>41 <a href="#spec">Spec</a>42 <a href="#examples">Examples</a>43 <a href="#history">History</a>44 </nav>4546 <div class="chapter">47 <div class="nipno" data-n="17">17</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 17</div>50 <h1>Private Direct Messages</h1>51 <p class="sub">A chat scheme that hides message content, timing, and who is talking to whom, built from NIP-44 encryption and NIP-59 gift wrapping.</p>52 </div>53 </div>5455 <main class="spread">56 <div class="main">5758 <h2 class="bar" id="overview"><span class="num">1</span> Overview</h2>59 <p>NIP-17 defines private direct messages. It hides the content of a message. It also hides who sent it, who received it, and when. NIP-17 does not invent new cryptography. It combines two existing NIPs: <a href="44.html">NIP-44</a> for encryption and <a href="59.html">NIP-59</a> for gift wrapping.</p>60 <p>A conversation happens inside a <strong>chat room</strong>. By convention, three kinds of content flow through a chat room: <code>kind 14</code> chat messages, <code>kind 15</code> file messages, and <code>kind 7</code> reactions, defined in <a href="25.html">NIP-25</a>. This page calls all three <strong>message rumors</strong>.</p>61 <p>Every message rumor is unsigned. A client seals it, then gift-wraps it, before it ever reaches a relay. Section 3 covers this chain in full. A separate event kind, <code>10050</code>, tells other clients which relays to use for delivery.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>NIP-17 replaces <a href="04.html">NIP-04</a> encrypted direct messages, which is now deprecated. NIP-04 leaked the sender, the receiver, and the timestamp of every message in plain sight. NIP-17 hides all three.</p>66 </div>6768 <h2 class="bar" id="rooms"><span class="num">2</span> Chat Rooms</h2>69 <p>The most common chat room has two members. NIP-17 also supports rooms with more members, though Section 8 recommends a different scheme past ten participants.</p>70 <p>The sender's <code>pubkey</code>, plus every pubkey in a <code>p</code> tag, defines the room. Add one <code>p</code> tag, or remove one, and the result is a new room. The new room starts with a clean, empty history.</p>71 <p>An optional <code>subject</code> tag names the room. Any member can rename the room by sending a new message with a new <code>subject</code> tag. Clients use the newest <code>subject</code> tag as the current name. A client does not need to repeat the <code>subject</code> tag on every message.</p>7273 <h2 class="bar" id="spec"><span class="num">3</span> The Encryption Chain</h2>74 <p>NIP-17 wraps a message rumor three layers deep. Each layer hides information that the layer around it does not need to know.</p>75 <ul>76 <li><strong>Rumor</strong> — the message itself: a <code>kind 14</code>, <code>kind 15</code>, or <code>kind 7</code> event, built and hashed like any other event, but never signed.</li>77 <li><strong>Seal</strong> — a <code>kind 13</code> event. Its <code>content</code> holds the NIP-44-encrypted rumor. Its <code>tags</code> array is always empty. The sender signs the seal with their real key, so the seal proves who wrote the rumor, but nobody can read the rumor without a private key.</li>78 <li><strong>Gift wrap</strong> — a <code>kind 1059</code> event. Its <code>content</code> holds the NIP-44-encrypted seal. It carries one <code>p</code> tag, naming the receiver. A brand-new, random, one-time keypair signs the gift wrap, so the gift wrap does not reveal the real sender.</li>79 </ul>8081 <figure class="diagram">82 <div class="frame">83 <svg viewBox="0 0 640 210" role="img" aria-label="Pipeline: rumor is encrypted into a seal, the seal is encrypted into a gift wrap, and the gift wrap is published to a relay">84 <rect class="d-actor" x="8" y="70" width="118" height="58" rx="8"/>85 <text class="d-actor-label" x="67" y="94" text-anchor="middle">Rumor</text>86 <text class="d-note" x="67" y="112" text-anchor="middle">kind 14 - unsigned</text>87 <rect class="d-actor" x="185" y="70" width="118" height="58" rx="8"/>88 <text class="d-actor-label" x="244" y="94" text-anchor="middle">Seal</text>89 <text class="d-note" x="244" y="112" text-anchor="middle">kind 13 - real sig</text>90 <rect class="d-actor" x="362" y="70" width="140" height="58" rx="8"/>91 <text class="d-actor-label" x="432" y="94" text-anchor="middle">Gift Wrap</text>92 <text class="d-note" x="432" y="112" text-anchor="middle">kind 1059 - random sig</text>93 <rect class="d-actor" x="558" y="70" width="74" height="58" rx="8"/>94 <text class="d-actor-label" x="595" y="94" text-anchor="middle">Relay</text>95 <text class="d-note" x="595" y="112" text-anchor="middle">stores it</text>96 <path class="d-msg d-flow" d="M126,99 L183,99"/>97 <path class="d-msg d-flow" d="M303,99 L360,99"/>98 <path class="d-msg blue d-flow" d="M502,99 L556,99"/>99 <text class="d-label" x="154" y="55" text-anchor="middle">nip44.encrypt()</text>100 <text class="d-note" x="154" y="140" text-anchor="middle">sender key + receiver key</text>101 <text class="d-label" x="331" y="55" text-anchor="middle">nip44.encrypt()</text>102 <text class="d-note" x="331" y="140" text-anchor="middle">random key + receiver key</text>103 <text class="d-label" x="529" y="55" text-anchor="middle">EVENT</text>104 <circle r="4" fill="#f26a00">105 <animateMotion dur="2.4s" repeatCount="indefinite" path="M126,99 L183,99"/>106 </circle>107 <circle r="4" fill="#f26a00">108 <animateMotion dur="2.4s" begin="0.8s" repeatCount="indefinite" path="M303,99 L360,99"/>109 </circle>110 <circle r="4" fill="#2e5f9e">111 <animateMotion dur="2.4s" begin="1.6s" repeatCount="indefinite" path="M502,99 L556,99"/>112 </circle>113 </svg>114 </div>115 <figcaption>Fig. 17-1 — <b>The encryption chain.</b> Rumor becomes seal becomes gift wrap. Only the last layer ever touches a relay.</figcaption>116 </figure>117118 <div class="crt">119 <div class="titlebar">Wire format - unsigned rumor (kind 14 chat message)</div>120<pre>{121 <span class="k">"id"</span>: <span class="c">&lt;usual sha256 hash, per NIP-01&gt;</span>122 <span class="k">"pubkey"</span>: <span class="c">&lt;sender pubkey&gt;</span>123 <span class="k">"created_at"</span>: <span class="c">&lt;current time&gt;</span>124 <span class="k">"kind"</span>: 14125 <span class="k">"tags"</span>: [126 [<span class="s">"p"</span>, <span class="c">&lt;receiver-1 pubkey&gt;</span>, <span class="c">&lt;relay-url&gt;</span>],127 [<span class="s">"p"</span>, <span class="c">&lt;receiver-2 pubkey&gt;</span>, <span class="c">&lt;relay-url&gt;</span>],128 [<span class="s">"subject"</span>, <span class="c">&lt;conversation title&gt;</span>]129 ],130 <span class="k">"content"</span>: <span class="c">&lt;message, plain text&gt;</span>131 <span class="c">-- no "sig" field. This object is never published as-is. --</span>132}</pre>133 </div>134135 <div class="crt">136 <div class="titlebar">Wire format - the full wrap chain</div>137<pre>{138 <span class="k">"id"</span>: <span class="c">&lt;usual hash&gt;</span>,139 <span class="k">"pubkey"</span>: wrapperPublicKey, <span class="c">-- random, one-time key</span>140 <span class="k">"created_at"</span>: randomTimeUpTo2DaysInThePast,141 <span class="k">"kind"</span>: 1059, <span class="c">-- gift wrap</span>142 <span class="k">"tags"</span>: [143 [<span class="s">"p"</span>, receiverPublicKey, <span class="c">&lt;relay-url&gt;</span>] <span class="c">-- receiver</span>144 ],145 <span class="k">"content"</span>: nip44.encrypt(146 {147 <span class="k">"id"</span>: <span class="c">&lt;usual hash&gt;</span>,148 <span class="k">"pubkey"</span>: senderPublicKey, <span class="c">-- real sender</span>149 <span class="k">"created_at"</span>: randomTimeUpTo2DaysInThePast,150 <span class="k">"kind"</span>: 13, <span class="c">-- seal</span>151 <span class="k">"tags"</span>: [], <span class="c">-- always empty</span>152 <span class="k">"content"</span>: nip44.encrypt(153 unsignedMessageRumor, <span class="c">-- kind 14, 15, or 7</span>154 nip44.compute_conversation_key(senderPrivateKey, receiverPublicKey)155 ),156 <span class="k">"sig"</span>: <span class="c">&lt;signed by senderPrivateKey&gt;</span>157 },158 nip44.compute_conversation_key(wrapperPrivateKey, receiverPublicKey)159 ),160 <span class="k">"sig"</span>: <span class="c">&lt;signed by wrapperPrivateKey&gt;</span>161}</pre>162 </div>163164 <div class="box warn">165 <span class="tag">Warning!</span>166 <p>Clients MUST verify that the <code>pubkey</code> on the <code>kind:13</code> seal matches the <code>pubkey</code> inside the sealed rumor. Without this check, any sender can impersonate any other sender by editing the rumor's <code>pubkey</code> field before sealing it.</p>167 </div>168169 <p><code>wrapperPrivateKey</code> is a new keypair. A client generates one fresh, random keypair for every single message it wraps. A sender creates one gift wrap per receiver. It usually also creates one more gift wrap addressed to itself, so its own client can recover the conversation later.</p>170 <p>Clients SHOULD randomize <code>created_at</code> by up to two days in the past, in both the seal and the gift wrap. Real message time stays hidden in the rumor. This stops a relay from grouping messages by timestamp and inferring who is talking to whom.</p>171172 <h2 class="bar" id="kinds"><span class="num">4</span> Message Kinds: Chat and File</h2>173 <p><strong>Kind 14 — chat message.</strong> Its <code>content</code> MUST be plain text: no markup, no encoding. The <code>id</code> and <code>created_at</code> fields are required, computed the normal way even though the event stays unsigned.</p>174 <div class="tscroll"><table class="ref">175 <caption>Kind 14 tags</caption>176 <tr><th>Tag</th><th>Form</th><th>Rule</th></tr>177 <tr><td class="btn">p</td><td><code>["p", &lt;receiver pubkey&gt;, &lt;relay-url&gt;]</code></td><td>One per receiver. One or more required.</td></tr>178 <tr><td class="btn">e</td><td><code>["e", &lt;parent kind-14 id&gt;, &lt;relay-url&gt;]</code></td><td>Present only on a reply. Names the direct parent message.</td></tr>179 <tr><td class="btn">subject</td><td><code>["subject", &lt;conversation title&gt;]</code></td><td>Optional. Renames the room. See Section 2.</td></tr>180 <tr><td class="btn">q</td><td><code>["q", &lt;event id or address&gt;, &lt;relay-url&gt;, &lt;pubkey, if a regular event&gt;]</code></td><td>Optional. Cites an event quoted inside <code>content</code>, using the <a href="21.html">NIP-21</a> <code>nostr:</code> scheme.</td></tr>181 </table></div>182183 <p><strong>Kind 15 — file message.</strong> It sends an encrypted file by reference. The <code>content</code> field holds only the file's URL; the file itself is encrypted and hosted separately.</p>184 <div class="tscroll"><table class="ref">185 <caption>Kind 15 tags</caption>186 <tr><th>Tag</th><th>Meaning</th></tr>187 <tr><td class="btn">p</td><td>One per receiver, same as kind 14.</td></tr>188 <tr><td class="btn">e</td><td>Direct parent message, if this is a reply.</td></tr>189 <tr><td class="btn">subject</td><td>Optional room name, same as kind 14.</td></tr>190 <tr><td class="btn">file-type</td><td>MIME type of the file before encryption, for example <code>image/jpeg</code>.</td></tr>191 <tr><td class="btn">encryption-algorithm</td><td>Algorithm used to encrypt the file. Only <code>aes-gcm</code> is defined today.</td></tr>192 <tr><td class="btn">decryption-key</td><td>The key the receiver needs to decrypt the file.</td></tr>193 <tr><td class="btn">decryption-nonce</td><td>The nonce the receiver needs to decrypt the file.</td></tr>194 <tr><td class="btn">x</td><td>sha256 hash, hex-encoded, of the encrypted file.</td></tr>195 <tr><td class="btn">ox</td><td>sha256 hash, hex-encoded, of the file before encryption.</td></tr>196 <tr><td class="btn">size</td><td>Optional. Size of the encrypted file, in bytes.</td></tr>197 <tr><td class="btn">dim</td><td>Optional. Pixel size, as <code>&lt;width&gt;x&lt;height&gt;</code>.</td></tr>198 <tr><td class="btn">thumbhash</td><td>Optional. A <a href="https://evanw.github.io/thumbhash/">thumbhash</a> to show while the file loads.</td></tr>199 <tr><td class="btn">blurhash</td><td>Optional. A <a href="https://github.com/woltapp/blurhash">blurhash</a> to show while the file loads.</td></tr>200 <tr><td class="btn">thumb</td><td>Optional. URL of a thumbnail, same aspect ratio, encrypted with the same key and nonce.</td></tr>201 <tr><td class="btn">fallback</td><td>Optional. Zero or more backup URLs for the file, in case the main URL fails.</td></tr>202 </table></div>203204 <div class="box tech">205 <span class="tag">Tech Note</span>206 <p>Source inconsistency: the kind 14 example's reply <code>e</code> tag has three elements, no marker: <code>["e", &lt;id&gt;, &lt;relay-url&gt;]</code>. The kind 15 example's reply <code>e</code> tag has a fourth element, a <code>"reply"</code> marker: <code>["e", &lt;id&gt;, &lt;relay-url&gt;, "reply"]</code>. This page restates both examples as written in <code>source-nips/17.md</code>. One 2025 change to this NIP is titled "removes the need for markers," which makes the kind 15 example's leftover marker look like an unfixed copy-paste artifact rather than an intended rule. Treat the unmarked, three-element form as current.</p>207 </div>208209 <h2 class="bar" id="publishing"><span class="num">5</span> Publishing and DM Relay Lists</h2>210 <p>Kind <code>10050</code> tells other clients where to deliver DMs. It is a small, replaceable event that lists a user's preferred inbox relays.</p>211 <div class="crt">212 <div class="titlebar">Wire format - kind 10050 DM relay list</div>213<pre>{214 <span class="k">"kind"</span>: 10050,215 <span class="k">"tags"</span>: [216 [<span class="s">"relay"</span>, <span class="s">"wss://inbox.nostr.wine"</span>],217 [<span class="s">"relay"</span>, <span class="s">"wss://myrelay.nostr1.com"</span>]218 ],219 <span class="k">"content"</span>: <span class="s">""</span>220 <span class="c">-- other fields, id/pubkey/created_at/sig, are the usual event fields --</span>221}</pre>222 </div>223 <p>The event MUST include one or more <code>relay</code> tags. Clients MUST publish gift wraps only to relays listed in the receiver's kind <code>10050</code> event. If a receiver has no such list, that receiver is not ready to receive messages. Clients should not try to send to them.</p>224225 <figure class="diagram">226 <div class="frame">227 <svg viewBox="0 0 620 260" role="img" aria-label="Sequence: Client A publishes a gift wrap to a relay from Client B's kind 10050 list; Client B subscribes and receives it, then unwraps it locally">228 <line class="d-wire" x1="90" y1="40" x2="90" y2="235" stroke-dasharray="3 4"/>229 <line class="d-wire" x1="310" y1="40" x2="310" y2="235" stroke-dasharray="3 4"/>230 <line class="d-wire" x1="530" y1="40" x2="530" y2="235" stroke-dasharray="3 4"/>231 <rect class="d-actor" x="35" y="8" width="110" height="30" rx="6"/>232 <text class="d-actor-label" x="90" y="28" text-anchor="middle">Client A</text>233 <rect class="d-actor" x="255" y="8" width="110" height="30" rx="6"/>234 <text class="d-actor-label" x="310" y="28" text-anchor="middle">Relay</text>235 <rect class="d-actor" x="475" y="8" width="110" height="30" rx="6"/>236 <text class="d-actor-label" x="530" y="28" text-anchor="middle">Client B</text>237 <text class="d-note" x="310" y="52" text-anchor="middle">from B's kind 10050 list</text>238 <path class="d-msg d-flow" d="M90,75 L305,75"/>239 <text class="d-label" x="200" y="70" text-anchor="middle">["EVENT", giftwrap]</text>240 <path class="d-msg blue" d="M310,102 L95,102"/>241 <text class="d-label" x="200" y="97" text-anchor="middle">["OK", id, true, ""]</text>242 <path class="d-msg d-flow" d="M530,142 L315,142"/>243 <text class="d-label" x="420" y="137" text-anchor="middle">["REQ","dm",&#123;"kinds":[1059],"#p":[B]&#125;]</text>244 <path class="d-msg blue d-flow" d="M310,172 L525,172"/>245 <text class="d-label" x="420" y="167" text-anchor="middle">["EVENT","dm", giftwrap]</text>246 <path class="d-msg blue" d="M310,198 L525,198"/>247 <text class="d-label" x="420" y="193" text-anchor="middle">["EOSE","dm"]</text>248 <text class="d-note" x="420" y="225" text-anchor="middle">Client B unwraps offline: gift wrap -&gt; seal -&gt; rumor.</text>249 </svg>250 </div>251 <figcaption>Fig. 17-2 — <b>Delivery.</b> A publishes to relays from B's kind 10050 list. B subscribes with a <code>#p</code> filter and unwraps locally.</figcaption>252 </figure>253254 <h2 class="bar" id="relays"><span class="num">6</span> Relay Recommendations</h2>255 <p>Relays SHOULD protect metadata: serve <code>kind:1059</code> events only to the pubkey named in the event's <code>p</code> tag. Relays enforce this with <a href="42.html">NIP-42</a> AUTH, requiring the requesting client to prove it controls that pubkey before the relay returns any results.</p>256 <p>Clients SHOULD guide users to keep their <code>kind:10050</code> list small, one to three relays. Clients SHOULD also spread that small list across as many separate relay operators as is practical, to avoid a single relay seeing every message.</p>257258 <h2 class="bar" id="lifecycle"><span class="num">7</span> Delete, Edit, and Disappearing Messages</h2>259 <p><a href="59.html">NIP-59</a> already lets the <code>p</code>-tagged recipient of a gift wrap delete it. NIP-17 adds one more option: a client MAY wrap a <code>kind:5</code> delete request in its own <code>kind:1059</code> gift wrap, and send it to the recipient as part of the conversation. Clients SHOULD remove a deleted message from the conversation view, or mark it as deleted.</p>260 <p>Clients MAY implement editing as a delete, followed by a new message published with the same timestamp as the original.</p>261 <p>Clients MAY offer disappearing messages two ways: add an <code>expiration</code> tag (<a href="40.html">NIP-40</a>) to each receiver's gift wrap, or simply skip generating a gift wrap addressed to the sender's own key. The <code>expiration</code> tag SHOULD also be set on the <code>kind:13</code> seal, in case the seal ever leaks on its own.</p>262263 <h2 class="bar" id="benefits"><span class="num">8</span> Benefits, Limitations, and Spam</h2>264 <p>NIP-17's three-layer design buys real privacy properties. It also has real costs. Both matter when choosing this scheme.</p>265 <div class="tscroll"><table class="ref">266 <caption>Privacy and security properties</caption>267 <tr><th>Property</th><th>What it means</th></tr>268 <tr><td class="btn">No metadata leak</td><td>Identities, real timestamps, kinds, and tags stay hidden from public view. Public data alone cannot link a sender to a receiver.</td></tr>269 <tr><td class="btn">No public group identifiers</td><td>No shared queue, channel, or ID lets an outsider count or correlate all messages in one room.</td></tr>270 <tr><td class="btn">No moderation</td><td>No admins, invitations, or bans exist at the protocol level.</td></tr>271 <tr><td class="btn">No shared secrets</td><td>No secret needs to be known by every member, so no secret can leak from a careless member.</td></tr>272 <tr><td class="btn">Fully recoverable</td><td>Any client holding the user's private key can recover the full message history.</td></tr>273 <tr><td class="btn">Optional forward secrecy</td><td>Users and clients may opt in to disappearing messages, described in Section 7.</td></tr>274 <tr><td class="btn">Works on public relays</td><td>Messages flow through public relays without losing privacy. Private relays help further, but are not required.</td></tr>275 <tr><td class="btn">Cold storage</td><td>Users may unilaterally share their messages with a separate key, kept only for DM backup and recovery.</td></tr>276 </table></div>277 <p>The main limitation: a sender must send a separate gift wrap to every single receiver. A group chat with more than ten participants should use a scheme built for groups instead.</p>278 <div class="box tech">279 <span class="tag">Tech Note</span>280 <p>Gift wraps sign with random, throwaway keys. A relay cannot apply the usual anti-spam tools, reputation, web-of-trust, or per-key rate limits, because every gift wrap arrives from a new, unknown key. The recommended fix is the <a href="42.html">NIP-42</a> AUTH-based relay policy described in <a href="59.html">NIP-59</a>'s spam protection section: require a real, authenticated identity before the relay accepts or serves gift wraps.</p>281 </div>282283 <h2 class="bar" id="examples"><span class="num">9</span> Worked Example</h2>284 <p>Alice sends "Hola, que tal?" to Bob. Below is a full round trip: the plain rumor, and the two real gift wraps this NIP produces, one addressed to Bob and one addressed back to Alice, exactly as published in the NIP-17 source text (long ciphertext blobs shortened with "…").</p>285286 <div class="crt">287 <div class="titlebar">Step 1 - unsigned rumor</div>288<pre>{289 <span class="k">"pubkey"</span>: <span class="s">"6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93"</span>,290 <span class="k">"created_at"</span>: 1703015180,291 <span class="k">"kind"</span>: 14,292 <span class="k">"tags"</span>: [293 [<span class="s">"p"</span>, <span class="s">"918e2da906df4ccd12c8ac672d8335add131a4cf9d27ce42b3bb3625755f0788"</span>]294 ],295 <span class="k">"content"</span>: <span class="s">"Hola, que tal?"</span>296}</pre>297 </div>298299 <div class="crt">300 <div class="titlebar">Step 2 - sealed with the sender's real key (kind 13)</div>301<pre>{302 <span class="k">"pubkey"</span>: <span class="s">"6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93"</span>,303 <span class="k">"created_at"</span>: 1703015180,304 <span class="k">"kind"</span>: 13,305 <span class="k">"tags"</span>: [],306 <span class="k">"content"</span>: <span class="c">nip44.encrypt(rumor, conversation_key(sender, receiver))</span>,307 <span class="k">"sig"</span>: <span class="c">&lt;signed by sender&gt;</span>308}</pre>309 </div>310311 <div class="crt">312 <div class="titlebar">Step 3 - gift wrap addressed to Bob (kind 1059)</div>313<pre>{314 <span class="k">"id"</span>: <span class="s">"2886780f7349afc1344047524540ee716f7bdc1b64191699855662330bf235d8"</span>,315 <span class="k">"pubkey"</span>: <span class="s">"8f8a7ec43b77d25799281207e1a47f7a654755055788f7482653f9c9661c6d51"</span>,316 <span class="k">"created_at"</span>: 1703128320,317 <span class="k">"kind"</span>: 1059,318 <span class="k">"tags"</span>: [319 [<span class="s">"p"</span>, <span class="s">"918e2da906df4ccd12c8ac672d8335add131a4cf9d27ce42b3bb3625755f0788"</span>]320 ],321 <span class="k">"content"</span>: <span class="s">"AsqzdlMsG304G8h08bE67dhAR1gFTzTckUUyuvndZ8L…U1Ob9fEJv/ibNuSfqL6E4BnN"</span>,322 <span class="k">"sig"</span>: <span class="s">"a3c6ce632b145c0869423c1afaff4a6d764a9b64dedaf15f170b944ead67227…c22bab"</span>323}</pre>324 </div>325326 <div class="crt">327 <div class="titlebar">Step 4 - gift wrap addressed back to Alice (kind 1059)</div>328<pre>{329 <span class="k">"id"</span>: <span class="s">"162b0611a1911cfcb30f8a5502792b346e535a45658b3a31ae5c178465509721"</span>,330 <span class="k">"pubkey"</span>: <span class="s">"626be2af274b29ea4816ad672ee452b7cf96bbb4836815a55699ae402183f512"</span>,331 <span class="k">"created_at"</span>: 1702711587,332 <span class="k">"kind"</span>: 1059,333 <span class="k">"tags"</span>: [334 [<span class="s">"p"</span>, <span class="s">"44900586091b284416a0c001f677f9c49f7639a55c3f1e2ec130a8e1a7998e1b"</span>]335 ],336 <span class="k">"content"</span>: <span class="s">"AsTClTzr0gzXXji7uye5UB6LYrx3HDjWGdkNaBS6BAX…lTLHiHMlTbarFOd6J/ybIb"</span>,337 <span class="k">"sig"</span>: <span class="s">"c94e74533b482aa8eeeb54ae72a5303e0b21f62909ca43c8ef06b0357412d6f…22bab"</span>338}</pre>339 </div>340 <p>Two gift wraps, near-identical in shape, carry the same rumor to two different keys: the receiver's, and the sender's own. A relay watching the wire sees two unrelated events, signed by two unrelated random keys. It cannot tell they are the same conversation.</p>341342 <h2 class="bar" id="history"><span class="num">10</span> Version History</h2>343 <p>NIP-17 has grown since 2024, from a two-party text chat into a scheme that also carries files and reactions. Notable changes, newest first:</p>344 <ul class="patchnotes">345 <li class="minor"><span class="ver">2026</span><span class="when">2026-06</span>Repo-wide cosmetic pass: code fences switched from <code>jsonc</code> to <code>yaml</code>; sections reordered for clarity. No rule changes.</li>346 <li><span class="ver">2026</span><span class="when">2026-05</span>Added the deletion section (a <code>kind:5</code> delete wrapped in a <code>kind:1059</code> gift wrap), and recommended <a href="42.html">NIP-42</a> AUTH as the standard fix for gift-wrap spam.</li>347 <li class="minor"><span class="ver">2026</span><span class="when">2026-04</span>Added <code>thumbhash</code> as an optional preview field, alongside the existing <code>blurhash</code>.</li>348 <li><span class="ver">2025</span><span class="when">2025-12</span>Tagged the NIP as a <code>relay</code>-relevant document.</li>349 <li><span class="ver">2025</span><span class="when">2025-10</span>Refined wording throughout, and confirmed <code>kind 7</code> reactions as a message rumor type alongside kind 14 and kind 15. A second change the same month clarified that clients must publish only to the receiver's kind 10050 relays.</li>350 <li><span class="ver">2025</span><span class="when">2025-02</span>Dropped the old reply-marker convention in favor of a plain <code>e</code> tag, and added <code>q</code> tags for citing events in <code>content</code>. Days earlier, <code>kind 15</code> file messages were added.</li>351 <li class="minor"><span class="ver">2024</span><span class="when">2024-08 to 2024-12</span>Small fixes: object formatting, JSON-to-JSONC code fences, and a clarification on field order.</li>352 <li><span class="ver">2024</span><span class="when">2024-04</span>fiatjaf added relay considerations and implementation notes, the same day the NIP landed.</li>353 <li><span class="ver">Origin</span><span class="when">2024-04-24</span>Vitor Pamplona published the seal-and-gift-wrap DM scheme as NIP-17, renumbered from an earlier draft that had circulated as NIP-24 (PR <a href="https://github.com/nostr-protocol/nips/pull/686">#686</a>). The filename existed since the August 2023 repo-wide "nips over nostr" import, but held no NIP-17 content until this commit.</li>354 </ul>355356 <div class="credits">357 <h3 class="cardhead">Original Proposal - Credits</h3>358 <table>359 <tr><td>Author</td><td>Vitor Pamplona</td></tr>360 <tr><td>First commit</td><td>2024-04-24 — “NIP-17 (old 24) Sealed Gift-Wrapped Messages for Private DMs and Small Group Chats” (PR <a href="https://github.com/nostr-protocol/nips/pull/686">#686</a>)</td></tr>361 <tr><td>Status</td><td><span class="chip draft">draft</span> <span class="chip optional">optional</span> <span class="chip relay">relay</span></td></tr>362 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/17.md">17.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/17.md">full change history</a></td></tr>363 </table>364 </div>365366 <h2 class="bar"><span class="num">11</span> See Also</h2>367 <ul>368 <li><a href="01.html">NIP-01</a> — the base event format that every rumor, seal, and gift wrap builds on.</li>369 <li><a href="04.html">NIP-04</a> — the older, deprecated encrypted DM scheme that NIP-17 replaces.</li>370 <li><a href="44.html">NIP-44</a> — the versioned encryption used at both the seal and the gift-wrap layer.</li>371 <li><a href="59.html">NIP-59</a> — defines the rumor, seal, and gift-wrap kinds themselves, and their deletion and spam-protection rules.</li>372 <li><a href="40.html">NIP-40</a> — the <code>expiration</code> tag used for disappearing messages.</li>373 <li><a href="42.html">NIP-42</a> — relay AUTH, used to restrict gift-wrap delivery and to mitigate spam.</li>374 <li><a href="09.html">NIP-09</a> — event deletion, the base mechanism a wrapped <code>kind:5</code> request relies on.</li>375 <li><a href="25.html">NIP-25</a> — defines <code>kind 7</code> reactions, one of the three message rumor types.</li>376 </ul>377 </div>378379 <aside>380 <div class="cartridge">381 <div class="inner">382 <h3 class="cardhead">Spec Card</h3>383 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>384 <dl>385 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip optional">optional</span><span class="chip relay">relay</span></dd>386 <dt>Defines</dt><dd>kind 14 (chat) - kind 15 (file) - kind 10050 (DM relay list)</dd>387 <dt>Kind(s)</dt><dd>14 - 15 - 10050 (wraps 13 &amp; 1059 from NIP-59)</dd>388 <dt>Requires</dt><dd><a href="01.html">NIP-01</a> - <a href="44.html">NIP-44</a> - <a href="59.html">NIP-59</a></dd>389 <dt>Transport</dt><dd>WebSocket - nested NIP-44 ciphertext</dd>390 <dt>Crypto</dt><dd>NIP-44 v2 - secp256k1 ECDH - ChaCha20</dd>391 <dt>First commit</dt><dd>2023-08-09</dd>392 <dt>Last updated</dt><dd>2026-06-13</dd>393 </dl>394 </div>395 </div>396 </aside>397 </main>398399 <nav class="navbtns">400 <a href="16.html">◀ NIP-16: Event Treatment</a>401 <a href="18.html">NIP-18: Reposts ▶</a>402 </nav>403404 <footer class="pagefoot">405 <a href="../index.html">Nostr Protocol Manual</a>406 <span class="pageno">NIP 17 / F4</span>407 </footer>408</div>409410<script src="../assets/webmcp.js" defer></script>411</body>412</html>