clone

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

main
nip.guide / site / nips / 34.html
34.html 36.6K
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-34 - `git` stuff — Nostr Instruction Manual</title>7<meta name="description" content="NIP-34: code collaboration over Nostr. Repository announcements, patches, pull requests, issues, and status events. 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 34 - Code Collab</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="34">34</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 34</div>50 <h1>`git` stuff</h1>51 <p class="sub">Announce git repositories, send patches and pull requests, file issues, and track their status — all as signed Nostr events.</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 code collaboration with <code>git</code> over Nostr. The code itself stays on ordinary Git servers. Nostr carries everything around the code: repository announcements, branch state, patches, pull requests, issues, and status changes.</p>60 <p>A <strong>maintainer</strong> publishes an announcement event that describes a repository. A <strong>contributor</strong> sends a patch or a pull request as an event to the relays the announcement lists. Anyone files issues the same way. Status events then mark each thread as open, applied, merged, resolved, closed, or draft.</p>61 <p>No single forge holds the project hostage. The repository can move between Git servers. Its identity, its history of proposals, and its discussion stay on Nostr, tied to keys instead of accounts.</p>6263 <div class="box hint">64 <span class="tag">Hint!</span>65 <p>Think of this NIP as a mailing-list workflow with cryptographic identities. Patches travel like email in the kernel style. Pull requests point at branches on clone servers, GitHub style. Both are plain events you can verify.</p>66 </div>6768 <figure class="diagram">69 <div class="frame">70 <svg viewBox="0 0 560 202" role="img" aria-label="Map of the event kinds defined by NIP-34, grouped by role">71 <text class="d-actor-label" x="10" y="18">Kind map</text>72 <rect x="10" y="30" width="150" height="26" rx="4" fill="#2e5f9e"/>73 <text class="d-label" x="85" y="47" text-anchor="middle" fill="#fff">30617 - 30618</text>74 <text class="d-note" x="170" y="47">REPOSITORY — announcement and state (addressable)</text>75 <rect x="10" y="64" width="150" height="26" rx="4" fill="#f26a00"/>76 <text class="d-label" x="85" y="81" text-anchor="middle" fill="#fff">1617 - 1618 - 1619</text>77 <text class="d-note" x="170" y="81">CHANGES — patch, pull request, PR update</text>78 <rect x="10" y="98" width="150" height="26" rx="4" fill="#2c7a3f"/>79 <text class="d-label" x="85" y="115" text-anchor="middle" fill="#fff">1621</text>80 <text class="d-note" x="170" y="115">ISSUES — bug reports, requests, questions</text>81 <rect x="10" y="132" width="150" height="26" rx="4" fill="#7d8794"/>82 <text class="d-label" x="85" y="149" text-anchor="middle" fill="#fff">1630 – 1633</text>83 <text class="d-note" x="170" y="149">STATUS — open, applied/merged/resolved, closed, draft</text>84 <rect x="10" y="166" width="150" height="26" rx="4" fill="#2e5f9e"/>85 <text class="d-label" x="85" y="183" text-anchor="middle" fill="#fff">10317</text>86 <text class="d-note" x="170" y="183">USER — grasp server list (replaceable)</text>87 </svg>88 </div>89 <figcaption>Fig. 34-1 — <b>Kind map.</b> Ten event kinds cover the whole collaboration workflow.</figcaption>90 </figure>9192 <h2 class="bar" id="spec"><span class="num">2</span> Repository Announcements</h2>93 <p>A repository lives on one or more Git-enabled servers. A <strong>kind 30617</strong> addressable event announces that it exists. The author of the announcement asserts that they maintain the primary project, unless the event carries a <code>u</code> tag that marks it as a subordinate fork.</p>9495 <div class="crt">96 <div class="titlebar">Wire format - kind 30617 repository announcement</div>97<pre>{98 <span class="k">"kind"</span>: 30617,99 <span class="k">"content"</span>: <span class="s">""</span>,100 <span class="k">"tags"</span>: [101 [<span class="s">"d"</span>, <span class="c">&lt;repo-id&gt;</span>], <span class="c">// usually a kebab-case short name</span>102 [<span class="s">"name"</span>, <span class="c">&lt;human-readable project name&gt;</span>],103 [<span class="s">"description"</span>, <span class="c">&lt;brief description&gt;</span>],104 [<span class="s">"web"</span>, <span class="c">&lt;url for browsing&gt;</span>, ...],105 [<span class="s">"clone"</span>, <span class="c">&lt;url for git-cloning&gt;</span>, ...],106 [<span class="s">"relays"</span>, <span class="c">&lt;relay-url&gt;</span>, ...],107 [<span class="s">"r"</span>, <span class="c">&lt;earliest-unique-commit-id&gt;</span>, <span class="s">"euc"</span>],108 [<span class="s">"maintainers"</span>, <span class="c">&lt;other-recognized-maintainer&gt;</span>, ...],109 [<span class="s">"u"</span>, <span class="s">"30617:<span class="c">&lt;pubkey&gt;</span>:<span class="c">&lt;identifier&gt;</span>|<span class="c">&lt;git-url-https-preferred&gt;</span>"</span>,110 <span class="c">&lt;relay-hint&gt;</span>, <span class="c">&lt;author-pubkey&gt;</span>], <span class="c">// marks a subordinate fork</span>111 [<span class="s">"t"</span>, <span class="c">&lt;arbitrary string&gt;</span>] <span class="c">// hashtags labelling the repo</span>112 ]113}</pre>114 </div>115116 <div class="tscroll"><table class="ref">117 <caption>Kind 30617 tags</caption>118 <tr><th>Tag</th><th>Values</th><th>Rule</th></tr>119 <tr><td class="btn">d</td><td>one</td><td>The repository identifier. The only required tag. Usually a kebab-case short name.</td></tr>120 <tr><td class="btn">name</td><td>one</td><td>Human-readable project name.</td></tr>121 <tr><td class="btn">description</td><td>one</td><td>Brief human-readable project description.</td></tr>122 <tr><td class="btn">web</td><td>many</td><td>Webpage URLs for browsing, if the Git server offers a web view.</td></tr>123 <tr><td class="btn">clone</td><td>many</td><td>URLs you can give to <code>git clone</code>.</td></tr>124 <tr><td class="btn">relays</td><td>many</td><td>Relays this repository monitors for patches and issues.</td></tr>125 <tr><td class="btn">r</td><td>one, marked <span class="kbd">euc</span></td><td>The earliest unique commit ID. Identifies the repo among forks. See below.</td></tr>126 <tr><td class="btn">maintainers</td><td>many</td><td>Public keys of other recognized maintainers.</td></tr>127 <tr><td class="btn">u</td><td>one</td><td>Marks the repository as a subordinate fork. The value points to the parent: a <code>30617:&lt;pubkey&gt;:&lt;identifier&gt;</code> address or a git URL, HTTPS preferred. A relay hint and the author pubkey may follow.</td></tr>128 <tr><td class="btn">t</td><td>many</td><td>Arbitrary hashtag strings that label the repository.</td></tr>129 </table></div>130131 <p>The <code>r</code> tag with the <span class="kbd">euc</span> marker holds the <strong>earliest unique commit</strong> of the repo. It groups copies of the same project hosted in different places. In most cases it is the root commit. After a permanent fork between two projects, use the first commit after the fork instead.</p>132133 <div class="box tech">134 <span class="tag">Tech Note</span>135 <p>The source spec writes the <code>u</code> tag value as one string with a <code>|</code> between the event address and a git URL. It does not say when to use which side, and it spells "identifer" and "prefered". The intro sentence also only says a <code>u</code> tag cancels the maintainer assertion; the fork semantics live in a trailing comment. Treat the exact <code>u</code> format as underspecified.</p>136 </div>137138 <h3>Nostr clone URL format</h3>139 <p>A <code>nostr://</code> URL references a repository announcement. With a <a href="https://git-scm.com/docs/gitremote-helpers">git-remote-nostr</a> helper installed, <code>git clone</code> accepts it directly. Three forms exist:</p>140 <div class="crt">141 <div class="titlebar">nostr:// clone URL forms</div>142<pre>nostr://<span class="c">&lt;naddr&gt;</span>143nostr://<span class="c">&lt;npub|nip05&gt;</span>/<span class="c">&lt;identifier&gt;</span>144nostr://<span class="c">&lt;npub|nip05&gt;</span>/<span class="c">&lt;relay-hint&gt;</span>/<span class="c">&lt;identifier&gt;</span>145146<span class="c">-- examples --</span>147nostr://npub15qy…2exs5cyejr/relay.ngit.dev/ngit148nostr://npub15qy…2exs5cyejr/my%20%F0%9F%9A%80%20repo149nostr://danconwaydev.com/ws%3A%2F%2Flocalhost%3A7334/my-local-only-repo150nostr://danconwaydev.com/relay.ngit.dev/ngit</pre>151 </div>152 <ul>153 <li><code>&lt;identifier&gt;</code> is the <code>d</code> tag value of the kind 30617 event.</li>154 <li><code>&lt;relay-hint&gt;</code> is a relay URL. You may drop its <code>wss://</code> scheme for brevity.</li>155 <li>Both <code>&lt;relay-hint&gt;</code> and <code>&lt;identifier&gt;</code> must be percent-encoded per <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-2.1">RFC 3986 §2.1</a>.</li>156 <li><code>&lt;naddr&gt;</code> and <code>&lt;npub&gt;</code> are <a href="19.html">NIP-19</a> encodings; <code>&lt;nip05&gt;</code> is a <a href="05.html">NIP-05</a> address.</li>157 </ul>158159 <h2 class="bar"><span class="num">3</span> Repository State</h2>160 <p>A <strong>kind 30618</strong> addressable event is an optional source of truth for the branches and tags of a repository. Its <code>d</code> tag matches the identifier of the corresponding announcement.</p>161 <div class="crt">162 <div class="titlebar">Wire format - kind 30618 repository state</div>163<pre>{164 <span class="k">"kind"</span>: 30618,165 <span class="k">"content"</span>: <span class="s">""</span>,166 <span class="k">"tags"</span>: [167 [<span class="s">"d"</span>, <span class="c">&lt;repo-id&gt;</span>],168 [<span class="s">"refs/&lt;heads|tags&gt;/&lt;branch-or-tag-name&gt;"</span>, <span class="c">&lt;commit-id&gt;</span>],169 [<span class="s">"HEAD"</span>, <span class="s">"ref: refs/heads/<span class="c">&lt;branch-name&gt;</span>"</span>]170 ]171}</pre>172 </div>173 <p>The <code>refs</code> tag may appear many times, or not at all. Each one maps a branch or tag name to a commit ID, exactly like the output of <code>git show-ref</code>. The <code>HEAD</code> tag names the default branch.</p>174175 <h2 class="bar"><span class="num">4</span> Patches</h2>176 <p>Anyone can send a patch or a pull request to any repository. Send them to the relays listed in the announcement's <code>relays</code> tag. Include an <code>a</code> tag that points to the announcement address. Use patches when each event stays under 60kb. Use pull requests otherwise.</p>177 <p>A <strong>kind 1617</strong> patch carries the output of <code>git format-patch</code> in its <code>content</code>. Patches in a set chain together: each later patch includes a <a href="10.html">NIP-10</a> <code>e</code> reply tag that points to the previous patch. The first patch of a revision includes a NIP-10 <code>e</code> reply tag that points to the original root patch. The first patch in a series may be a cover letter in <code>git format-patch</code> format.</p>178179 <div class="crt">180 <div class="titlebar">Wire format - kind 1617 patch</div>181<pre>{182 <span class="k">"kind"</span>: 1617,183 <span class="k">"content"</span>: <span class="c">&lt;output of git format-patch&gt;</span>,184 <span class="k">"tags"</span>: [185 [<span class="s">"a"</span>, <span class="s">"30617:<span class="c">&lt;base-repo-owner-pubkey&gt;</span>:<span class="c">&lt;base-repo-id&gt;</span>"</span>],186 [<span class="s">"r"</span>, <span class="c">&lt;earliest-unique-commit-id-of-repo&gt;</span>],187 [<span class="s">"p"</span>, <span class="c">&lt;repository-owner&gt;</span>],188 [<span class="s">"p"</span>, <span class="c">&lt;other-user&gt;</span>], <span class="c">// optional: bring it to their attention</span>189190 [<span class="s">"t"</span>, <span class="s">"root"</span>], <span class="c">// omitted for later patches in a series</span>191 [<span class="s">"t"</span>, <span class="s">"root-revision"</span>], <span class="c">// only on the first patch of a revision</span>192193 <span class="c">// optional: keep a stable commit id when the patch is applied</span>194 [<span class="s">"commit"</span>, <span class="c">&lt;current-commit-id&gt;</span>],195 [<span class="s">"r"</span>, <span class="c">&lt;current-commit-id&gt;</span>],196 [<span class="s">"parent-commit"</span>, <span class="c">&lt;parent-commit-id&gt;</span>],197 [<span class="s">"commit-pgp-sig"</span>, <span class="s">"-----BEGIN PGP SIGNATURE-----..."</span>],198 [<span class="s">"committer"</span>, <span class="c">&lt;name&gt;</span>, <span class="c">&lt;email&gt;</span>, <span class="c">&lt;timestamp&gt;</span>, <span class="c">&lt;tz offset, minutes&gt;</span>]199 ]200}</pre>201 </div>202203 <div class="tscroll"><table class="ref">204 <caption>Kind 1617 tags</caption>205 <tr><th>Tag</th><th>Rule</th></tr>206 <tr><td class="btn">a</td><td>Address of the target repository announcement.</td></tr>207 <tr><td class="btn">r</td><td>Earliest unique commit ID of the repo. Lets clients subscribe to all patches for a local repo. A second <code>r</code> tag may carry the current commit ID so clients find patches for a specific commit.</td></tr>208 <tr><td class="btn">p</td><td>The repository owner. Extra <code>p</code> tags may notify other users.</td></tr>209 <tr><td class="btn">t</td><td><span class="kbd">root</span> on the first patch of a set; <span class="kbd">root-revision</span> on the first patch of a revision. Omit on later patches in a series.</td></tr>210 <tr><td class="btn">commit</td><td>Optional. The commit ID the patch produces on the proposer's machine.</td></tr>211 <tr><td class="btn">parent-commit</td><td>Optional. The parent of that commit.</td></tr>212 <tr><td class="btn">commit-pgp-sig</td><td>Optional. The PGP signature of the commit. An empty string means an unsigned commit.</td></tr>213 <tr><td class="btn">committer</td><td>Optional. Name, email, timestamp, and timezone offset in minutes.</td></tr>214 </table></div>215216 <div class="box hint">217 <span class="tag">Hint!</span>218 <p>The four optional commit tags exist for one reason: byte-for-byte reproducible commits. With them, the commit a maintainer creates by applying the patch gets the same ID it had on the proposer's machine. Omit them all if nobody cares.</p>219 </div>220221 <div class="box tech">222 <span class="tag">Tech Note</span>223 <p>The source sentence reads "The first patch revision in a patch revision SHOULD include a reply to the original root patch". The doubled phrase is a typo; the intended meaning — first patch <em>of</em> a revision replies to the root patch — is restated above.</p>224 </div>225226 <h2 class="bar"><span class="num">5</span> Pull Requests and Updates</h2>227 <p>A <strong>kind 1618</strong> pull request points to proposed changes that live on a Git server instead of inside the event. Its <code>content</code> is Markdown text that describes the change.</p>228 <div class="crt">229 <div class="titlebar">Wire format - kind 1618 pull request</div>230<pre>{231 <span class="k">"kind"</span>: 1618,232 <span class="k">"content"</span>: <span class="c">&lt;markdown text&gt;</span>,233 <span class="k">"tags"</span>: [234 [<span class="s">"a"</span>, <span class="s">"30617:<span class="c">&lt;base-repo-owner-pubkey&gt;</span>:<span class="c">&lt;base-repo-id&gt;</span>"</span>],235 [<span class="s">"r"</span>, <span class="c">&lt;earliest-unique-commit-id-of-repo&gt;</span>],236 [<span class="s">"p"</span>, <span class="c">&lt;repository-owner&gt;</span>],237 [<span class="s">"p"</span>, <span class="c">&lt;other-user&gt;</span>], <span class="c">// optional</span>238239 [<span class="s">"subject"</span>, <span class="c">&lt;PR-subject&gt;</span>],240 [<span class="s">"t"</span>, <span class="c">&lt;PR-label&gt;</span>], <span class="c">// optional, repeatable</span>241242 [<span class="s">"c"</span>, <span class="c">&lt;current-commit-id&gt;</span>], <span class="c">// tip of the PR branch</span>243 [<span class="s">"clone"</span>, <span class="c">&lt;clone-url&gt;</span>, ...], <span class="c">// at least one url with the commit</span>244 [<span class="s">"branch-name"</span>, <span class="c">&lt;branch-name&gt;</span>], <span class="c">// optional recommended name</span>245246 [<span class="s">"e"</span>, <span class="c">&lt;root-patch-event-id&gt;</span>], <span class="c">// optional: PR revises this patch</span>247 [<span class="s">"merge-base"</span>, <span class="c">&lt;commit-id&gt;</span>] <span class="c">// optional: common ancestor</span>248 ]249}</pre>250 </div>251 <div class="tscroll"><table class="ref">252 <caption>Kind 1618 tags</caption>253 <tr><th>Tag</th><th>Rule</th></tr>254 <tr><td class="btn">a</td><td>Address of the target repository announcement.</td></tr>255 <tr><td class="btn">r</td><td>Earliest unique commit ID, for subscriptions.</td></tr>256 <tr><td class="btn">p</td><td>Repository owner; extra <code>p</code> tags notify other users.</td></tr>257 <tr><td class="btn">subject</td><td>The PR title.</td></tr>258 <tr><td class="btn">t</td><td>Optional labels; may repeat.</td></tr>259 <tr><td class="btn">c</td><td>The commit at the tip of the PR branch.</td></tr>260 <tr><td class="btn">clone</td><td>At least one git clone URL where the commit can be downloaded.</td></tr>261 <tr><td class="btn">branch-name</td><td>Optional recommended branch name.</td></tr>262 <tr><td class="btn">e</td><td>Optional. Marks the PR as a revision of an existing root patch, which should then be closed.</td></tr>263 <tr><td class="btn">merge-base</td><td>Optional. The most recent common ancestor with the target branch.</td></tr>264 </table></div>265266 <h3>Pull request updates</h3>267 <p>A <strong>kind 1619</strong> PR update moves the tip of an existing pull request. It references the PR with the uppercase <a href="22.html">NIP-22</a> tags: <code>E</code> holds the PR event ID and <code>P</code> holds the PR author.</p>268 <div class="crt">269 <div class="titlebar">Wire format - kind 1619 PR update</div>270<pre>{271 <span class="k">"kind"</span>: 1619,272 <span class="k">"content"</span>: <span class="s">""</span>,273 <span class="k">"tags"</span>: [274 [<span class="s">"a"</span>, <span class="s">"30617:<span class="c">&lt;base-repo-owner-pubkey&gt;</span>:<span class="c">&lt;base-repo-id&gt;</span>"</span>],275 [<span class="s">"r"</span>, <span class="c">&lt;earliest-unique-commit-id-of-repo&gt;</span>],276 [<span class="s">"p"</span>, <span class="c">&lt;repository-owner&gt;</span>],277 [<span class="s">"p"</span>, <span class="c">&lt;other-user&gt;</span>], <span class="c">// optional</span>278279 [<span class="s">"E"</span>, <span class="c">&lt;pull-request-event-id&gt;</span>], <span class="c">// NIP-22 root reference</span>280 [<span class="s">"P"</span>, <span class="c">&lt;pull-request-author&gt;</span>],281282 [<span class="s">"c"</span>, <span class="c">&lt;current-commit-id&gt;</span>], <span class="c">// updated tip of the PR</span>283 [<span class="s">"clone"</span>, <span class="c">&lt;clone-url&gt;</span>, ...],284 [<span class="s">"merge-base"</span>, <span class="c">&lt;commit-id&gt;</span>] <span class="c">// optional</span>285 ]286}</pre>287 </div>288289 <h2 class="bar"><span class="num">6</span> Issues and Replies</h2>290 <p>A <strong>kind 1621</strong> issue is a Markdown conversational thread about the repository: a bug report, a feature request, a question, or a comment of any kind. Like patches, send issues to the relays in the announcement's <code>relays</code> tag.</p>291 <div class="crt">292 <div class="titlebar">Wire format - kind 1621 issue</div>293<pre>{294 <span class="k">"kind"</span>: 1621,295 <span class="k">"content"</span>: <span class="c">&lt;markdown text&gt;</span>,296 <span class="k">"tags"</span>: [297 [<span class="s">"a"</span>, <span class="s">"30617:<span class="c">&lt;base-repo-owner-pubkey&gt;</span>:<span class="c">&lt;base-repo-id&gt;</span>"</span>],298 [<span class="s">"p"</span>, <span class="c">&lt;repository-owner&gt;</span>],299 [<span class="s">"subject"</span>, <span class="c">&lt;issue-subject&gt;</span>], <span class="c">// optional header</span>300 [<span class="s">"t"</span>, <span class="c">&lt;issue-label&gt;</span>], <span class="c">// optional, repeatable</span>301 [<span class="s">"t"</span>, <span class="c">&lt;another-issue-label&gt;</span>]302 ]303}</pre>304 </div>305 <p>Clients may display the <code>subject</code> tag as a header. One or more <code>t</code> tags label the issue.</p>306 <p><strong>Replies.</strong> Replies to an issue (<code>kind:1621</code>), a patch (<code>kind:1617</code>), or a pull request (<code>kind:1618</code>) follow the <a href="22.html">NIP-22</a> comment format.</p>307308 <h2 class="bar"><span class="num">7</span> Status Events</h2>309 <p>Every root patch, pull request, and issue has a <strong>status</strong>. The status defaults to Open. Status events change it. Four kinds exist:</p>310 <div class="tscroll"><table class="ref">311 <caption>Status kinds</caption>312 <tr><th>Kind</th><th>Meaning</th></tr>313 <tr><td class="btn">1630</td><td>Open.</td></tr>314 <tr><td class="btn">1631</td><td>Applied or Merged for patches and PRs; Resolved for issues.</td></tr>315 <tr><td class="btn">1632</td><td>Closed.</td></tr>316 <tr><td class="btn">1633</td><td>Draft.</td></tr>317 </table></div>318 <div class="crt">319 <div class="titlebar">Wire format - kinds 1630–1633 status</div>320<pre>{321 <span class="k">"kind"</span>: <span class="c">&lt;1630|1631|1632|1633&gt;</span>,322 <span class="k">"content"</span>: <span class="c">&lt;markdown text&gt;</span>,323 <span class="k">"tags"</span>: [324 [<span class="s">"e"</span>, <span class="c">&lt;issue-or-PR-or-original-root-patch-id-hex&gt;</span>, <span class="s">""</span>, <span class="s">"root"</span>],325 [<span class="s">"e"</span>, <span class="c">&lt;accepted-revision-root-id-hex&gt;</span>, <span class="s">""</span>, <span class="s">"reply"</span>], <span class="c">// when a revision applied</span>326 [<span class="s">"p"</span>, <span class="c">&lt;repository-owner&gt;</span>],327 [<span class="s">"p"</span>, <span class="c">&lt;root-event-author&gt;</span>],328 [<span class="s">"p"</span>, <span class="c">&lt;revision-author&gt;</span>],329330 <span class="c">// optional, for better subscription filters</span>331 [<span class="s">"a"</span>, <span class="s">"30617:<span class="c">&lt;base-repo-owner-pubkey&gt;</span>:<span class="c">&lt;base-repo-id&gt;</span>"</span>, <span class="c">&lt;relay-url&gt;</span>],332 [<span class="s">"r"</span>, <span class="c">&lt;earliest-unique-commit-id-of-repo&gt;</span>],333334 <span class="c">// optional, kind 1631 only</span>335 [<span class="s">"q"</span>, <span class="c">&lt;applied-or-merged-patch-event-id&gt;</span>, <span class="c">&lt;relay-url&gt;</span>, <span class="c">&lt;pubkey&gt;</span>], <span class="c">// for each</span>336 [<span class="s">"merge-commit"</span>, <span class="c">&lt;merge-commit-id&gt;</span>], <span class="c">// when merged</span>337 [<span class="s">"r"</span>, <span class="c">&lt;merge-commit-id&gt;</span>],338 [<span class="s">"applied-as-commits"</span>, <span class="c">&lt;commit-id-in-master-branch&gt;</span>, ...], <span class="c">// when applied</span>339 [<span class="s">"r"</span>, <span class="c">&lt;applied-commit-id&gt;</span>] <span class="c">// for each</span>340 ]341}</pre>342 </div>343 <p>Two rules resolve conflicts:</p>344 <ul>345 <li>The valid status is the most recent status event by <code>created_at</code>, from either the author of the issue or patch, or from a maintainer.</li>346 <li>A patch revision inherits the status of its root patch. Exception: when the root patch is <code>1631</code> (Applied/Merged) and the revision is not tagged in that <code>1631</code> event, the revision counts as <code>1632</code> (Closed).</li>347 </ul>348349 <div class="box tech">350 <span class="tag">Tech Note</span>351 <p>The source phrases the revision rule as "The Status of a patch-revision is to either that of the root-patch" — the sentence is garbled. The restatement above follows the evident intent. The spec also leaves inline file comments undefined; it lists them under "possible things to be added later" and expects separate kinds for patches and merged files.</p>352 </div>353354 <figure class="diagram">355 <div class="frame">356 <svg viewBox="0 0 560 300" role="img" aria-label="Sequence diagram: contributor sends a patch to the repo relays, replies flow via NIP-22, the maintainer publishes a 1631 status event">357 <line class="d-wire" x1="110" y1="40" x2="110" y2="285" stroke-dasharray="3 4"/>358 <line class="d-wire" x1="450" y1="40" x2="450" y2="285" stroke-dasharray="3 4"/>359 <rect class="d-actor" x="55" y="8" width="110" height="30" rx="6"/>360 <text class="d-actor-label" x="110" y="28" text-anchor="middle">Contributor</text>361 <rect class="d-actor" x="395" y="8" width="110" height="30" rx="6"/>362 <text class="d-actor-label" x="450" y="28" text-anchor="middle">Maintainer</text>363 <path class="d-msg d-flow" d="M110,62 L445,62"/>364 <text class="d-label" x="280" y="55" text-anchor="middle">1617 patch ["t","root"] → repo relays</text>365 <path class="d-msg blue" d="M450,105 L115,105"/>366 <text class="d-label" x="280" y="98" text-anchor="middle">NIP-22 comment: "please rebase"</text>367 <path class="d-msg d-flow" d="M110,148 L445,148"/>368 <text class="d-label" x="280" y="141" text-anchor="middle">1617 patch ["t","root-revision"], e-reply → root</text>369 <path class="d-msg blue" d="M450,195 L115,195"/>370 <text class="d-label" x="280" y="188" text-anchor="middle">1631 Applied - merge-commit + q tags</text>371 <text class="d-note" x="280" y="240" text-anchor="middle">The maintainer applies the revision. The root patch reads Applied.</text>372 <text class="d-note" x="280" y="258" text-anchor="middle">Untagged revisions of that root now count as Closed.</text>373 <circle r="4" fill="#f26a00">374 <animateMotion dur="2.6s" repeatCount="indefinite" path="M110,62 L445,62"/>375 </circle>376 </svg>377 </div>378 <figcaption>Fig. 34-2 — <b>Patch lifecycle.</b> Patch, review comment, revision, then a status event closes the loop.</figcaption>379 </figure>380381 <h2 class="bar"><span class="num">8</span> User Grasp List</h2>382 <p>A <strong>grasp server</strong> is a combined relay and Git server for NIP-34 activity. A <strong>kind 10317</strong> replaceable event lists the grasp servers a user prefers, in order. It works like the <a href="65.html">NIP-65</a> relay list and the <a href="b7.html">NIP-B7</a> blossom server list.</p>383 <div class="crt">384 <div class="titlebar">Wire format - kind 10317 user grasp list</div>385<pre>{386 <span class="k">"kind"</span>: 10317,387 <span class="k">"content"</span>: <span class="s">""</span>,388 <span class="k">"tags"</span>: [389 [<span class="s">"g"</span>, <span class="c">&lt;grasp-service-websocket-url&gt;</span>] <span class="c">// zero or more, in order of preference</span>390 ]391}</pre>392 </div>393 <p>The event should include one <code>g</code> tag per grasp service WebSocket URL, ordered by preference.</p>394395 <h2 class="bar" id="examples"><span class="num">9</span> Worked Example</h2>396 <p>Carol maintains the project <code>orange-peeler</code>. Dave sends a one-commit patch. Carol applies it and publishes the status. Orange lines go to the relay. Blue lines come from the relay.</p>397 <div class="crt">398 <div class="titlebar">Session transcript - wss://relay.ngit.example</div>399<pre><span class="c">-- Carol announces the repository --</span>400<span class="send">→ ["EVENT", {"kind":30617,"pubkey":"c4a01…","content":"","tags":[401 ["d","orange-peeler"],["name","Orange Peeler"],402 ["description","peels oranges over websockets"],403 ["clone","https://git.example.com/carol/orange-peeler.git"],404 ["relays","wss://relay.ngit.example"],405 ["r","94ba1e2…","euc"],["t","cli"]], …}]</span>406<span class="recv">← ["OK", "77ff01…", true, ""]</span>407408<span class="c">-- Dave sends a root patch to the repo's relay --</span>409<span class="send">→ ["EVENT", {"kind":1617,"pubkey":"da4e9…",410 "content":"From 8f0c11… Mon Sep 17 00:00:00 2001\nFrom: Dave…\nSubject: [PATCH] fix peel angle\n…",411 "tags":[["a","30617:c4a01…:orange-peeler"],["r","94ba1e2…"],412 ["p","c4a01…"],["t","root"],413 ["commit","8f0c11…"],["parent-commit","94ba1e2…"],414 ["commit-pgp-sig",""],415 ["committer","Dave","dave@example.com","1750000000","-120"]], …}]</span>416<span class="recv">← ["OK", "e3d2ab…", true, ""]</span>417418<span class="c">-- Carol applies it and publishes the status --</span>419<span class="send">→ ["EVENT", {"kind":1631,"pubkey":"c4a01…","content":"Applied, thanks!",420 "tags":[["e","e3d2ab…","","root"],["p","c4a01…"],["p","da4e9…"],421 ["a","30617:c4a01…:orange-peeler","wss://relay.ngit.example"],422 ["r","94ba1e2…"],["q","e3d2ab…","wss://relay.ngit.example","da4e9…"],423 ["applied-as-commits","8f0c11…"],["r","8f0c11…"]], …}]</span>424<span class="recv">← ["OK", "b9a844…", true, ""]</span>425426<span class="c">-- anyone can now clone via the announcement --</span>427git clone nostr://npub1carol…/relay.ngit.example/orange-peeler</pre>428 </div>429430 <h2 class="bar" id="history"><span class="num">10</span> Version History</h2>431 <p>NIP-34 started as fiatjaf's sketch of "git stuff" and grew through heavy iteration by DanConwayDev, the author of the ngit tooling. Notable changes, newest first:</p>432 <ul class="patchnotes">433 <li><span class="ver">2026</span><span class="when">2026-06</span>Replaced the old <code>personal-fork</code> marker with the <code>u</code> tag for subordinate forks; aligned with GRASP servers. Grasp hosting instructions later left the PR description spec.</li>434 <li><span class="ver">2026</span><span class="when">2026-04</span>Added the <code>nostr://</code> clone URL format. Removed an unused <code>refs</code> tag extension and an incorrect claim about NIP-09 and state reset.</li>435 <li><span class="ver">2025</span><span class="when">2025-10</span>Added pull request (kind 1618) and PR update (kind 1619) events.</li>436 <li><span class="ver">2025</span><span class="when">2025-03</span>Deprecated the old git reply format in favor of <a href="22.html">NIP-22</a> comments.</li>437 <li><span class="ver">2024</span><span class="when">2024-09</span>Issues gained <code>subject</code> and <code>t</code> label tags; repository announcements gained optional <code>t</code> hashtags (2025-02).</li>438 <li><span class="ver">2024</span><span class="when">2024-05</span>Added repository state <code>refs</code>; simplified the <code>r</code> earliest-unique-commit tag (2024-04).</li>439 <li class="minor"><span class="ver">2024</span><span class="when">2024-03</span>One week after origin: status events, cover letter rules, the repo-id standard, extra maintainers, and discoverability tags.</li>440 <li><span class="ver">Origin</span><span class="when">2024-03-05</span>fiatjaf published "NIP-34: git stuff" as PR #997.</li>441 </ul>442443 <div class="credits">444 <h3 class="cardhead">Original Proposal - Credits</h3>445 <table>446 <tr><td>Author</td><td>fiatjaf</td></tr>447 <tr><td>First commit</td><td>2024-03-05 — “NIP-34: git stuff” (<a href="https://github.com/nostr-protocol/nips/pull/997">PR #997</a>)</td></tr>448 <tr><td>Status</td><td>draft - optional — implemented by ngit, gitworkshop.dev, and grasp servers</td></tr>449 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/34.md">34.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/34.md">full change history</a></td></tr>450 </table>451 </div>452453 <h2 class="bar"><span class="num">11</span> See Also</h2>454 <ul>455 <li><a href="01.html">NIP-01</a> — the base protocol: events, addressable kinds, and the <code>a</code>/<code>e</code>/<code>p</code>/<code>r</code>/<code>t</code> tag machinery used here.</li>456 <li><a href="10.html">NIP-10</a> — the <code>e</code> reply marker that chains patches in a series.</li>457 <li><a href="22.html">NIP-22</a> — the comment format for replies to patches, PRs, and issues, and the <code>E</code>/<code>P</code> tags in PR updates.</li>458 <li><a href="19.html">NIP-19</a> — the <code>naddr</code> and <code>npub</code> encodings inside <code>nostr://</code> clone URLs.</li>459 <li><a href="05.html">NIP-05</a> — DNS-based addresses, also usable in <code>nostr://</code> clone URLs.</li>460 <li><a href="65.html">NIP-65</a> — the relay list that the user grasp list imitates.</li>461 <li><a href="b7.html">NIP-B7</a> — the blossom server list, the other model for kind 10317.</li>462 </ul>463 </div>464465 <aside>466 <div class="cartridge">467 <div class="inner">468 <h3 class="cardhead">Spec Card</h3>469 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>470 <dl>471 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip">optional</span></dd>472 <dt>Defines</dt><dd>repo announcement - repo state - patches - PRs - issues - status - grasp list</dd>473 <dt>Kinds</dt><dd>30617, 30618, 1617, 1618, 1619, 1621, 1630–1633, 10317</dd>474 <dt>Requires</dt><dd><a href="01.html">NIP-01</a> - <a href="10.html">NIP-10</a> - <a href="22.html">NIP-22</a></dd>475 <dt>Transport</dt><dd>WebSocket relays + external Git servers</dd>476 <dt>First commit</dt><dd>2024-03-05</dd>477 <dt>Last updated</dt><dd>2026-07-26</dd>478 </dl>479 </div>480 </div>481 </aside>482 </main>483484 <nav class="navbtns">485 <a href="33.html">◀ NIP-33: Parameterized Replaceable Events</a>486 <a href="35.html">NIP-35: Torrents ▶</a>487 </nav>488489 <footer class="pagefoot">490 <a href="../index.html">Nostr Protocol Manual</a>491 <span class="pageno">NIP 34 / F4</span>492 </footer>493</div>494495<script src="../assets/webmcp.js" defer></script>496</body>497</html>