← All NIPs

NIP-34

git stuff

DraftEvents

Code collaboration over Nostr: announce git repositories, publish their branch state, and send patches, pull requests, issues and status updates as events, with no central forge.

At a glance

Status
In use and still allowed to change.
Kinds
Tags
  • a
  • c
  • commit-pgp-sig
  • d
  • description
  • e
  • g
  • maintainers
  • p
  • P
  • r
  • t
  • u
  • web
Last changed
Jul 26, 2026

Try it

Edit the form or the JSON: both stay in sync, the badge says whether the result is valid, and tapping any field explains it.

What to build

How the parts connect: Contributing a patch

Examples
Valid 1 note

Not signed yet · Demo keys only. These personas' secret keys are public on purpose.

Form
Jan 1, 2025, 12:00 AM UTC

Content

This kind has no content: leave it as an empty string.

Tags

  1. d Required

    Repository identifier, usually a short kebab-case name.

  2. name Optional

    Human-readable project name.

  3. description Optional

    Brief project description.

  4. web Optional

    Web pages for browsing the code.

  5. clone Optional

    URLs for git clone.

  6. relays Optional

    Relays this repository watches for patches and issues.

  7. r Recommended

    The earliest unique commit, to recognise the same project across hosts and forks.

  8. maintainers Optional

    Other recognised maintainers.

  9. t Optional

    A label or hashtag.

Add tag

How this NIP works

Step 1 of 6

Announce a repository

A maintainer publishes kind 30617 with a d identifier, clone and web URLs, and the relays that collect patches and issues. Publishing it makes you a maintainer of the project.

In the course

No course chapter covers this NIP yet. The editor above is the quickest way in.

Builds on

See also

Specification

Specification text is quoted in English from the nostr-protocol/nips repository. Our explanations and the interface are translated. Source on GitHub at commit 0046368

Read the spec1,293 words, quoted from the repository at commit 0046368.

This NIP defines all the ways code collaboration using and adjacent to git can be done using Nostr.

Repository announcements

Git repositories are hosted in Git-enabled servers, but their existence can be announced using Nostr events. By doing so the author asserts themselves as a maintainer of the primary project, unless a u tag is included.

{
  "kind": 30617,
  "content": "",
  "tags": [
    ["d", "<repo-id>"], // usually kebab-case short name
    ["name", "<human-readable project name>"],
    ["description", "brief human-readable project description>"],
    ["web", "<url for browsing>", ...], // a webpage url, if the git server being used provides such a thing
    ["clone", "<url for git-cloning>", ...], // a url to be given to `git clone` so anyone can clone it
    ["relays", "<relay-url>", ...], // relays that this repository will monitor for patches and issues
    ["r", "<earliest-unique-commit-id>", "euc"],
    ["maintainers", "<other-recognized-maintainer>", ...],
    ["u","30617:<pubkey>:<identifer>|<git-url-https-prefered>","<relay-hint>","<author-pubkey>"], // optionally indicate repository is a subordinate fork
    ["t", "<arbitrary string>"], // hashtags labelling the repository
  ]
}

The tags web, clone, relays, maintainers can have multiple values.

The r tag annotated with the "euc" marker should be the commit ID of the earliest unique commit of this repo, made to identify it among forks and group it with other repositories hosted elsewhere that may represent essentially the same project. In most cases it will be the root commit of a repository. In case of a permanent fork between two projects, then the first commit after the fork should be used.

Except d, all tags are optional.

Nostr Clone URL format

A nostr:// URL can be used to reference a repository announcement in a way that is compatible with git clone when a git-remote-nostr helper is installed:

nostr://<naddr>
nostr://<npub|nip05>/<identifier>
nostr://<npub|nip05>/<relay-hint>/<identifier>

Both <relay-hint> and <identifier> MUST be percent-encoded per RFC 3986 §2.1. <relay-hint> is a relay URL whose wss:// scheme may be omitted for brevity. <identifier> is the d tag value of the kind 30617 event.

Examples:

nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/relay.ngit.dev/ngit
nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my%20%F0%9F%9A%80%20repo
nostr://danconwaydev.com/ws%3A%2F%2Flocalhost%3A7334/my-local-only-repo
nostr://danconwaydev.com/relay.ngit.dev/ngit

Repository state announcements

An optional source of truth for the state of branches and tags in a repository.

{
  "kind": 30618,
  "content": "",
  "tags": [
    ["d", "<repo-id>"], // matches the identifier in the corresponding repository announcement
    ["refs/<heads|tags>/<branch-or-tag-name>","<commit-id>"]
    ["HEAD", "ref: refs/heads/<branch-name>"]
  ]
}

The refs tag may appear multiple times, or none.

Patches and Pull Requests (PRs)

Patches and PRs can be sent by anyone to any repository. Patches and PRs to a specific repository SHOULD be sent to the relays specified in that repository's announcement event's "relays" tag. Patch and PR events SHOULD include an a tag pointing to that repository's announcement address.

Patches SHOULD be used if each event is under 60kb, otherwise PRs SHOULD be used.

Patches

Patches in a patch set SHOULD include a NIP-10 e reply tag pointing to the previous patch.

The first patch revision in a patch revision SHOULD include a NIP-10 e reply to the original root patch.

