clone

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

main
nip.guide / site / nips / 64.html
64.html 21.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-64 - Chess (Portable Game Notation) — Nostr Instruction Manual</title>7<meta name="description" content="NIP-64: kind 64 chess game events. PGN content format, export vs import mode, client rendering rules, and relay validation. 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 64 - Games</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="64">64</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 64</div>50 <h1>Chess (Portable Game Notation)</h1>51 <p class="sub">Publish complete chess games as kind-64 events. The content is standard PGN text that both humans and chess engines can read.</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>This NIP defines <strong>kind 64</strong> events that carry chess games. The <code>content</code> field holds text in <strong>Portable Game Notation</strong> (PGN). PGN is the standard interchange format for recorded chess games. Humans can read it. Most chess software can parse it.</p>60 <p>The event wrapper adds what PGN alone lacks: an author signature, a timestamp, and relay distribution. A chess client publishes a game once. Any subscribed client can then fetch the game, replay it, and render it on a chessboard.</p>61 <p>The NIP defines no new tags and no new messages. It assigns one kind number and points at the existing PGN standard for the payload. Everything else follows the base rules in <a href="01.html">NIP-01</a>.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>One kind-64 event can hold more than one game. PGN defines a <em>database</em>: a sequence of games in one text file. The <code>content</code> string represents exactly such a PGN database.</p>66 </div>6768 <figure class="diagram">69 <div class="frame">70 <svg viewBox="0 0 560 150" role="img" aria-label="A chess client publishes a kind 64 event with PGN content to a relay; a viewer client receives it and renders a chessboard">71 <rect class="d-actor" x="10" y="50" width="130" height="50" rx="8"/>72 <text class="d-actor-label" x="75" y="72" text-anchor="middle">Chess client</text>73 <text class="d-note" x="75" y="89" text-anchor="middle">writes PGN</text>74 <rect class="d-actor" x="225" y="50" width="110" height="50" rx="8"/>75 <text class="d-actor-label" x="280" y="79" text-anchor="middle">Relay</text>76 <rect class="d-actor" x="420" y="50" width="130" height="50" rx="8"/>77 <text class="d-actor-label" x="485" y="72" text-anchor="middle">Viewer client</text>78 <text class="d-note" x="485" y="89" text-anchor="middle">renders board</text>79 <path class="d-msg d-flow" d="M142,75 L222,75"/>80 <path class="d-msg blue d-flow" d="M337,75 L417,75"/>81 <text class="d-label" x="182" y="63" text-anchor="middle">kind 64</text>82 <text class="d-label" x="377" y="63" text-anchor="middle">EVENT</text>83 <circle r="4" fill="#f26a00">84 <animateMotion dur="2.6s" repeatCount="indefinite" path="M142,75 L222,75"/>85 </circle>86 <circle r="4" fill="#2e5f9e">87 <animateMotion dur="2.6s" begin="1.3s" repeatCount="indefinite" path="M337,75 L417,75"/>88 </circle>89 </svg>90 </div>91 <figcaption>Fig. 64-1 — <b>Game distribution.</b> One signed PGN event travels from the author's chess client through relays to any viewer.</figcaption>92 </figure>9394 <h2 class="bar" id="spec"><span class="num">2</span> The Kind 64 Event</h2>95 <p>A chess game event is a normal <a href="01.html">NIP-01</a> event with <code>kind</code> set to 64. The <code>content</code> is a string that represents a PGN database. The smallest valid event needs nothing else:</p>9697 <div class="crt">98 <div class="titlebar">Wire format - minimal chess event</div>99<pre>{100 <span class="k">"kind"</span>: 64,101 <span class="k">"content"</span>: <span class="s">"1. e4 *"</span>102 <span class="c">// other NIP-01 fields: id, pubkey, created_at, tags, sig</span>103}</pre>104 </div>105106 <div class="tscroll"><table class="ref">107 <caption>Kind 64 - field rules</caption>108 <tr><th>Field</th><th>Rule</th></tr>109 <tr><td class="btn">kind</td><td>Always <code>64</code>.</td></tr>110 <tr><td class="btn">content</td><td>A string that represents a PGN database: one or more games, each with optional tag pairs and a movetext section.</td></tr>111 <tr><td class="btn">tags</td><td>No required tags. Clients may add extra tags such as <span class="kbd">alt</span> from <a href="31.html">NIP-31</a>. See §3.</td></tr>112 </table></div>113114 <h3>PGN content in brief</h3>115 <p>A PGN game has two parts. First come optional <strong>tag pairs</strong> in square brackets, such as <code>[White "Fischer, Robert J."]</code>. The PGN standard names seven of them the <em>Seven Tag Roster</em>: Event, Site, Date, Round, White, Black, Result. Then comes the <strong>movetext</strong>: numbered moves, optional comments in braces, and a game termination marker. The markers are:</p>116 <div class="tscroll"><table class="ref">117 <caption>PGN game termination markers</caption>118 <tr><th>Marker</th><th>Meaning</th></tr>119 <tr><td class="btn">1-0</td><td>White wins.</td></tr>120 <tr><td class="btn">0-1</td><td>Black wins.</td></tr>121 <tr><td class="btn">1/2-1/2</td><td>Draw.</td></tr>122 <tr><td class="btn">*</td><td>Result unknown: the game is in progress, abandoned, or otherwise undecided.</td></tr>123 </table></div>124 <p>The full grammar lives in the PGN specification and its formal syntax section. This NIP does not restate it. It requires only that <code>content</code> parses as a PGN database.</p>125126 <div class="box tech">127 <span class="tag">Tech Note</span>128 <p>Kind 64 sits outside every storage-class range that <a href="01.html">NIP-01</a> names (regular is 1, 2, 4-44, and 1000-9999). The NIP does not state a storage class, so behavior is by convention: relays treat kind 64 as a regular stored event. Note also that the proposal originally used kind 30, inside the addressable range; the kind changed to 64 in February 2024.</p>129 </div>130131 <h2 class="bar" id="client"><span class="num">3</span> Client Behavior</h2>132 <p>The NIP gives clients three SHOULD rules and one MAY rule:</p>133 <div class="tscroll"><table class="ref">134 <caption>Client rules</caption>135 <tr><th>Level</th><th>Rule</th></tr>136 <tr><td class="btn">SHOULD</td><td>Display the <code>content</code> as a chessboard, not as raw text.</td></tr>137 <tr><td class="btn">SHOULD</td><td>Publish PGN in <em>export format</em> ("strict mode": machine-generated, canonical layout). Accept incoming PGN in <em>import format</em> ("lax mode": human-written, loose layout).</td></tr>138 <tr><td class="btn">SHOULD</td><td>Check that the PGN formatting is valid and that every move complies with the rules of chess.</td></tr>139 <tr><td class="btn">MAY</td><td>Add extra tags, for example an <span class="kbd">alt</span> tag (<a href="31.html">NIP-31</a>), so non-supporting clients can show a readable summary.</td></tr>140 </table></div>141142 <figure class="diagram">143 <div class="frame">144 <svg viewBox="0 0 560 160" role="img" aria-label="A client publishes strict export-format PGN but accepts lax import-format PGN from the network">145 <rect class="d-actor" x="200" y="55" width="160" height="50" rx="8"/>146 <text class="d-actor-label" x="280" y="77" text-anchor="middle">Client</text>147 <text class="d-note" x="280" y="94" text-anchor="middle">validate + render</text>148 <rect class="d-actor" x="10" y="55" width="120" height="50" rx="8"/>149 <text class="d-actor-label" x="70" y="84" text-anchor="middle">Network</text>150 <rect class="d-actor" x="430" y="55" width="120" height="50" rx="8"/>151 <text class="d-actor-label" x="490" y="84" text-anchor="middle">Relays</text>152 <path class="d-msg blue d-flow" d="M132,80 L197,80"/>153 <path class="d-msg d-flow" d="M362,80 L427,80"/>154 <text class="d-label" x="164" y="46" text-anchor="middle">IN: import format</text>155 <text class="d-note" x="164" y="126" text-anchor="middle">lax - human-made</text>156 <text class="d-label" x="395" y="46" text-anchor="middle">OUT: export format</text>157 <text class="d-note" x="395" y="126" text-anchor="middle">strict - machine-made</text>158 </svg>159 </div>160 <figcaption>Fig. 64-2 — <b>Strict out, lax in.</b> A client emits canonical export-format PGN but tolerates loose import-format PGN from others.</figcaption>161 </figure>162163 <div class="box warn">164 <span class="tag">Warning!</span>165 <p>Do not skip move validation. A signed event proves who published the game. It does not prove the moves are legal. Illegal movetext can crash naive board renderers or show a false position.</p>166 </div>167168 <h2 class="bar" id="relay"><span class="num">4</span> Relay Behavior</h2>169 <p>Relays MAY validate the PGN content of a kind-64 event. A relay that validates MAY reject an invalid event. Rejection follows <a href="01.html">NIP-01</a>: reply <code>["OK", &lt;event_id&gt;, false, "invalid: ..."]</code>. Relays are not required to understand PGN at all. Clients therefore cannot rely on relays to filter out broken games.</p>170171 <h2 class="bar" id="examples"><span class="num">5</span> Worked Examples</h2>172 <p>The NIP ships a ladder of valid PGN payloads, from empty to a full database. All of the following are valid <code>content</code> strings.</p>173174 <div class="crt">175 <div class="titlebar">PGN - the smallest games</div>176<pre><span class="c">-- Nothing is known. Game in progress, abandoned, or result</span>177<span class="c">-- otherwise unknown. Maybe players died before a move was made.</span>178*179180<span class="c">-- One move, result still open --</span>1811. e4 *182183<span class="c">-- Some tag pairs, a comment, no result yet --</span>184[White <span class="s">"Fischer, Robert J."</span>]185[Black <span class="s">"Spassky, Boris V."</span>]1861871. e4 e5 2. Nf3 Nc6 3. Bb5 {This opening is called the Ruy Lopez.} *</pre>188 </div>189190 <p>A complete historical game, with the Seven Tag Roster filled in, wrapped in a full event. The <span class="kbd">alt</span> tag gives non-chess clients a human-readable line:</p>191192 <div class="crt">193 <div class="titlebar">Event - Fischer vs. Spassky, 1992</div>194<pre>{195 <span class="k">"kind"</span>: 64,196 <span class="k">"tags"</span>: [197 [<span class="s">"alt"</span>, <span class="s">"Fischer vs. Spassky in Belgrade on 1992-11-04 (F/S Return Match, Round 29)"</span>]198 <span class="c">// rest of tags...</span>199 ],200 <span class="k">"content"</span>: <span class="s">"[Event \"F/S Return Match\"]\n[Site \"Belgrade, Serbia JUG\"]\n201 [Date \"1992.11.04\"]\n[Round \"29\"]\n[White \"Fischer, Robert J.\"]\n202 [Black \"Spassky, Boris V.\"]\n[Result \"1/2-1/2\"]\n\n203 1. e4 e5 2. Nf3 Nc6 3. Bb5 {This opening is called the Ruy Lopez.} 3... a6\n204 4. Ba4 Nf6 5. O-O Be7 6. Re1 b5 7. Bb3 d6 8. c3 O-O 9. h3 Nb8 10. d4 Nbd7\n205 11. c4 c6 12. cxb5 axb5 13. Nc3 Bb7 14. Bg5 b4 15. Nb1 h6 16. Bh4 c5 17. dxe5\n206 Nxe4 18. Bxe7 Qxe7 19. exd6 Qf6 20. Nbd2 Nxd6 21. Nc4 Nxc4 22. Bxc4 Nb6\n207 23. Ne5 Rae8 24. Bxf7+ Rxf7 25. Nxf7 Rxe1+ 26. Qxe1 Kxf7 27. Qe3 Qg5 28. Qxg5\n208 hxg5 29. b3 Ke6 30. a3 Kd6 31. axb4 cxb4 32. Ra5 Nd5 33. f3 Bc8 34. Kf2 Bf5\n209 35. Ra7 g6 36. Ra6+ Kc5 37. Ke1 Nf4 38. g3 Nxh3 39. Kd2 Kb5 40. Rd6 Kc5 41. Ra6\n210 Nf2 42. g4 Bd3 43. Re6 1/2-1/2"</span>211 <span class="c">// other fields...</span>212}</pre>213 </div>214 <div class="box tech">215 <span class="tag">Tech Note</span>216 <p>The real <code>content</code> is one long JSON string with <code>\n</code> escapes; the block above wraps lines for print only. The source NIP labels its event examples as YAML code fences, yet shows JSON with <code>//</code> comments. Treat them as illustrative JSON fragments, not literal wire bytes.</p>217 </div>218219 <p>Finally, a two-game database in one <code>content</code> string. Extra tag pairs beyond the Seven Tag Roster (Elo ratings, time control, ECO code, termination) are normal PGN and pass through unchanged:</p>220221 <div class="crt">222 <div class="titlebar">PGN database - two lichess games in one event</div>223<pre>[Event <span class="s">"Hourly HyperBullet Arena"</span>]224[Site <span class="s">"https://lichess.org/wxx4GldJ"</span>]225[Date <span class="s">"2017.04.01"</span>]226[White <span class="s">"T_LUKE"</span>]227[Black <span class="s">"decidement"</span>]228[Result <span class="s">"1-0"</span>]229[UTCDate <span class="s">"2017.04.01"</span>]230[UTCTime <span class="s">"11:56:14"</span>]231[WhiteElo <span class="s">"2047"</span>]232[BlackElo <span class="s">"1984"</span>]233[WhiteRatingDiff <span class="s">"+10"</span>]234[BlackRatingDiff <span class="s">"-7"</span>]235[Variant <span class="s">"Standard"</span>]236[TimeControl <span class="s">"30+0"</span>]237[ECO <span class="s">"B00"</span>]238[Termination <span class="s">"Abandoned"</span>]2392401. e4 1-0241242243[Event <span class="s">"Hourly HyperBullet Arena"</span>]244[Site <span class="s">"https://lichess.org/rospUdSk"</span>]245[Date <span class="s">"2017.04.01"</span>]246[White <span class="s">"Bastel"</span>]247[Black <span class="s">"oslochess"</span>]248[Result <span class="s">"1-0"</span>]249[UTCDate <span class="s">"2017.04.01"</span>]250[UTCTime <span class="s">"11:55:56"</span>]251[WhiteElo <span class="s">"2212"</span>]252[BlackElo <span class="s">"2000"</span>]253[WhiteRatingDiff <span class="s">"+6"</span>]254[BlackRatingDiff <span class="s">"-4"</span>]255[Variant <span class="s">"Standard"</span>]256[TimeControl <span class="s">"30+0"</span>]257[ECO <span class="s">"A01"</span>]258[Termination <span class="s">"Normal"</span>]2592601. b3 d5 2. Bb2 c6 3. Nc3 Bf5 4. d4 Nf6 5. e3 Nbd7 6. f4 Bg6 7. Nf3 Bh52618. Bd3 e6 9. O-O Be7 10. Qe1 O-O 11. Ne5 Bg6 12. Nxg6 hxg6 13. e4 dxe426214. Nxe4 Nxe4 15. Bxe4 Nf6 16. c4 Bd6 17. Bc2 Qc7 18. f5 Be7 19. fxe6 fxe626320. Qxe6+ Kh8 21. Qh3+ Kg8 22. Bxg6 Qd7 23. Qe3 Bd6 24. Bf5 Qe7 25. Be6+ Kh826426. Qh3+ Nh7 27. Bf5 Rf6 28. Qxh7# 1-0</pre>265 </div>266267 <p>The publish round trip itself is plain <a href="01.html">NIP-01</a>:</p>268 <div class="crt">269 <div class="titlebar">Session transcript - wss://relay.example.com</div>270<pre><span class="c">-- publish a game --</span>271<span class="send">→ ["EVENT", {"id":"3ac1f2b0…","kind":64,"content":"1. e4 *", …}]</span>272<span class="recv">← ["OK", "3ac1f2b0…", true, ""]</span>273274<span class="c">-- fetch games by an author --</span>275<span class="send">→ ["REQ", "chess", {"kinds":[64], "authors":["22a12a12…"]}]</span>276<span class="recv">← ["EVENT", "chess", {"id":"3ac1f2b0…", "kind":64, …}]</span>277<span class="recv">← ["EOSE", "chess"]</span>278279<span class="c">-- a validating relay rejects broken PGN --</span>280<span class="recv">← ["OK", "9f2c11ab…", false, "invalid: content is not a valid PGN database"]</span></pre>281 </div>282283 <h3>Resources</h3>284 <p>The normative payload grammar lives outside Nostr. The NIP links these references:</p>285 <ul>286 <li><a href="https://github.com/mliebelt/pgn-spec-commented/blob/main/pgn-specification.md">PGN Specification</a> — the Portable Game Notation standard.</li>287 <li><a href="https://github.com/mliebelt/pgn-spec-commented/blob/main/pgn-spec-supplement.md">PGN Specification Supplement</a> — additions for graphical elements, clock values, and evaluations.</li>288 <li><a href="https://github.com/mliebelt/pgn-spec-commented/blob/main/pgn-specification.md#18-formal-syntax">PGN Formal Syntax</a> — the grammar a validator implements.</li>289 <li><a href="https://github.com/mliebelt/pgn-spec-commented/blob/main/pgn-specification.md#811-seven-tag-roster">Seven Tag Roster</a> — the seven standard tag pairs.</li>290 <li><a href="https://github.com/mliebelt/pgn-spec-commented/blob/main/pgn-specification.md#31-import-format-allows-for-manually-prepared-data">Import Format</a> and <a href="https://github.com/mliebelt/pgn-spec-commented/blob/main/pgn-specification.md#32-export-format-used-for-program-generated-output">Export Format</a> — the lax and strict PGN modes.</li>291 <li><a href="https://github.com/lichess-org/pgn-viewer">lichess pgn-viewer</a> — an embeddable PGN board widget.</li>292 </ul>293294 <h2 class="bar" id="history"><span class="num">6</span> Version History</h2>295 <p>NIP-64 arrived late in 2023 and settled quickly. Notable changes, newest first:</p>296 <ul class="patchnotes">297 <li class="minor"><span class="ver">2026</span><span class="when">2026-06</span>Example code-fence labels switched from <code>jsonc</code> to <code>yaml</code> repo-wide.</li>298 <li class="minor"><span class="ver">2024-25</span><span class="when">2024-2025</span>Formatting cleanups: JSON style, header style, and cross-reference link fixes.</li>299 <li><span class="ver">2024</span><span class="when">2024-03</span>"Chess" added to the NIP title.</li>300 <li><span class="ver">2024</span><span class="when">2024-02</span>Kind changed from 30 to 64. FEN position tags removed; the spec narrowed to pure PGN content. Examples moved to the bottom.</li>301 <li><span class="ver">Origin</span><span class="when">2023-12</span>theborakompanioni proposed Portable Game Notation events.</li>302 </ul>303304 <div class="credits">305 <h3 class="cardhead">Original Proposal - Credits</h3>306 <table>307 <tr><td>Author</td><td>theborakompanioni</td></tr>308 <tr><td>First commit</td><td>2023-12-28 — “docs(NIP-64): Portable Game Notation”</td></tr>309 <tr><td>Status</td><td>draft - optional</td></tr>310 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/64.md">64.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/64.md">full change history</a></td></tr>311 </table>312 </div>313314 <h2 class="bar"><span class="num">7</span> See Also</h2>315 <ul>316 <li><a href="01.html">NIP-01</a> — the base event format, kinds, and the publish/subscribe flow used here.</li>317 <li><a href="31.html">NIP-31</a> — the <code>alt</code> tag: a readable summary for clients that cannot render a chessboard.</li>318 <li><a href="10.html">NIP-10</a> — text notes and threading, if players discuss a published game.</li>319 </ul>320 </div>321322 <aside>323 <div class="cartridge">324 <div class="inner">325 <h3 class="cardhead">Spec Card</h3>326 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>327 <dl>328 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip">optional</span></dd>329 <dt>Defines</dt><dd>kind 64 - chess game notes</dd>330 <dt>Kind(s)</dt><dd>64</dd>331 <dt>Content</dt><dd>PGN database (text)</dd>332 <dt>Requires</dt><dd><a href="01.html">NIP-01</a> events; <a href="31.html">NIP-31</a> alt tag recommended</dd>333 <dt>Payload spec</dt><dd>PGN - external standard</dd>334 <dt>First commit</dt><dd>2023-12-28</dd>335 <dt>Last updated</dt><dd>2026-06-13</dd>336 </dl>337 </div>338 </div>339 </aside>340 </main>341342 <nav class="navbtns">343 <a href="62.html">◀ NIP-62: Request to Vanish</a>344 <a href="65.html">NIP-65: Relay List Metadata ▶</a>345 </nav>346347 <footer class="pagefoot">348 <a href="../index.html">Nostr Protocol Manual</a>349 <span class="pageno">NIP 64 / F4</span>350 </footer>351</div>352353<script src="../assets/webmcp.js" defer></script>354</body>355</html>