clone

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

main
nip.guide / site / nips / 92.html
92.html 18.5K
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-92 - Media Attachments Metadata (imeta) — Nostr Instruction Manual</title>7<meta name="description" content="NIP-92: the imeta tag. Inline metadata for media URLs in event content: MIME type, blurhash, dimensions, hash, alt text, and fallbacks. 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 92 - 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="92">92</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 92</div>50 <h1>Media Attachments Metadata (<code>imeta</code>)</h1>51 <p class="sub">Attach machine-readable metadata to the media URLs inside an event's content with one <code>imeta</code> tag per URL.</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>A Nostr note carries media the simple way: the author pastes a URL into the event <code>content</code>. A bare URL tells the client nothing else. The client does not know the file type, the image size, or a text alternative. It must download the file before it can lay out the post.</p>60 <p>This NIP fixes that with the <strong><code>imeta</code></strong> tag. The name means <em>inline metadata</em>. A client adds media attachments — images, videos, and other files — by including a URL in the event content, along with a matching <code>imeta</code> tag. The tag describes that URL: MIME type, dimensions, a blurhash placeholder, a file hash, alt text, and fallback URLs.</p>61 <p><code>imeta</code> tags MAY add information about media URLs in the event's content. Each <code>imeta</code> tag SHOULD match a URL in the event content. Clients MAY replace <code>imeta</code> URLs with rich previews. The tag reuses the field vocabulary of <a href="94.html">NIP-94</a>, so one set of field names serves both file events and inline attachments.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>Think of <code>imeta</code> as the sticker on the back of a photo print. The picture is in the <code>content</code>. The sticker in the <code>tags</code> says what it is, how big it is, and where to find a spare copy.</p>66 </div>6768 <figure class="diagram">69 <div class="frame">70 <svg viewBox="0 0 560 200" role="img" aria-label="A URL in the event content is matched by url field of an imeta tag in the event tags">71 <rect class="d-actor" x="10" y="20" width="250" height="70" rx="8"/>72 <text class="d-actor-label" x="135" y="44" text-anchor="middle">content</text>73 <text class="d-note" x="135" y="62" text-anchor="middle">"Coast pic!</text>74 <text class="d-note" x="135" y="78" text-anchor="middle">https://…/my-image.jpg"</text>75 <rect class="d-actor" x="300" y="20" width="250" height="160" rx="8"/>76 <text class="d-actor-label" x="425" y="44" text-anchor="middle">imeta tag</text>77 <text class="d-note" x="315" y="66">url https://…/my-image.jpg</text>78 <text class="d-note" x="315" y="84">m image/jpeg</text>79 <text class="d-note" x="315" y="102">dim 3024x4032</text>80 <text class="d-note" x="315" y="120">blurhash eVF$^OI:…</text>81 <text class="d-note" x="315" y="138">alt A scenic photo…</text>82 <text class="d-note" x="315" y="156">x &lt;sha256&gt; - fallback …</text>83 <path class="d-msg d-flow" d="M262,66 L297,66"/>84 <text class="d-label" x="280" y="112" text-anchor="middle" transform="rotate(90 280 112)">MATCH</text>85 <circle r="4" fill="#f26a00">86 <animateMotion dur="2.6s" repeatCount="indefinite" path="M262,66 L297,66"/>87 </circle>88 </svg>89 </div>90 <figcaption>Fig. 92-1 — <b>URL matching.</b> The <code>url</code> field of the <code>imeta</code> tag points at one URL inside the event content.</figcaption>91 </figure>9293 <h2 class="bar" id="spec"><span class="num">2</span> The imeta Tag</h2>94 <p>The <code>imeta</code> tag is <strong>variadic</strong>: it has no fixed length. Each entry after the tag name is one space-delimited key/value pair inside a single string. The key is the first word. The value is the rest of the string.</p>9596 <div class="crt">97 <div class="titlebar">Tag format - imeta</div>98<pre>[99 <span class="s">"imeta"</span>,100 <span class="s">"url <span class="c">&lt;media URL, required&gt;</span>"</span>,101 <span class="s">"<span class="c">&lt;field&gt;</span> <span class="c">&lt;value&gt;</span>"</span>, <span class="c">// one entry per field, at least one</span>102 ...103]</pre>104 </div>105106 <p>Four normative rules govern the tag:</p>107 <ol>108 <li>Each <code>imeta</code> tag MUST have a <code>url</code> entry and at least one other field.</li>109 <li>An <code>imeta</code> tag MAY include any field specified by <a href="94.html">NIP-94</a>.</li>110 <li>Each <code>imeta</code> tag SHOULD match a URL in the event content.</li>111 <li>There SHOULD be only one <code>imeta</code> tag per URL.</li>112 </ol>113114 <p>The fields below appear in the specification's own example. All of them come from the <a href="94.html">NIP-94</a> vocabulary:</p>115 <div class="tscroll"><table class="ref">116 <caption>Common imeta fields (from NIP-94)</caption>117 <tr><th>Field</th><th>Value</th><th>Purpose</th></tr>118 <tr><td class="btn">url</td><td>URL</td><td>The media URL. Required. It should match a URL in the content.</td></tr>119 <tr><td class="btn">m</td><td>MIME type</td><td>The file type, for example <code>image/jpeg</code>. Lets the client pick a renderer before downloading.</td></tr>120 <tr><td class="btn">blurhash</td><td>blurhash string</td><td>A tiny encoded placeholder. The client shows a blur while the file loads.</td></tr>121 <tr><td class="btn">dim</td><td><code>&lt;width&gt;x&lt;height&gt;</code></td><td>Pixel dimensions. The client reserves layout space and avoids reflow.</td></tr>122 <tr><td class="btn">alt</td><td>free text</td><td>A text description for accessibility. Screen readers use it.</td></tr>123 <tr><td class="btn">x</td><td>hex string</td><td>The sha256 hash of the file, as specified in <a href="94.html">NIP-94</a>. Verifies the download.</td></tr>124 <tr><td class="btn">fallback</td><td>URL</td><td>An alternate URL for the same file. May appear more than once. Used when the primary URL fails.</td></tr>125 </table></div>126127 <div class="box tech">128 <span class="tag">Tech Note</span>129 <p>The spec calls each entry a "key/value pair", yet its own example repeats the <code>fallback</code> key. It never states which keys may repeat. It also never states the split rule for values that contain spaces, such as <code>alt</code>; implementations split on the first space only. Treat both points as conventions, not written rules.</p>130 </div>131132 <h2 class="bar" id="behavior"><span class="num">3</span> Recommended Client Behavior</h2>133 <p>The metadata is produced at composition time. The spec recommends two paths:</p>134 <ul>135 <li><strong>Upload path.</strong> The user uploads a file during a new post. The client MAY include this metadata after the file is uploaded and included in the post.</li>136 <li><strong>Paste path.</strong> The user pastes a URL during post composition. The client MAY download the file and add this metadata before the post is sent.</li>137 </ul>138 <p>On the reading side, a client MAY replace <code>imeta</code> URLs in the content with rich previews. A client MAY ignore <code>imeta</code> tags that do not match the URL in the event content. Every rule in this NIP is a MAY or a SHOULD: an event without <code>imeta</code> tags is still valid, and a client without <code>imeta</code> support still renders the bare URL.</p>139140 <figure class="diagram">141 <div class="frame">142 <svg viewBox="0 0 560 270" role="img" aria-label="Sequence diagram: client uploads a file to a media host, receives URL and metadata, builds the imeta tag, and publishes the event to a relay">143 <line class="d-wire" x1="90" y1="40" x2="90" y2="255" stroke-dasharray="3 4"/>144 <line class="d-wire" x1="280" y1="40" x2="280" y2="255" stroke-dasharray="3 4"/>145 <line class="d-wire" x1="470" y1="40" x2="470" y2="255" stroke-dasharray="3 4"/>146 <rect class="d-actor" x="40" y="8" width="100" height="30" rx="6"/>147 <text class="d-actor-label" x="90" y="28" text-anchor="middle">Client</text>148 <rect class="d-actor" x="230" y="8" width="100" height="30" rx="6"/>149 <text class="d-actor-label" x="280" y="28" text-anchor="middle">Media host</text>150 <rect class="d-actor" x="420" y="8" width="100" height="30" rx="6"/>151 <text class="d-actor-label" x="470" y="28" text-anchor="middle">Relay</text>152 <path class="d-msg" d="M90,62 L275,62"/>153 <text class="d-label" x="182" y="55" text-anchor="middle">upload file</text>154 <path class="d-msg blue" d="M280,95 L95,95"/>155 <text class="d-label" x="182" y="88" text-anchor="middle">URL + type, dim, hash…</text>156 <text class="d-note" x="90" y="130" text-anchor="start">build content with URL + matching imeta tag</text>157 <path class="d-msg d-flow" d="M90,165 L465,165"/>158 <text class="d-label" x="278" y="158" text-anchor="middle">["EVENT", {…, tags:[["imeta", …]]}]</text>159 <path class="d-msg blue" d="M470,198 L95,198"/>160 <text class="d-label" x="278" y="191" text-anchor="middle">["OK", &lt;id&gt;, true, ""]</text>161 <text class="d-note" x="280" y="235" text-anchor="middle">Paste path: the client downloads the pasted URL itself, then builds the same tag.</text>162 <circle r="4" fill="#f26a00">163 <animateMotion dur="2.6s" repeatCount="indefinite" path="M90,165 L465,165"/>164 </circle>165 </svg>166 </div>167 <figcaption>Fig. 92-2 — <b>Compose-time flow.</b> The client gathers metadata at upload or paste time, then publishes one event with URL and <code>imeta</code> together.</figcaption>168 </figure>169170 <div class="box warn">171 <span class="tag">Warning!</span>172 <p>Metadata is a claim by the author, not a proof. Verify the <code>x</code> hash after download before you trust that a fallback URL served the same file. Do not let a spoofed <code>dim</code> or <code>m</code> value drive unchecked buffer sizes.</p>173 </div>174175 <h2 class="bar" id="examples"><span class="num">4</span> Worked Example</h2>176 <p>The event below is a plain kind-1 note (<a href="10.html">NIP-10</a>). The content holds one image URL. One <code>imeta</code> tag matches it and carries seven metadata entries, including two <code>fallback</code> URLs.</p>177178 <div class="crt">179 <div class="titlebar">Event - kind 1 note with one image attachment</div>180<pre>{181 <span class="k">"id"</span>: <span class="s">"7fa8c2e1d0…"</span>,182 <span class="k">"pubkey"</span>: <span class="s">"22a12a12…"</span>,183 <span class="k">"created_at"</span>: 1749762600,184 <span class="k">"kind"</span>: 1,185 <span class="k">"content"</span>: <span class="s">"More image metadata tests, do not mind me https://nostr.build/i/my-image.jpg"</span>,186 <span class="k">"tags"</span>: [187 [188 <span class="s">"imeta"</span>,189 <span class="s">"url https://nostr.build/i/my-image.jpg"</span>,190 <span class="s">"m image/jpeg"</span>,191 <span class="s">"blurhash eVF$^OI:${M{o#*0-nNFxakD-?xVM}WEWB%iNKxvR-oetmo#R-aen$"</span>,192 <span class="s">"dim 3024x4032"</span>,193 <span class="s">"alt A scenic photo overlooking the coast of Costa Rica"</span>,194 <span class="s">"x <span class="c">&lt;sha256 hash as specified in NIP-94&gt;</span>"</span>,195 <span class="s">"fallback https://nostrcheck.me/alt1.jpg"</span>,196 <span class="s">"fallback https://void.cat/alt1.jpg"</span>197 ]198 ],199 <span class="k">"sig"</span>: <span class="s">"e8551d85…"</span>200}</pre>201 </div>202203 <p>A rendering client walks through the event like this:</p>204 <ol>205 <li>Scan the content. Find the URL <code>https://nostr.build/i/my-image.jpg</code>.</li>206 <li>Find the <code>imeta</code> tag whose <code>url</code> entry matches that URL.</li>207 <li>Reserve a 3024x4032 box from <code>dim</code>. Paint the <code>blurhash</code> placeholder into it.</li>208 <li>Fetch the image. If the primary URL fails, try each <code>fallback</code> URL in turn.</li>209 <li>Check the download against the <code>x</code> hash. Swap the placeholder for the image.</li>210 <li>Set the <code>alt</code> text on the rendered image. Replace the raw URL in the text with the rich preview.</li>211 </ol>212213 <h2 class="bar" id="history"><span class="num">5</span> Version History</h2>214 <p>The <code>imeta</code> tag started life under the number 29 and moved to 92 three months later. Notable changes, newest first:</p>215 <ul class="patchnotes">216 <li><span class="ver">2026</span><span class="when">2026-06</span>Title settled as "Media Attachments Metadata (<code>imeta</code>)" after two renames for clarity.</li>217 <li><span class="ver">2025</span><span class="when">2025-01</span>Lowercase "may" became normative MAY for adding metadata, using NIP-94 fields, and rich previews.</li>218 <li><span class="ver">2024</span><span class="when">2024-02</span>Added the rule that clients MAY ignore <code>imeta</code> tags that do not match a content URL.</li>219 <li><span class="ver">2024</span><span class="when">2024-02</span>Renumbered from NIP-29 to NIP-92; the old number later went to relay-based groups (<a href="29.html">NIP-29</a>).</li>220 <li><span class="ver">2024</span><span class="when">2024-02</span>Scope widened from images to all files; "MUST match URLs" softened to SHOULD; <code>m</code> added to the example.</li>221 <li class="minor"><span class="ver">2024</span><span class="when">2024-02</span>Typo fixes: URL casing and a missing comma in the example.</li>222 <li><span class="ver">Origin</span><span class="when">2023-11</span>Jon Staab added the <code>imeta</code> tag as draft NIP-29, "Add imeta tag".</li>223 </ul>224225 <div class="credits">226 <h3 class="cardhead">Original Proposal - Credits</h3>227 <table>228 <tr><td>Author</td><td>Jon Staab</td></tr>229 <tr><td>First commit</td><td>2023-11-27 — "Add imeta tag" (filed as 29.md, renumbered to 92.md on 2024-02-01)</td></tr>230 <tr><td>Status</td><td>active - optional — clients without it still render the bare URL</td></tr>231 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/92.md">92.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/92.md">full change history</a></td></tr>232 </table>233 </div>234235 <h2 class="bar"><span class="num">6</span> See Also</h2>236 <ul>237 <li><a href="01.html">NIP-01</a> — the base event and tag format that <code>imeta</code> rides on.</li>238 <li><a href="94.html">NIP-94</a> — defines the field vocabulary (<code>m</code>, <code>dim</code>, <code>x</code>, <code>blurhash</code>, …) that <code>imeta</code> borrows.</li>239 <li><a href="68.html">NIP-68</a> — picture-first feeds; picture events build on <code>imeta</code>.</li>240 <li><a href="71.html">NIP-71</a> — video events with the same metadata style.</li>241 <li><a href="96.html">NIP-96</a> — HTTP file storage servers that return this metadata at upload time.</li>242 </ul>243 </div>244245 <aside>246 <div class="cartridge">247 <div class="inner">248 <h3 class="cardhead">Spec Card</h3>249 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>250 <dl>251 <dt>Status</dt><dd><span class="chip draft">draft</span> active - optional</dd>252 <dt>Defines</dt><dd>the <code>imeta</code> tag - inline media metadata</dd>253 <dt>Kinds</dt><dd>none — a tag usable in any event with media URLs</dd>254 <dt>Fields</dt><dd><code>url</code> + any <a href="94.html">NIP-94</a> field</dd>255 <dt>Requires</dt><dd><a href="01.html">NIP-01</a> - <a href="94.html">NIP-94</a></dd>256 <dt>First commit</dt><dd>2023-11-27</dd>257 <dt>Last updated</dt><dd>2026-06-02</dd>258 </dl>259 </div>260 </div>261 </aside>262 </main>263264 <nav class="navbtns">265 <a href="90.html">◀ NIP-90: Data Vending Machine</a>266 <a href="94.html">NIP-94: File Metadata ▶</a>267 </nav>268269 <footer class="pagefoot">270 <a href="../index.html">Nostr Protocol Manual</a>271 <span class="pageno">NIP 92 / F4</span>272 </footer>273</div>274275<script src="../assets/webmcp.js" defer></script>276</body>277</html>