clone

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

main
nip.guide / site / nips / 38.html
38.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-38 - User Statuses — Nostr Instruction Manual</title>7<meta name="description" content="NIP-38: user statuses. Kind 30315 addressable events for live status lines - general activity, now playing music, and custom types. 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 38 - Social</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="38">38</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 38</div>50 <h1>User Statuses</h1>51 <p class="sub">A one-line live status next to your name: what you do right now, or what track you listen to.</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>Instant messengers of the 2000s had a status line: "away", "busy", "listening to...". This NIP brings that feature to Nostr. A user publishes a short status event. Clients show the status next to the username on posts and profiles.</p>60 <p>A status is an <strong>addressable event</strong> of <code>kind:30315</code>, as defined in <a href="01.html">NIP-01</a>. The <code>d</code> tag holds the <strong>status type</strong>. A relay keeps only the latest event per pubkey, kind, and <code>d</code> value. A new status therefore replaces the old one of the same type automatically.</p>61 <p>A status can expire on its own. The event is <em>optionally expiring</em>: add an <code>expiration</code> tag (see <a href="40.html">NIP-40</a>) and the status goes stale at that timestamp. A music status expires when the track ends. No cleanup event is needed.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>Think of each <code>d</code> value as one status slot. The <code>general</code> slot and the <code>music</code> slot live side by side. Updating one slot does not touch the other.</p>66 </div>6768 <figure class="diagram">69 <div class="frame">70 <svg viewBox="0 0 560 200" role="img" aria-label="A user publishes kind 30315 status events into per-type slots on the relay; other clients read the latest status and show it next to the username">71 <rect class="d-actor" x="10" y="75" width="110" height="50" rx="8"/>72 <text class="d-actor-label" x="65" y="97" text-anchor="middle">Client A</text>73 <text class="d-note" x="65" y="114" text-anchor="middle">sets status</text>74 <rect class="d-actor" x="225" y="20" width="130" height="44" rx="8"/>75 <text class="d-actor-label" x="290" y="42" text-anchor="middle">Relay slot</text>76 <text class="d-note" x="290" y="57" text-anchor="middle">d = "general"</text>77 <rect class="d-actor" x="225" y="136" width="130" height="44" rx="8"/>78 <text class="d-actor-label" x="290" y="158" text-anchor="middle">Relay slot</text>79 <text class="d-note" x="290" y="173" text-anchor="middle">d = "music"</text>80 <rect class="d-actor" x="440" y="75" width="110" height="50" rx="8"/>81 <text class="d-actor-label" x="495" y="97" text-anchor="middle">Client B</text>82 <text class="d-note" x="495" y="114" text-anchor="middle">shows status</text>83 <path class="d-msg d-flow" d="M122,88 C170,74 180,56 223,46"/>84 <path class="d-msg d-flow" d="M122,112 C170,126 180,144 223,154"/>85 <path class="d-msg blue d-flow" d="M357,46 C400,56 410,74 438,88"/>86 <path class="d-msg blue d-flow" d="M357,154 C400,144 410,126 438,112"/>87 <text class="d-label" x="160" y="48" text-anchor="middle">30315</text>88 <text class="d-label" x="415" y="48" text-anchor="middle">latest only</text>89 <circle r="4" fill="#f26a00">90 <animateMotion dur="2.6s" repeatCount="indefinite" path="M122,88 C170,74 180,56 223,46"/>91 </circle>92 <circle r="4" fill="#2e5f9e">93 <animateMotion dur="2.6s" begin="1.3s" repeatCount="indefinite" path="M357,46 C400,56 410,74 438,88"/>94 </circle>95 </svg>96 </div>97 <figcaption>Fig. 38-1 — <b>Status slots.</b> Each <code>d</code> value is a slot. The relay keeps the latest event per slot. Readers always get the current status.</figcaption>98 </figure>99100 <h2 class="bar" id="spec"><span class="num">2</span> The Status Event (kind 30315)</h2>101 <p>The event named "User Status" uses <code>kind:30315</code>. The <code>content</code> is the status text. The <code>d</code> tag names the status type.</p>102103 <div class="crt">104 <div class="titlebar">Wire format - kind 30315 user status</div>105<pre>{106 <span class="k">"kind"</span>: 30315,107 <span class="k">"content"</span>: <span class="c">&lt;status text, may contain emoji&gt;</span>,108 <span class="k">"tags"</span>: [109 [<span class="s">"d"</span>, <span class="c">&lt;status type, e.g. "general" or "music"&gt;</span>],110 [<span class="s">"r"</span>, <span class="c">&lt;URL, optional&gt;</span>],111 [<span class="s">"expiration"</span>, <span class="c">&lt;unix timestamp, optional&gt;</span>]112 ]113}</pre>114 </div>115116 <div class="tscroll"><table class="ref">117 <caption>Tag reference</caption>118 <tr><th>Tag</th><th>Required</th><th>Rule</th></tr>119 <tr><td class="btn">d</td><td>yes</td><td>The status type. Selects the addressable slot per <a href="01.html">NIP-01</a>.</td></tr>120 <tr><td class="btn">r</td><td>optional</td><td>Links the status to a URL.</td></tr>121 <tr><td class="btn">p</td><td>optional</td><td>Links the status to a profile (pubkey).</td></tr>122 <tr><td class="btn">e</td><td>optional</td><td>Links the status to a note (event id).</td></tr>123 <tr><td class="btn">a</td><td>optional</td><td>Links the status to an addressable event.</td></tr>124 <tr><td class="btn">expiration</td><td>optional</td><td>Unix timestamp when the status stops being valid. See <a href="40.html">NIP-40</a>.</td></tr>125 </table></div>126127 <p>A status MAY include one <code>r</code>, <code>p</code>, <code>e</code>, or <code>a</code> tag. The tag turns the status into a link. Example: a "in a meeting" status can carry an <code>r</code> tag with the meeting URL.</p>128129 <h3>Status types</h3>130 <p>The <code>d</code> tag value is the status type. This NIP defines two common types:</p>131 <div class="tscroll"><table class="ref">132 <caption>Defined status types (d tag values)</caption>133 <tr><th>Type</th><th>Meaning</th><th>Expiry</th></tr>134 <tr><td class="btn">general</td><td>A general status: "Working", "Hiking", "Out of office".</td><td>Optional.</td></tr>135 <tr><td class="btn">music</td><td>The track the user listens to right now. A live "now playing" stream.</td><td>Set to the time when the track stops playing.</td></tr>136 </table></div>137 <p>Clients can use any other <code>d</code> value as a custom status type. This NIP does not define those types.</p>138139 <h3>Content rules</h3>140 <ul>141 <li>The <code>content</code> is the status text.</li>142 <li>The <code>content</code> MAY include emoji, or <a href="30.html">NIP-30</a> custom emoji.</li>143 <li>An empty <code>content</code> string clears the status. The client should stop showing it.</li>144 </ul>145146 <div class="box tech">147 <span class="tag">Tech Note</span>148 <p>Source issues, reported as found. (1) Both JSON examples in the source have a trailing comma after the <code>tags</code> array, so they are not valid JSON as printed. (2) The music example spells the Beastie Boys track as "Intergalatic" instead of "Intergalactic". (3) The source says the event is "optionally expiring" and uses an <code>expiration</code> tag but never cites <a href="40.html">NIP-40</a>, which defines that tag.</p>149 </div>150151 <h2 class="bar" id="lifecycle"><span class="num">3</span> Status Lifecycle</h2>152 <p>A status has three exits. A newer event with the same <code>d</code> value replaces it. Its <code>expiration</code> timestamp passes. Or an event with empty <code>content</code> clears it.</p>153154 <figure class="diagram">155 <div class="frame">156 <svg viewBox="0 0 560 150" role="img" aria-label="State machine: a published status is live, then it is replaced by a newer event, expired by the expiration tag, or cleared by empty content">157 <rect class="d-actor" x="20" y="50" width="120" height="50" rx="8"/>158 <text class="d-actor-label" x="80" y="79" text-anchor="middle">LIVE</text>159 <rect class="d-actor" x="420" y="10" width="120" height="38" rx="8"/>160 <text class="d-actor-label" x="480" y="34" text-anchor="middle">Replaced</text>161 <rect class="d-actor" x="420" y="58" width="120" height="38" rx="8"/>162 <text class="d-actor-label" x="480" y="82" text-anchor="middle">Expired</text>163 <rect class="d-actor" x="420" y="106" width="120" height="38" rx="8"/>164 <text class="d-actor-label" x="480" y="130" text-anchor="middle">Cleared</text>165 <path class="d-msg" d="M142,62 C250,40 320,32 417,29"/>166 <path class="d-msg" d="M142,77 L417,77"/>167 <path class="d-msg" d="M142,92 C250,114 320,122 417,125"/>168 <text class="d-label" x="280" y="24" text-anchor="middle">newer event, same d</text>169 <text class="d-label" x="280" y="70" text-anchor="middle">expiration timestamp passes</text>170 <text class="d-label" x="280" y="140" text-anchor="middle">event with empty content</text>171 </svg>172 </div>173 <figcaption>Fig. 38-2 — <b>Three ways out.</b> A live status ends by replacement, by expiry, or by an explicit clear.</figcaption>174 </figure>175176 <h2 class="bar" id="clients"><span class="num">4</span> Client Behavior</h2>177 <p>Clients MAY display the status next to the username on posts or on profiles. The status gives live user information at a glance.</p>178 <p>To read statuses, subscribe with a filter on <code>kind:30315</code> and the authors you follow. Ignore statuses whose <code>expiration</code> timestamp is in the past. Ignore statuses with empty <code>content</code>.</p>179 <p>The source lists these use cases:</p>180 <ul>181 <li>A calendar app updates your <code>general</code> status when you are in a meeting.</li>182 <li>Nostr Nests updates your <code>general</code> status with a link to the nest when you join.</li>183 <li>A music streaming service updates your <code>music</code> status while you listen.</li>184 <li>A podcast app updates your <code>music</code> status with a link so others can listen too.</li>185 <li>A client reads the system media player and updates the <code>music</code> status.</li>186 </ul>187188 <div class="box warn">189 <span class="tag">Warning!</span>190 <p>Expiry is cooperative. A relay that implements <a href="40.html">NIP-40</a> can drop expired statuses, but clients must still check the <code>expiration</code> tag themselves. Do not show a "now playing" line from last week.</p>191 </div>192193 <h2 class="bar" id="examples"><span class="num">5</span> Worked Example</h2>194 <p>Alice sets a general status with a link, streams a track as her music status, and later clears the general status. The transcript shows the full session. Orange lines go to the relay. Blue lines come from the relay.</p>195196 <div class="crt">197 <div class="titlebar">Session transcript - wss://relay.example.com</div>198<pre><span class="c">-- Alice sets her general status --</span>199<span class="send">→ ["EVENT", {"kind":30315, "pubkey":"a1ce55...", "created_at":1692840000,200 "content":"Sign up for nostrasia!",201 "tags":[["d","general"], ["r","https://nostr.world"]],202 "id":"7d1f04...", "sig":"9b21ac..."}]</span>203<span class="recv">← ["OK", "7d1f04...", true, ""]</span>204205<span class="c">-- Alice's player sets a music status that expires with the track --</span>206<span class="send">→ ["EVENT", {"kind":30315, "pubkey":"a1ce55...", "created_at":1692845400,207 "content":"Intergalatic - Beastie Boys",208 "tags":[["d","music"],209 ["r","spotify:search:Intergalatic%20-%20Beastie%20Boys"],210 ["expiration","1692845589"]],211 "id":"c04b9e...", "sig":"31d7f0..."}]</span>212<span class="recv">← ["OK", "c04b9e...", true, ""]</span>213214<span class="c">-- Bob's client fetches Alice's statuses --</span>215<span class="send">→ ["REQ", "status", {"kinds":[30315], "authors":["a1ce55..."]}]</span>216<span class="recv">← ["EVENT", "status", {"kind":30315, "content":"Sign up for nostrasia!", ...}]</span>217<span class="recv">← ["EVENT", "status", {"kind":30315, "content":"Intergalatic - Beastie Boys", ...}]</span>218<span class="recv">← ["EOSE", "status"]</span>219220<span class="c">-- Later Alice clears the general slot: empty content --</span>221<span class="send">→ ["EVENT", {"kind":30315, "pubkey":"a1ce55...", "created_at":1692850000,222 "content":"", "tags":[["d","general"]],223 "id":"e83a11...", "sig":"04c2b8..."}]</span>224<span class="recv">← ["OK", "e83a11...", true, ""]</span>225<span class="c">-- Bob's client sees the empty content and stops showing the status --</span></pre>226 </div>227228 <h2 class="bar" id="history"><span class="num">6</span> Version History</h2>229 <p>NIP-38 started life two days early as "NIP-315", named after its kind number. Notable changes, newest first:</p>230 <ul class="patchnotes">231 <li><span class="ver">2025</span><span class="when">2025-08</span>Fixed heading levels in the document.</li>232 <li><span class="ver">2024</span><span class="when">2024-08</span>Renamed "parameterized replaceable event" to "addressable event" across the repository, following the <a href="01.html">NIP-01</a> terminology change.</li>233 <li><span class="ver">2024</span><span class="when">2024-05</span>Moved the content rules (emoji, empty string clears the status) into the Live Statuses section.</li>234 <li class="minor"><span class="ver">2023-24</span><span class="when">2023-2024</span>Removed the author list; JSON and header formatting cleanups.</li>235 <li><span class="ver">2023</span><span class="when">2023-08</span>Added the content description: emoji and <a href="30.html">NIP-30</a> custom emoji allowed, empty content clears the status.</li>236 <li><span class="ver">2023</span><span class="when">2023-08</span>Renamed NIP-315 to NIP-38.</li>237 <li><span class="ver">Origin</span><span class="when">2023-08-23</span>William Casarin published "NIP-315: User Statuses" with kind 30315.</li>238 </ul>239240 <div class="credits">241 <h3 class="cardhead">Original Proposal - Credits</h3>242 <table>243 <tr><td>Author</td><td>William Casarin</td></tr>244 <tr><td>First commit</td><td>2023-08-23 — “NIP-315: User Statuses”</td></tr>245 <tr><td>Status</td><td>draft - optional</td></tr>246 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/38.md">38.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/38.md">full change history</a></td></tr>247 </table>248 </div>249250 <h2 class="bar"><span class="num">7</span> See Also</h2>251 <ul>252 <li><a href="01.html">NIP-01</a> — addressable events, the <code>d</code> tag, and the <code>r</code>/<code>p</code>/<code>e</code>/<code>a</code> tag shapes this NIP builds on.</li>253 <li><a href="40.html">NIP-40</a> — the <code>expiration</code> tag that makes a status self-destruct.</li>254 <li><a href="30.html">NIP-30</a> — custom emoji allowed inside the status <code>content</code>.</li>255 <li><a href="24.html">NIP-24</a> — profile metadata; the static counterpart to a live status.</li>256 </ul>257 </div>258259 <aside>260 <div class="cartridge">261 <div class="inner">262 <h3 class="cardhead">Spec Card</h3>263 <div class="lights"><span class="led on"></span><span class="led warn"></span><span class="led warn"></span></div>264 <dl>265 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip optional">optional</span></dd>266 <dt>Defines</dt><dd>kind 30315 - status types general/music</dd>267 <dt>Kind(s)</dt><dd>30315 (addressable)</dd>268 <dt>Requires</dt><dd><a href="01.html">NIP-01</a> - <a href="40.html">NIP-40</a> (expiry)</dd>269 <dt>First commit</dt><dd>2023-08-23</dd>270 <dt>Last updated</dt><dd>2025-08-13</dd>271 </dl>272 </div>273 </div>274 </aside>275 </main>276277 <nav class="navbtns">278 <a href="37.html">◀ NIP-37: Draft Wraps</a>279 <a href="39.html">NIP-39: Linking Profiles to Other Platforms ▶</a>280 </nav>281282 <footer class="pagefoot">283 <a href="../index.html">Nostr Protocol Manual</a>284 <span class="pageno">NIP 38 / F4</span>285 </footer>286</div>287288<script src="../assets/webmcp.js" defer></script>289</body>290</html>