12<html lang="en">3<head>4<meta charset="utf-8">5<meta name="viewport" content="width=device-width, initial-scale=1">6<title>NIP-B7 - Blossom media — Nostr Instruction Manual</title>7<meta name="description" content="NIP-B7: how Nostr clients use Blossom servers for media. The kind 10063 server list, sha256-addressed URLs, and the broken-link recovery procedure.">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 B7 - Media</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="B7">B7</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № B7</div>50 <h1>Blossom media</h1>51 <p class="sub">How clients use sha256-addressed Blossom servers to store media, and how they recover a file when its original URL dies.</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>Media links on Nostr rot. A note lives forever on relays, but the image it points to sits on one HTTP server. When that server goes away, the note shows a broken picture. This NIP fixes that.</p>60 <p><strong>Blossom</strong> is a set of standards for media servers. Each standard is called a <strong>BUD</strong>. A Blossom server stores files and addresses each file by its <code>sha256</code> hash. The same file has the same address on every Blossom server. That makes every copy interchangeable.</p>61 <p>This NIP tells Nostr clients how to use Blossom. Clients may use all the BUDs: upload files, manage their own files, and so on. Most importantly, clients SHOULD use <a href="https://github.com/hzrd149/blossom/blob/master/buds/03.md">BUD-03</a> to fetch each user's <code>kind:10063</code> list of Blossom servers. With that list, a client can find a lost file on the user's other servers.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>Think of the sha256 hash as a serial number stamped on the file itself. The URL only tells you which shelf to check first. If the shelf is empty, ask the owner's <code>kind:10063</code> list which other shelves hold the same serial number.</p>66 </div>6768 <figure class="diagram">69 <div class="frame">70 <svg viewBox="0 0 560 120" role="img" aria-label="Recovery pipeline: a dead media URL leads to the kind 10063 server list, then to alternate Blossom servers, then to a sha256 check">71 <rect class="d-actor" x="10" y="35" width="120" height="50" rx="8"/>72 <text class="d-actor-label" x="70" y="57" text-anchor="middle">Dead URL</text>73 <text class="d-note" x="70" y="74" text-anchor="middle">…/<64-hex>.png</text>74 <rect class="d-actor" x="180" y="35" width="120" height="50" rx="8"/>75 <text class="d-actor-label" x="240" y="57" text-anchor="middle">kind 10063</text>76 <text class="d-note" x="240" y="74" text-anchor="middle">server list</text>77 <rect class="d-actor" x="350" y="35" width="90" height="50" rx="8"/>78 <text class="d-actor-label" x="395" y="57" text-anchor="middle">Retry</text>79 <text class="d-note" x="395" y="74" text-anchor="middle">each server</text>80 <rect class="d-actor" x="480" y="35" width="70" height="50" rx="8"/>81 <text class="d-actor-label" x="515" y="57" text-anchor="middle">Verify</text>82 <text class="d-note" x="515" y="74" text-anchor="middle">sha256</text>83 <path class="d-msg d-flow" d="M132,60 L177,60"/>84 <path class="d-msg d-flow" d="M302,60 L347,60"/>85 <path class="d-msg d-flow" d="M442,60 L477,60"/>86 </svg>87 </div>88 <figcaption>Fig. B7-1 — <b>Recovery pipeline.</b> The hash in the URL is the key. The server list supplies fallback locations.</figcaption>89 </figure>9091 <h2 class="bar" id="spec"><span class="num">2</span> The Server List: kind 10063</h2>92 <p>Each user publishes one <code>kind:10063</code> event. The event lists the Blossom servers that hold the user's files. Each server appears in one <code>server</code> tag. The <code>content</code> field stays empty. The list format comes from BUD-03; clients SHOULD fetch these lists as BUD-03 describes.</p>9394 <div class="crt">95 <div class="titlebar">Wire format - kind 10063 server list</div>96<pre>{97 <span class="k">"id"</span>: <span class="s">"e4bee088334cb5d38cff1616e964369c37b6081be997962ab289d6c671975d71"</span>,98 <span class="k">"pubkey"</span>: <span class="s">"781208004e09102d7da3b7345e64fd193cd1bc3fce8fdae6008d77f9cabcd036"</span>,99 <span class="k">"content"</span>: <span class="s">""</span>,100 <span class="k">"kind"</span>: 10063,101 <span class="k">"created_at"</span>: 1708774162,102 <span class="k">"tags"</span>: [103 [<span class="s">"server"</span>, <span class="s">"https://blossom.self.hosted"</span>],104 [<span class="s">"server"</span>, <span class="s">"https://cdn.blossom.cloud"</span>]105 ],106 <span class="k">"sig"</span>: <span class="s">"cc5efa74f59e80622c77cacf4dd62076bcb7581b45e9acff471e7963a1f4d8b3406adab5ee1ac9673487480e57d20e523428e60ffcc7e7a904ac882cfccfc653"</span>107}</pre>108 </div>109110 <div class="tscroll"><table class="ref">111 <caption>Kind and tag reference</caption>112 <tr><th>Item</th><th>Value</th><th>Rule</th></tr>113 <tr><td class="btn">kind</td><td><code>10063</code></td><td>The user's Blossom server list. One list per user. The kind sits in the replaceable range of <a href="01.html">NIP-01</a>, so relays keep only the latest event per pubkey.</td></tr>114 <tr><td class="btn">server</td><td><code>["server", <server URL>]</code></td><td>One tag per Blossom server. The value is the server's base HTTPS URL.</td></tr>115 <tr><td class="btn">content</td><td><code>""</code></td><td>Empty string. The tags carry all the data.</td></tr>116 </table></div>117118 <div class="box tech">119 <span class="tag">Tech Note</span>120 <p>This NIP defines almost nothing itself. Kind <code>10063</code>, the <code>server</code> tag, and the server HTTP API are all normative in the external Blossom BUD documents, not in the NIPs repository. The event shown above is the source's only example; treat <a href="https://github.com/hzrd149/blossom/blob/master/buds/03.md">BUD-03</a> as the authority if the two ever disagree.</p>121 </div>122123 <h2 class="bar" id="recovery"><span class="num">3</span> Recovering a Broken Media URL</h2>124 <p>A client renders an event and finds a media URL inside it. Sometimes that URL no longer works. Follow this procedure:</p>125 <ol>126 <li>Check the URL path. Does it end with a 64-character hex string, with or without a file extension? If yes, that string is likely a <code>sha256</code> hash of the file.</li>127 <li>The event's author may have published a <code>kind:10063</code> list. The client SHOULD fetch that list.</li>128 <li>For each <code>server</code> tag in the list, build a new URL. Use the hex string as the path, optionally with the file extension at the end: <code>https://blossom.self.hosted/<hex-string>.png</code>.</li>129 <li>Request each candidate URL until one returns the file.</li>130 <li>The client SHOULD verify the download: the <code>sha256</code> hash of the file's contents must match the 64-character hex string.</li>131 </ol>132 <p>More information about this lookup is in <a href="https://github.com/hzrd149/blossom/blob/master/buds/03.md">BUD-03</a>.</p>133134 <div class="box warn">135 <span class="tag">Warning!</span>136 <p>Do not skip the hash check. Any server can answer any path. Only the <code>sha256</code> match proves you received the file the author posted. Discard a file whose hash differs.</p>137 </div>138139 <div class="box tech">140 <span class="tag">Tech Note</span>141 <p>The trigger is a heuristic. The source says a dead URL that ends in 64 hex characters "is likely" a sha256 hash. It defines no marker that says so for certain, and it does not say how a client decides that a URL "is not available anymore" (status codes, timeouts). Implementations must pick their own failure test. False positives are harmless: the hash check in step 5 rejects any wrong file.</p>142 </div>143144 <figure class="diagram">145 <div class="frame">146 <svg viewBox="0 0 560 320" role="img" aria-label="Sequence diagram: client fetches the kind 10063 list from a relay, then requests the file by hash from a Blossom server and verifies the sha256">147 <line class="d-wire" x1="90" y1="40" x2="90" y2="305" stroke-dasharray="3 4"/>148 <line class="d-wire" x1="300" y1="40" x2="300" y2="140" stroke-dasharray="3 4"/>149 <line class="d-wire" x1="480" y1="40" x2="480" y2="305" stroke-dasharray="3 4"/>150 <rect class="d-actor" x="40" y="8" width="100" height="30" rx="6"/>151 <text class="d-actor-label" x="90" y="28" text-anchor="middle">Client</text>152 <rect class="d-actor" x="250" y="8" width="100" height="30" rx="6"/>153 <text class="d-actor-label" x="300" y="28" text-anchor="middle">Relay</text>154 <rect class="d-actor" x="420" y="8" width="120" height="30" rx="6"/>155 <text class="d-actor-label" x="480" y="28" text-anchor="middle">Blossom srv</text>156 <path class="d-msg" d="M90,70 L295,70"/>157 <text class="d-label" x="192" y="63" text-anchor="middle">["REQ","srv",{"kinds":[10063],…}]</text>158 <path class="d-msg blue" d="M300,105 L95,105"/>159 <text class="d-label" x="192" y="98" text-anchor="middle">["EVENT","srv",{server tags}]</text>160 <path class="d-msg" d="M90,160 L475,160"/>161 <text class="d-label" x="282" y="153" text-anchor="middle">GET /<hex-string>.png</text>162 <path class="d-msg blue d-flow" d="M480,205 L95,205"/>163 <text class="d-label" x="282" y="198" text-anchor="middle">200 OK + file bytes</text>164 <text class="d-note" x="282" y="245" text-anchor="middle">Client computes sha256(bytes).</text>165 <text class="d-note" x="282" y="265" text-anchor="middle">Match with the hex string → render. Mismatch → discard.</text>166 <circle r="4" fill="#f26a00">167 <animateMotion dur="2.6s" repeatCount="indefinite" path="M90,160 L475,160"/>168 </circle>169 </svg>170 </div>171 <figcaption>Fig. B7-2 — <b>Recovery sequence.</b> The relay serves the server list. The Blossom server serves the file. The hash check closes the loop.</figcaption>172 </figure>173174 <h2 class="bar" id="complex"><span class="num">4</span> More Complex Interactions</h2>175 <p>Blossom servers expose more facilities than plain downloads. For example, a client can check that a file exists on a server without downloading it. Clients may use any of these facilities. The <a href="https://github.com/hzrd149/blossom">Blossom BUDs</a> document them; this NIP does not restate them.</p>176177 <h2 class="bar" id="examples"><span class="num">5</span> Worked Example</h2>178 <p>Alice posted a photo to her own Blossom server. That server is now offline. Bob's client repairs the link. Orange lines are requests the client sends. Blue lines are responses.</p>179 <div class="crt">180 <div class="titlebar">Recovery session - Bob's client</div>181<pre><span class="c">-- 1. The original URL in Alice's note is dead --</span>182<span class="send">→ GET https://media.dead.example/b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553.jpg</span>183<span class="recv">← (connection refused)</span>184185<span class="c">-- 2. Path ends in 64 hex chars: fetch Alice's server list --</span>186<span class="send">→ ["REQ", "srv", {"kinds":[10063], "authors":["781208004e09…cd036"]}]</span>187<span class="recv">← ["EVENT", "srv", {"kind":10063, "tags":[188 ["server","https://blossom.self.hosted"],189 ["server","https://cdn.blossom.cloud"]], …}]</span>190<span class="recv">← ["EOSE", "srv"]</span>191192<span class="c">-- 3. Try each server with the hash as the path --</span>193<span class="send">→ GET https://blossom.self.hosted/b1674191a88e…f4f553.jpg</span>194<span class="recv">← 404 Not Found</span>195<span class="send">→ GET https://cdn.blossom.cloud/b1674191a88e…f4f553.jpg</span>196<span class="recv">← 200 OK (image bytes)</span>197198<span class="c">-- 4. Verify: sha256(bytes) == b1674191a88e…f4f553 → render the photo --</span></pre>199 </div>200201 <h2 class="bar" id="history"><span class="num">6</span> Version History</h2>202 <p>NIP-B7 is young and short. It replaced <a href="96.html">NIP-96</a> as the recommended way to host media for Nostr. Changes, newest first:</p>203 <ul class="patchnotes">204 <li class="minor"><span class="ver">2025</span><span class="when">2025-04</span>Renamed the source files to uppercase hex (<code>b7.md</code> became <code>B7.md</code>). No spec change.</li>205 <li><span class="ver">Origin</span><span class="when">2025-04-24</span>fiatjaf added NIP-B7 for Blossom interaction in <a href="https://github.com/nostr-protocol/nips/pull/1822">PR #1822</a>. The Blossom project itself predates the NIP.</li>206 </ul>207208 <div class="credits">209 <h3 class="cardhead">Original Proposal - Credits</h3>210 <table>211 <tr><td>Author</td><td>fiatjaf</td></tr>212 <tr><td>First commit</td><td>2025-04-24 — “add B7 NIP for Blossom interaction (#1822)” - <a href="https://github.com/nostr-protocol/nips/pull/1822">PR #1822</a></td></tr>213 <tr><td>Status</td><td>draft - optional — clients that show media SHOULD implement the recovery lookup</td></tr>214 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/B7.md">B7.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/B7.md">full change history</a></td></tr>215 </table>216 </div>217218 <h2 class="bar"><span class="num">7</span> See Also</h2>219 <ul>220 <li><a href="01.html">NIP-01</a> — the event format, replaceable kinds, and the <code>REQ</code> flow used to fetch the server list.</li>221 <li><a href="96.html">NIP-96</a> — the older HTTP file storage spec, now deprecated in favor of this NIP.</li>222 <li><a href="92.html">NIP-92</a> — <code>imeta</code> tags that attach metadata (including a <code>sha256</code> hash) to media URLs in events.</li>223 <li><a href="94.html">NIP-94</a> — file metadata events that describe hosted files.</li>224 <li><a href="51.html">NIP-51</a> — the standard lists catalog; it registers kind <code>10063</code> as the Blossom server list.</li>225 <li><a href="5a.html">NIP-5A</a> — static websites served from Blossom blobs; uses the same <code>kind:10063</code> lookup.</li>226 </ul>227 </div>228229 <aside>230 <div class="cartridge">231 <div class="inner">232 <h3 class="cardhead">Spec Card</h3>233 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>234 <dl>235 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip">optional</span></dd>236 <dt>Defines</dt><dd>use of Blossom BUDs - hash-based media recovery</dd>237 <dt>Kind</dt><dd>10063 - server list (from BUD-03)</dd>238 <dt>Requires</dt><dd><a href="01.html">NIP-01</a> - Blossom BUD-03</dd>239 <dt>Transport</dt><dd>HTTPS to Blossom servers - WebSocket to relays</dd>240 <dt>Crypto</dt><dd>sha256 content addressing</dd>241 <dt>First commit</dt><dd>2025-04-24</dd>242 <dt>Last updated</dt><dd>2025-04-24</dd>243 </dl>244 </div>245 </div>246 </aside>247 </main>248249 <nav class="navbtns">250 <a href="b0.html">◀ NIP-B0: Web Bookmarking</a>251 <a href="be.html">NIP-BE: Nostr BLE Communications Protocol ▶</a>252 </nav>253254 <footer class="pagefoot">255 <a href="../index.html">Nostr Protocol Manual</a>256 <span class="pageno">NIP B7 / F4</span>257 </footer>258</div>259260<script src="../assets/webmcp.js" defer></script>261</body>262</html>