NIP-34 ====== `git` stuff ----------- `draft` `optional` This NIP defines all the ways code collaboration using and adjacent to [`git`](https://git-scm.com/) 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 and expresses a willingness to receive patches, bug reports and comments in general, unless `t` tag `personal-fork` is included. ```jsonc { "kind": 30617, "content": "", "tags": [ ["d", ""], // usually kebab-case short name ["name", ""], ["description", "brief human-readable project description>"], ["web", "", ...], // a webpage url, if the git server being used provides such a thing ["clone", "", ...], // a url to be given to `git clone` so anyone can clone it ["relays", "", ...], // relays that this repository will monitor for patches and issues ["r", "", "euc"], ["maintainers", "", ...], ["t","personal-fork"], // optionally indicate author isn't a maintainer ["t", ""], // 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. ## Repository state announcements An optional source of truth for the state of branches and tags in a repository. ```jsonc { "kind": 30618, "content": "", "tags": [ ["d", ""], // matches the identifier in the corresponding repository announcement ["refs//",""] ["HEAD", "ref: refs/heads/"] ] } ``` The `refs` tag may appear multiple times, or none. If no `refs` tags are present, the author is no longer tracking repository state using this event. This approach enables the author to restart tracking state at a later time unlike [NIP-09](09.md) deletion requests. The `refs` tag can be optionally extended to enable clients to identify how many commits ahead a ref is: ```jsonc { "tags": [ ["refs//", "", "", "", ...], ] } ``` ## 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. ### When to Use Patches vs. Pull Requests **Patches SHOULD be used if each event is under 60kb, otherwise PRs SHOULD be used.** However, the choice between patches and pull requests isn't just about size. Here are the key differences: #### Patches (Kind 1617) - **Content**: Patch content is embedded directly in the Nostr event content field - **Size**: Each patch event must be under 60KB - **Workflow**: Event-based, sequential patch series linked via NIP-10 reply tags - **Use Cases**: - Small, self-contained changes - Bug fixes and typo corrections - Simple feature additions - When you want to send code changes as self-contained events without maintaining a fork - **Series**: Patches in a series are linked via NIP-10 reply tags - **Format**: Patch content can be in any format (git format-patch, unified diff, or plain text description) - **Discovery**: Patches are discoverable as Nostr events on relays, independent of git repository access #### Pull Requests (Kind 1618) - **Content**: Markdown description with references to commits via clone URLs - **Size**: No strict limit (commits are stored in git repositories, not in the event) - **Workflow**: Branch-based, iterative collaboration - **Use Cases**: - Large, complex changes - Multi-file refactoring - Features requiring discussion and iteration - When working with forks and branches - **Updates**: PRs can be updated (kind 1619) to change the tip commit - **Format**: References commits that must be accessible via clone URLs #### Key Differences Summary | Aspect | Patches | Pull Requests | |--------|---------|---------------| | **Event Size** | Under 60KB | No limit (commits stored separately) | | **Content Location** | In event content | In referenced git repository | | **Workflow Style** | Email-style, sequential | Branch-based, iterative | | **Best For** | Small, self-contained changes | Large, complex changes | | **Maintenance** | Static (new patch for revisions) | Dynamic (can update tip commit) | | **Application** | Extract from event, then `git am` (if applicable) | `git merge` or `git cherry-pick` | | **Discussion** | Via NIP-22 comments | Via NIP-22 comments + inline code comments | ### Patches wPatches are created as Nostr events (kind 1617) with the patch content embedded in the event's content field. This makes patches self-contained and discoverable on Nostr relays without requiring access to git repositories. Patches in a patch set SHOULD include a [NIP-10](10.md) `e` `reply` tag pointing to the previous patch. The first patch revision in a patch revision SHOULD include a [NIP-10](10.md) `e` `reply` to the original root patch. ```jsonc { "kind": 1617, "content": "", // Patch content in any format (git format-patch, unified diff, or plain text) "tags": [ ["a", "30617::"], ["r", ""], // so clients can subscribe to all patches sent to a local git repo ["p", ""], ["p", ""], // 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", ""], ["r", ""], // so clients can find existing patches for a specific commit ["parent-commit", ""], ["commit-pgp-sig", "-----BEGIN PGP SIGNATURE-----..."], // empty string for unsigned commit ["committer", "", "", "", ""], ] } ``` The patch content can be in any format that describes code changes. Common formats include git format-patch output, unified diff format, or plain text descriptions. The first patch in a series MAY be a cover letter describing the patch series. ### Pull Requests The PR or PR update tip SHOULD be successfully pushed to `refs/nostr/<[PR|PR-Update]-event-id>` in all repositories listed in its `clone` tag before the event is signed. An attempt SHOULD be made to push this ref to all repositories listed in the repository's announcement event's `"clone"` tag, for which their is reason to believe the user might have write access. This includes each [grasp server](https://njump.me/naddr1qvzqqqrhnypzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqy28wumn8ghj7un9d3shjtnwva5hgtnyv4mqqpt8wfshxuqlnvh8x) which can be identified using this method: `clone` tag includes `[http|https]:////.git` and `relays` tag includes `[ws/wss]://`. Clients MAY fallback to creating a 'personal-fork' `repository announcement` listing other grasp servers, e.g. from the `User grasp list`, for the purpose of serving the specified commit(s). ```jsonc { "kind": 1618, "content": "", "tags": [ ["a", "30617::"], ["r", ""] // so clients can subscribe to all PRs sent to a local git repo ["p", ""], ["p", ""], // optionally send the PR to another user to bring it to their attention ["subject", ""], ["t", ""], // optional ["t", ""], // optional ["c", ""], // tip of the PR branch ["clone", "", ...], // at least one git clone url where commit can be downloaded ["branch-name", ""], // optional recommended branch name ["e", ""], // optionally indicate PR is a revision of an existing patch, which should be closed ["merge-base", ""], // optional: the most recent common ancestor with the target branch ] } ``` ### Pull Request Updates A PR Update changes the tip of a referenced PR event. ```jsonc { "kind": 1619, "content": "", "tags": [ ["a", "30617::"], ["r", ""] // so clients can subscribe to all PRs sent to a local git repo ["p", ""], ["p", ""], // optionally send the PR to another user to bring it to their attention // NIP-22 tags ["E", ""], ["P", ""], ["c", ""], // updated tip of PR ["clone", "", ...], // at least one git clone url where commit can be downloaded ["merge-base", ""], // 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. ```json { "kind": 1621, "content": "", "tags": [ ["a", "30617::"], ["p", ""] ["subject", ""] ["t", ""] ["t", ""] ] } ``` ## Replies Replies to either a `kind:1621` (_issue_), `kind:1617` (_patch_) or `kind:1618` (_pull request_) event should follow [NIP-22 comment](22.md). ## Status Root Patches, PRs and Issues have a Status that defaults to 'Open' and can be set by issuing Status events. ```jsonc { "kind": 1630, // Open "kind": 1631, // Applied / Merged for Patches; Resolved for Issues "kind": 1632, // Closed "kind": 1633, // Draft "content": "", "tags": [ ["e", "", "", "root"], ["e", "", "", "reply"], // for when revisions applied ["p", ""], ["p", ""], ["p", ""], // optional for improved subscription filter efficiency ["a", "30617::", ""], ["r", ""] // optional for `1631` status ["q", "", "", ""], // for each // when merged ["merge-commit", ""] ["r", ""] // when applied ["applied-as-commits", "", ...] ["r", ""] // 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](https://njump.me/naddr1qvzqqqrhnypzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqy28wumn8ghj7un9d3shjtnwva5hgtnyv4mqqpt8wfshxuqlnvh8x) 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. ```jsonc { "kind": 10317, "content": "", "tags": [ ["g", ""], // 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) ## GitRepublic Usage NIP-34 is the core NIP that GitRepublic implements. All repository functionality is built on top of NIP-34 event kinds. ### Repository Announcements (Kind 30617) GitRepublic uses repository announcements to: - **Discover Repositories**: The landing page fetches all kind 30617 events to display available repositories - **Repository Creation**: Users create repository announcements via the signup page - **Repository Updates**: Repository information updates are done via the signup page, which updates the repository announcement event - **Fork Detection**: Forks are identified by `a` tags pointing to the original repository - **Offline Papertrail**: Repository announcements are saved to `nostr/repo-events.jsonl` in the repository for offline verification ### Pull Requests (Kind 1618) - **PR Creation**: Users create PRs by pushing commits and creating kind 1618 events - **PR Updates**: PR updates (kind 1619) change the tip commit of existing PRs - **PR Display**: PRs are displayed with their full markdown content and metadata - **PR Merging**: Merging a PR creates a status event (kind 1631) with merge commit information ### Issues (Kind 1621) - **Issue Creation**: Users create issues for bug reports, feature requests, and questions - **Issue Threading**: Issues use NIP-22 comments for discussion - **Issue Status**: Status events (kinds 1630-1633) track issue state (open, resolved, closed, draft) ### Status Events (Kinds 1630-1633) - **Status Tracking**: PRs and issues use status events to track their state - **Most Recent Wins**: Only the most recent status event from the author or a maintainer is considered valid - **Merge Tracking**: When a PR is merged (status 1631), the merge commit ID is included in tags ### Repository State (Kind 30618) - **Branch Tracking**: Optional repository state events track branch and tag positions - **State Updates**: Repository state can be updated to reflect current branch/tag positions ### Patches (Kind 1617) GitRepublic supports event-based patches for small, self-contained code changes: - **Patch Creation**: Users create patches as Nostr events (kind 1617) via the web interface - **Patch Content**: Patch content is embedded directly in the event's content field (can be in any format: git format-patch, unified diff, or plain text) - **Event-Based**: Patches are self-contained Nostr events, discoverable on relays without requiring git repository access - **Patch Series**: Multiple related patches can be linked using NIP-10 reply tags - **Patch Status**: Patches use status events (kinds 1630-1633) to track state (open, applied, closed, draft) - **Patch Application**: Maintainers extract patch content from events and apply them, then mark as applied with status events **Implementation**: - Repository announcements: `src/routes/signup/+page.svelte`, `src/lib/services/nostr/repo-polling.ts` - Pull requests: `src/lib/services/nostr/prs-service.ts` - Issues: `src/lib/services/nostr/issues-service.ts` - Status events: Used throughout PR, patch, and issue management