{
  "kind": 1617,
  "content": "<patch>", // contents of <git format-patch>
  "tags": [
    ["a", "30617:<base-repo-owner-pubkey>:<base-repo-id>"],
    ["r", "<earliest-unique-commit-id-of-repo>"] // so clients can subscribe to all patches sent to a local git repo
    ["p", "<repository-owner>"],
    ["p", "<other-user>"], // optionally send the patch to another user to bring it to their attention

    ["t", "root"], // omitted for additional patches in a series
    // for the first patch in a revision
    ["t", "root-revision"],

    // optional tags for when it is desirable that the merged patch has a stable commit id
    // these fields are necessary for ensuring that the commit resulting from applying a patch
    // has the same id as it had in the proposer's machine -- all these tags can be omitted
    // if the maintainer doesn't care about these things
    ["commit", "<current-commit-id>"],
    ["r", "<current-commit-id>"] // so clients can find existing patches for a specific commit
    ["parent-commit", "<parent-commit-id>"],
    ["commit-pgp-sig", "-----BEGIN PGP SIGNATURE-----..."], // empty string for unsigned commit
    ["committer", "<name>", "<email>", "<timestamp>", "<timezone offset in minutes>"],
  ]
}

The first patch in a series MAY be a cover letter in the format produced by git format-patch.

Pull Requests

A pull request is an event that points to proposed changes in a git repository.

{
  "kind": 1618,
  "content": "<markdown text>",
  "tags": [
    ["a", "30617:<base-repo-owner-pubkey>:<base-repo-id>"],
    ["r", "<earliest-unique-commit-id-of-repo>"] // so clients can subscribe to all PRs sent to a local git repo
    ["p", "<repository-owner>"],
    ["p", "<other-user>"], // optionally send the PR to another user to bring it to their attention

    ["subject", "<PR-subject>"],
    ["t", "<PR-label>"], // optional
    ["t", "<another-PR-label>"], // optional

    ["c", "<current-commit-id>"], // tip of the PR branch
    ["clone", "<clone-url>", ...], // at least one git clone url where commit can be downloaded
    ["branch-name", "<branch-name>"], // optional recommended branch name

    ["e", "<root-patch-event-id>"], // optionally indicate PR is a revision of an existing patch, which should be closed
    ["merge-base", "<commit-id>"], // optional: the most recent common ancestor with the target branch
  ]
}

Pull Request Updates

A PR Update changes the tip of a referenced PR event.

{
  "kind": 1619,
  "content": "",
  "tags": [
    ["a", "30617:<base-repo-owner-pubkey>:<base-repo-id>"],
    ["r", "<earliest-unique-commit-id-of-repo>"] // so clients can subscribe to all PRs sent to a local git repo
    ["p", "<repository-owner>"],
    ["p", "<other-user>"], // optionally send the PR to another user to bring it to their attention

    // NIP-22 tags
    ["E", "<pull-request-event-id>"],
    ["P", "<pull-request-author>"],

    ["c", "<current-commit-id>"], // updated tip of PR
    ["clone", "<clone-url>", ...], // at least one git clone url where commit can be downloaded
    ["merge-base", "<commit-id>"], // optional: the most recent common ancestor with the target branch
  ]
}

Issues

Issues are Markdown text that is just human-readable conversational threads related to the repository: bug reports, feature requests, questions or comments of any kind. Like patches, these SHOULD be sent to the relays specified in that repository's announcement event's "relays" tag.

Issues may have a subject tag, which clients can utilize to display a header. Additionally, one or more t tags may be included to provide labels for the issue.

{
  "kind": 1621,
  "content": "<markdown text>",
  "tags": [
    ["a", "30617:<base-repo-owner-pubkey>:<base-repo-id>"],
    ["p", "<repository-owner>"]
    ["subject", "<issue-subject>"]
    ["t", "<issue-label>"]
    ["t", "<another-issue-label>"]
  ]
}

Replies

Replies to either a kind:1621 (issue), kind:1617 (patch) or kind:1618 (pull request) event should follow NIP-22 comment.

Status

Root Patches, PRs and Issues have a Status that defaults to 'Open' and can be set by issuing Status events.

{
  "kind": 1630, // Open
  "kind": 1631, // Applied / Merged for Patches; Resolved for Issues
  "kind": 1632, // Closed
  "kind": 1633, // Draft
  "content": "<markdown text>",
  "tags": [
    ["e", "<issue-or-PR-or-original-root-patch-id-hex>", "", "root"],
    ["e", "<accepted-revision-root-id-hex>", "", "reply"], // for when revisions applied
    ["p", "<repository-owner>"],
    ["p", "<root-event-author>"],
    ["p", "<revision-author>"],

    // optional for improved subscription filter efficiency
    ["a", "30617:<base-repo-owner-pubkey>:<base-repo-id>", "<relay-url>"],
    ["r", "<earliest-unique-commit-id-of-repo>"]

    // optional for `1631` status
    ["q", "<applied-or-merged-patch-event-id>", "<relay-url>", "<pubkey>"], // for each
    // when merged
    ["merge-commit", "<merge-commit-id>"]
    ["r", "<merge-commit-id>"]
    // when applied
    ["applied-as-commits", "<commit-id-in-master-branch>", ...]
    ["r", "<applied-commit-id>"] // for each
  ]
}

The most recent Status event (by created_at date) from either the issue/patch author or a maintainer is considered valid.

The Status of a patch-revision is to either that of the root-patch, or 1632 (Closed) if the root-patch's Status is 1631 (Applied/Merged) and the patch-revision isn't tagged in the 1631 (Applied/Merged) event.

User grasp list

List of grasp servers the user generally wishes to use for NIP-34 related activity. It is similar in function to the NIP-65 relay list and NIP-B7 blossom list.

The event SHOULD include a list of g tags with grasp service websocket URLs in order of preference.

{
  "kind": 10317,
  "content": "",
  "tags": [
    ["g", "<grasp-service-websocket-url>"], // zero or more grasp sever urls
  ],
}

Possible things to be added later

  • inline file comments kind (we probably need one for patches and a different one for merged files)