clone

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

main
nip.guide / site / nips / 66.html
66.html 22.1K
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-66 - Relay Liveness Monitoring — Nostr Instruction Manual</title>7<meta name="description" content="NIP-66: relay discovery events (kind 30166) and relay monitor announcements (kind 10166). How monitors probe relays and publish liveness data.">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 66 - Relay Ops</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="66">66</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 66</div>50 <h1>Relay Liveness Monitoring</h1>51 <p class="sub">Monitors probe relays, measure them, and publish the results as events. Clients read those events to discover relays that actually work.</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 relay can be slow, offline, or different from what it advertises. Its <a href="11.html">NIP-11</a> document describes its policy, but nothing checks that the document is true. This NIP adds an outside observer: the <strong>monitor</strong>.</p>60 <p>A monitor is a normal Nostr identity that probes relays. It measures response times, checks supported NIPs, and records network type and location. It publishes one <strong>relay discovery event</strong> (kind <code>30166</code>) per relay. Clients query these events to find live relays that match their needs.</p>61 <p>A monitor may also publish a <strong>relay monitor announcement</strong> (kind <code>10166</code>). This event advertises the monitor's intent to publish kind <code>30166</code> events at a regular, predictable frequency. It also lists the checks the monitor performs.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>Think of a monitor as a weather station for relays. It does not run the relay. It only reports the conditions it observed. Anyone can run one, so read the forecast from more than one station.</p>66 </div>6768 <figure class="diagram">69 <div class="frame">70 <svg viewBox="0 0 560 200" role="img" aria-label="A monitor probes two relays, publishes kind 30166 discovery events to an index relay, and a client queries them">71 <rect class="d-actor" x="10" y="20" width="110" height="44" rx="8"/>72 <text class="d-actor-label" x="65" y="47" text-anchor="middle">Relay A</text>73 <rect class="d-actor" x="10" y="130" width="110" height="44" rx="8"/>74 <text class="d-actor-label" x="65" y="157" text-anchor="middle">Relay B</text>75 <rect class="d-actor" x="225" y="75" width="110" height="50" rx="8"/>76 <text class="d-actor-label" x="280" y="97" text-anchor="middle">Monitor</text>77 <text class="d-note" x="280" y="114" text-anchor="middle">probes + signs</text>78 <rect class="d-actor" x="440" y="20" width="110" height="44" rx="8"/>79 <text class="d-actor-label" x="495" y="47" text-anchor="middle">Index relay</text>80 <rect class="d-actor" x="440" y="130" width="110" height="44" rx="8"/>81 <text class="d-actor-label" x="495" y="157" text-anchor="middle">Client</text>82 <path class="d-msg d-flow" d="M223,90 C180,80 165,60 122,48"/>83 <path class="d-msg d-flow" d="M223,110 C180,120 165,140 122,152"/>84 <path class="d-msg blue d-flow" d="M337,90 C380,80 395,60 438,48"/>85 <path class="d-msg blue d-flow" d="M495,66 L495,127"/>86 <text class="d-label" x="168" y="34" text-anchor="middle">probe</text>87 <text class="d-label" x="392" y="34" text-anchor="middle">30166</text>88 <text class="d-label" x="530" y="100">REQ</text>89 <circle r="4" fill="#f26a00">90 <animateMotion dur="2.6s" repeatCount="indefinite" path="M223,90 C180,80 165,60 122,48"/>91 </circle>92 <circle r="4" fill="#2e5f9e">93 <animateMotion dur="2.6s" begin="1.3s" repeatCount="indefinite" path="M337,90 C380,80 395,60 438,48"/>94 </circle>95 </svg>96 </div>97 <figcaption>Fig. 66-1 — <b>The monitoring loop.</b> The monitor probes relays, publishes one kind 30166 event per relay, and clients query the results.</figcaption>98 </figure>99100 <h2 class="bar" id="spec"><span class="num">2</span> Relay Discovery Events - Kind 30166</h2>101 <p>A kind <code>30166</code> event documents the characteristics of one relay. The monitor infers them from the relay's <a href="11.html">NIP-11</a> document, from probing, or from both. It is an addressable event under the <a href="01.html">NIP-01</a> storage rules: a relay keeps only the latest event per monitor pubkey and <code>d</code> value.</p>102 <p>The tags may contradict the relay's own NIP-11 document. That is intentional. A monitor reports the policy it observed, not the policy the relay advertises.</p>103 <p>The <code>content</code> field may hold the stringified JSON of the relay's NIP-11 informational document. It may also be empty.</p>104105 <div class="crt">106 <div class="titlebar">Wire format - kind 30166 relay discovery event</div>107<pre>{108 <span class="k">"kind"</span>: 30166,109 <span class="k">"pubkey"</span>: <span class="c">&lt;monitor's pubkey&gt;</span>,110 <span class="k">"content"</span>: <span class="c">&lt;optional stringified NIP-11 document&gt;</span>,111 <span class="k">"tags"</span>: [112 [<span class="s">"d"</span>, <span class="s">"wss://some.relay/"</span>], <span class="c">&lt;- required, normalized URL</span>113 [<span class="s">"n"</span>, <span class="s">"clearnet"</span>],114 [<span class="s">"N"</span>, <span class="s">"40"</span>], [<span class="s">"N"</span>, <span class="s">"33"</span>],115 [<span class="s">"R"</span>, <span class="s">"!payment"</span>], [<span class="s">"R"</span>, <span class="s">"auth"</span>],116 [<span class="s">"g"</span>, <span class="s">"ww8p1r4t8"</span>],117 [<span class="s">"t"</span>, <span class="s">"nsfw"</span>],118 [<span class="s">"rtt-open"</span>, <span class="s">"234"</span>]119 ]120}</pre>121 </div>122123 <p>The only required tag is <code>d</code>. Set it to the relay's URL, normalized per RFC 3986 §6. For a relay that has no URL, a hex-encoded pubkey may stand in as the <code>d</code> value instead.</p>124125 <div class="tscroll"><table class="ref">126 <caption>Kind 30166 tags</caption>127 <tr><th>Tag</th><th>Value</th><th>Rule</th></tr>128 <tr><td class="btn">d</td><td>normalized relay URL</td><td>Required. Identifies the relay. A hex pubkey may replace the URL for relays without one.</td></tr>129 <tr><td class="btn">rtt-open</td><td>milliseconds</td><td>The relay's open round-trip time.</td></tr>130 <tr><td class="btn">rtt-read</td><td>milliseconds</td><td>The relay's read round-trip time.</td></tr>131 <tr><td class="btn">rtt-write</td><td>milliseconds</td><td>The relay's write round-trip time.</td></tr>132 <tr><td class="btn">n</td><td>network type</td><td>Should be one of <code>clearnet</code>, <code>tor</code>, <code>i2p</code>, <code>loki</code>.</td></tr>133 <tr><td class="btn">T</td><td>relay type</td><td>An enumerated relay type in <code>PascalCase</code>, for example <code>PrivateInbox</code>.</td></tr>134 <tr><td class="btn">N</td><td>NIP number</td><td>A NIP the relay supports. Repeat the tag for each NIP.</td></tr>135 <tr><td class="btn">R</td><td>requirement key</td><td>A key from the <a href="11.html">NIP-11</a> <code>limitations</code> object: <code>auth</code>, <code>writes</code>, <code>pow</code>, <code>payment</code>. Prefix false values with <code>!</code>, for example <code>!auth</code>.</td></tr>136 <tr><td class="btn">t</td><td>topic</td><td>A topic associated with the relay.</td></tr>137 <tr><td class="btn">k</td><td>kind number</td><td>An accepted kind. Prefix unaccepted kinds with <code>!</code>.</td></tr>138 <tr><td class="btn">g</td><td>geohash</td><td>A <a href="52.html">NIP-52</a> geohash for the relay's location.</td></tr>139 </table></div>140141 <div class="box warn">142 <span class="tag">Warning!</span>143 <p>One value per tag. When a tag has several values, repeat the tag: <code>[["t","cats"],["t","dogs"]]</code>. Do not pack values into one tag like <code>[["t","cats","dogs"]]</code>. Relays index only the first value, so packed values become unqueryable.</p>144 </div>145146 <div class="box tech">147 <span class="tag">Tech Note</span>148 <p>Two loose ends in the source spec. First, its example event carries an <code>["l","en","ISO-639-1"]</code> language tag that the tag list never defines. Second, the <code>T</code> relay-type enumeration lives in a GitHub issue (nostr-protocol/nips #1282), not in any NIP, so there is no normative list of relay types.</p>149 </div>150151 <h2 class="bar" id="monitors"><span class="num">3</span> Relay Monitor Announcements - Kind 10166</h2>152 <p>A kind <code>10166</code> event advertises the author's intent to publish kind <code>30166</code> events. It is optional. Publish it when the monitor provides monitoring at a regular and predictable frequency. As a replaceable event, one announcement per monitor pubkey survives.</p>153154 <div class="tscroll"><table class="ref">155 <caption>Kind 10166 tags</caption>156 <tr><th>Tag</th><th>Value</th><th>Rule</th></tr>157 <tr><td class="btn">frequency</td><td>seconds</td><td>How often the monitor publishes events.</td></tr>158 <tr><td class="btn">timeout</td><td>milliseconds, check</td><td>Optional. Index 1 is the monitor's timeout in milliseconds. Index 2 names the test it applies to. Without index 2, the timeout applies to all tests.</td></tr>159 <tr><td class="btn">c</td><td>check name</td><td>A lowercase string naming one check the monitor conducts. Examples: <code>open</code>, <code>read</code>, <code>write</code>, <code>auth</code>, <code>nip11</code>, <code>dns</code>, <code>geo</code>.</td></tr>160 <tr><td class="btn">g</td><td>geohash</td><td>A <a href="52.html">NIP-52</a> geohash tag for the monitor's location.</td></tr>161 </table></div>162163 <p>A monitor should also publish a <code>kind 0</code> profile (<a href="01.html">NIP-01</a>) and a <code>kind 10002</code> relay selections event (<a href="65.html">NIP-65</a>). Together they tell clients who the monitor is and where its events live.</p>164165 <div class="crt">166 <div class="titlebar">Wire format - kind 10166 monitor announcement</div>167<pre>{168 <span class="k">"kind"</span>: 10166,169 <span class="k">"pubkey"</span>: <span class="c">&lt;monitor's pubkey&gt;</span>,170 <span class="k">"content"</span>: <span class="s">""</span>,171 <span class="k">"tags"</span>: [172 [<span class="s">"timeout"</span>, <span class="s">"5000"</span>, <span class="s">"open"</span>],173 [<span class="s">"timeout"</span>, <span class="s">"3000"</span>, <span class="s">"read"</span>],174 [<span class="s">"timeout"</span>, <span class="s">"3000"</span>, <span class="s">"write"</span>],175 [<span class="s">"frequency"</span>, <span class="s">"3600"</span>],176 [<span class="s">"c"</span>, <span class="s">"open"</span>], [<span class="s">"c"</span>, <span class="s">"nip11"</span>],177 [<span class="s">"c"</span>, <span class="s">"dns"</span>], [<span class="s">"c"</span>, <span class="s">"geo"</span>],178 [<span class="s">"g"</span>, <span class="s">"ww8p1r4t8"</span>]179 ]180}</pre>181 </div>182183 <div class="box tech">184 <span class="tag">Tech Note</span>185 <p>The source spec's own kind 10166 example contradicts its prose. The prose puts milliseconds at index 1 and the check name at index 2, but the example writes <code>["timeout","open","5000"]</code> - reversed. The example is also invalid JSON (a missing comma before the <code>g</code> tag), omits the <code>kind</code> field, uses a <code>"signature"</code> field instead of <code>"sig"</code>, and lists checks <code>ws</code> and <code>ssl</code> that the prose never names. The block above follows the prose.</p>186 </div>187188 <h2 class="bar" id="risk"><span class="num">4</span> Risk Mitigation</h2>189 <p>Monitoring data is advice, not authority. The spec sets three defensive rules:</p>190 <ul>191 <li>Clients must not require kind <code>30166</code> events to function. Absence of monitoring data must not prevent relay connections.</li>192 <li>A monitor may publish erroneous kind <code>30166</code> events, through misconfiguration or malicious intent. Plan for bad data.</li>193 <li>Clients should not trust a single source. Defenses include web-of-trust filtering, querying multiple monitors, and discarding filter results that would remove an unreasonable proportion of relays.</li>194 </ul>195196 <div class="box danger">197 <span class="tag">Warning!</span>198 <p>A hostile monitor can make good relays look dead and bad relays look healthy. If one monitor's data would cut most of your relay list, throw that data away, not your relays.</p>199 </div>200201 <figure class="diagram">202 <div class="frame">203 <svg viewBox="0 0 560 180" role="img" aria-label="A client cross-checks three monitors and keeps only relay reports that agree">204 <rect class="d-actor" x="10" y="12" width="120" height="40" rx="8"/>205 <text class="d-actor-label" x="70" y="37" text-anchor="middle">Monitor 1</text>206 <rect class="d-actor" x="10" y="70" width="120" height="40" rx="8"/>207 <text class="d-actor-label" x="70" y="95" text-anchor="middle">Monitor 2</text>208 <rect class="d-actor" x="10" y="128" width="120" height="40" rx="8"/>209 <text class="d-actor-label" x="70" y="153" text-anchor="middle">Monitor 3</text>210 <rect class="d-actor" x="240" y="65" width="130" height="50" rx="8"/>211 <text class="d-actor-label" x="305" y="87" text-anchor="middle">Client</text>212 <text class="d-note" x="305" y="104" text-anchor="middle">cross-check</text>213 <rect class="d-actor" x="440" y="65" width="110" height="50" rx="8"/>214 <text class="d-actor-label" x="495" y="87" text-anchor="middle">Relay list</text>215 <text class="d-note" x="495" y="104" text-anchor="middle">majority view</text>216 <path class="d-msg blue d-flow" d="M132,32 C185,42 190,62 237,76"/>217 <path class="d-msg blue d-flow" d="M132,90 L237,90"/>218 <path class="d-msg blue d-flow" d="M132,148 C185,138 190,118 237,104"/>219 <path class="d-msg d-flow" d="M372,90 L437,90"/>220 <text class="d-label" x="185" y="70" text-anchor="middle">30166 events</text>221 <text class="d-label" x="404" y="80" text-anchor="middle">keep</text>222 </svg>223 </div>224 <figcaption>Fig. 66-2 — <b>Do not trust one station.</b> The client compares reports from several monitors before it drops any relay.</figcaption>225 </figure>226227 <h2 class="bar" id="examples"><span class="num">5</span> Worked Example</h2>228 <p>A monitor probes <code>wss://some.relay/</code> and publishes its findings. Later, a client asks an index relay for fast clearnet relays. Orange lines go to the relay. Blue lines come from the relay.</p>229 <div class="crt">230 <div class="titlebar">Session transcript - monitor publishes, client discovers</div>231<pre><span class="c">-- monitor publishes one discovery event per probed relay --</span>232<span class="send">→ ["EVENT", {"kind":30166,"pubkey":"a3f1c2d4…","created_at":1749762600,233 "content":"{\"name\":\"Some Relay\",\"limitation\":{\"auth_required\":true}}",234 "tags":[["d","wss://some.relay/"],["n","clearnet"],["N","40"],["N","33"],235 ["R","auth"],["R","!payment"],["g","ww8p1r4t8"],["t","nsfw"],236 ["rtt-open","234"],["rtt-read","112"],["rtt-write","198"]],237 "id":"7cc21e04…","sig":"9b41aa07…"}]</span>238<span class="recv">← ["OK", "7cc21e04…", true, ""]</span>239240<span class="c">-- a client looks for live clearnet relays seen by this monitor --</span>241<span class="send">→ ["REQ", "relays", {"kinds":[30166], "authors":["a3f1c2d4…"],242 "#n":["clearnet"], "since":1749759000}]</span>243<span class="recv">← ["EVENT", "relays", {"kind":30166, "tags":[["d","wss://some.relay/"], …], …}]</span>244<span class="recv">← ["EOSE", "relays"]</span>245<span class="send">→ ["CLOSE", "relays"]</span></pre>246 </div>247 <p>The <code>since</code> filter is the liveness test. A monitor with <code>frequency</code> 3600 republishes every hour. A relay with no fresh kind <code>30166</code> event was down when the monitor last checked, or the monitor itself stopped.</p>248249 <h2 class="bar" id="history"><span class="num">6</span> Version History</h2>250 <p>NIP-66 circulated as a draft for years before it merged in 2025 as "Draft 7". Notable changes, newest first:</p>251 <ul class="patchnotes">252 <li><span class="ver">2026</span><span class="when">2026-05</span>Sanity fixes to the spec text by fiatjaf.</li>253 <li><span class="ver">2026</span><span class="when">2026-03</span>Added the Risk Mitigation section: clients must not depend on monitoring data or trust a single monitor.</li>254 <li><span class="ver">2025</span><span class="when">2025-12</span>Added the relay-type tag material; fixed typos and broken links.</li>255 <li><span class="ver">2025</span><span class="when">2025-10</span>Tag values became strings only - integers removed from tag examples.</li>256 <li><span class="ver">2025</span><span class="when">2025-09</span>Fixed fundamentally incorrect assertions in the spec text.</li>257 <li><span class="ver">2025</span><span class="when">2025-08</span>General cleanup pass by hodlbod.</li>258 <li class="minor"><span class="ver">2025</span><span class="when">2025-03/04</span>Title, description, and terminology fixes ("addressable event").</li>259 <li><span class="ver">Origin</span><span class="when">2025-03</span>Sandwich merged "Relay Discovery and Liveness Monitoring (Draft 7)" after a long review in PR #230.</li>260 </ul>261262 <div class="credits">263 <h3 class="cardhead">Original Proposal - Credits</h3>264 <table>265 <tr><td>Author</td><td>Sandwich</td></tr>266 <tr><td>First commit</td><td>2025-03-03 — “NIP-66 Relay Discovery and Liveness Monitoring (Draft 7)” - <a href="https://github.com/nostr-protocol/nips/pull/230">PR #230</a></td></tr>267 <tr><td>Status</td><td>draft - optional — for monitors and the clients that read them</td></tr>268 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/66.md">66.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/66.md">full change history</a></td></tr>269 </table>270 </div>271272 <h2 class="bar"><span class="num">7</span> See Also</h2>273 <ul>274 <li><a href="01.html">NIP-01</a> — the event format, addressable and replaceable storage, and the <code>kind 0</code> profile monitors should publish.</li>275 <li><a href="11.html">NIP-11</a> — the relay information document that kind <code>30166</code> events summarize and verify.</li>276 <li><a href="65.html">NIP-65</a> — the <code>kind 10002</code> relay selections event monitors should publish.</li>277 <li><a href="52.html">NIP-52</a> — defines the geohash format used by the <code>g</code> tag.</li>278 </ul>279 </div>280281 <aside>282 <div class="cartridge">283 <div class="inner">284 <h3 class="cardhead">Spec Card</h3>285 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>286 <dl>287 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip optional">optional</span></dd>288 <dt>Defines</dt><dd>relay discovery event - monitor announcement - rtt/n/T/N/R/t/k/g tags</dd>289 <dt>Kinds</dt><dd>30166 (addressable) - 10166 (replaceable)</dd>290 <dt>Requires</dt><dd><a href="01.html">NIP-01</a> - <a href="11.html">NIP-11</a></dd>291 <dt>Audience</dt><dd>monitors - relay-picking clients</dd>292 <dt>First commit</dt><dd>2025-03-03</dd>293 <dt>Last updated</dt><dd>2026-05-28</dd>294 </dl>295 </div>296 </div>297 </aside>298 </main>299300 <nav class="navbtns">301 <a href="65.html">◀ NIP-65: Relay List Metadata</a>302 <a href="67.html">NIP-67: EOSE Completeness Hint ▶</a>303 </nav>304305 <footer class="pagefoot">306 <a href="../index.html">Nostr Protocol Manual</a>307 <span class="pageno">NIP 66 / F4</span>308 </footer>309</div>310311<script src="../assets/webmcp.js" defer></script>312</body>313</html>