-
@ armstrys
2023-07-20 04:06:46Why this isn't a PR
I didn't know exactly where to post this... I have been thinking a lot the past few days about how we can use nostr and moderated communities NIP-172 to free the power of git from it's centralized overlord, GitHub.
I know there are already a few open pull requests that inspired this. PR #223 has lots of great discussion about the benefits of leaning on existing git servers for a nostr-git implementation. PR #324 establishes the usage of a
"c"
tag to make commits available for queries on relays. I think this aspect will be critical for fast and effecient git content discovery over nostr.Given that I have limited experience with submitting PRs to open project on GitHub and was not sure where to share these ideas. I took it as an opportunity to write a sample NIP! I would love to get feedback from others, especially folks like nostr:npub1melv683fw6n2mvhl5h6dhqd8mqfv3wmxnz4qph83ua4dk4006ezsrt5c24 and nostr:npub160t5zfxalddaccdc7xx30sentwa5lrr3rq4rtm38x99ynf8t0vwsvzyjc9, who have both been working on their PRs longer than I have been thinking about this.
A few highlights
- Git remotes get their own kind and they are replaceable. This allows events that reference the remote repository to use a static address (just like a longform article) that will continue to work if the author changes the location of the remote - especially important to maintain censorship resistance.
- The git remote kind can provide indexed
"c"
tags that allow remote discovery via relays. If multiple servers have instances of the Nostr git repository then all of those relays can quickly be surfaced by querying on the correct commit hash. - Because these coordination events effectively suggest changes that a user might pull into their existing git repositories it makes sense to use these events within moderated communities NIP-172 to establish approval mechanisms that indicate when a git-enabled client should merge a PR or whether or not a commit should be trusted.
The last thing I will note is that I have framed the usage of the new event kinds assuming that only key commits will be posted to Nostr. This should reduce data usage on relays if only commits that require discussion or action need to be posted and referenced. Git-enabled clients will still be able to access all other commits from individual remote git servers. However, there is nothing outside of data size stopping this framework from being used to track every single commit on a repository if that ended up being desirable.
Enough of the preamble...
NIP-XXX
Git Remote Index and Commit Checkpoints
draft
optional
author:armstrys
Git Kinds
The goal of this nip is to introduce mechanisms for git remote repository discovery and commit checkpointing into the Nostr protocol. Git is already decentralized by nature. Centralized clients like GitHub serve two primary purposes. - They establish one central remote repository as the source of truth. - They provide a platform for non-git metadata tracking including issues and comments.
A decentralized implementation on Nostr should replace the central remote repository with discovery mechanisms that take advantage of the already decentralized nature of git and also provide Nostr-native representations of commits that need to be referenced by external metadata not handled by git.
This nip introduces two new kinds to achieve this: - A "git repository" parameterized replaceable event (
kind:34617
) to provide undateable connection points to existing git servers that are easily discoverable and referenced by the second new kind... - A "git checkpoint" event (kind:4617
) which has the sole purpose of providing a coordination reference point for key commits in a git repository and should follow a reply structure similar to the chain of commits in git.With these two kinds it is possible to represent censorship resistant personal repositories, forked repositories, and even moderated repositories by integrating these kinds with NIP-172.
Note: This implementation assumes nothing about how the client will interact with git because it only aims to coordinate a layer above git to track metadata and discover existing git server locations. Clients would need to integrate authentication via other providers until git server implementations with Nostr authentication are available.
Git Tags
Additionally, we introduce two application-specific tags that should be used in conjunction with the new git event kinds: 1. A
"c"
tag that takes a commit, a branch name, and a marker ('',"head"
,"compare"
,"output"
) like so:["c", "<commit hash>", "<branch name>","<marker>"]
. 2. A"git-history"
tag that provides a plain text history of commands that a user ran to generate a merge output (e.g.“git checkout <base hash>\ngit merge <compare hash> —no-ff”
). 3. An"auth-required"
tag that allows an author to publicize whether a remote requires authentication to access. Clients SHOULD assume that remotes with no"auth-required"
default totrue
- the equivalent of["auth-required", true]
Git Remote Definition
Kind:34617
defines a replaceable event (NIP-33) that provides a url to a remote repository as it's.content
. Using a replaceable event allows other events (like commit checkpoints) to reference this remote via"a"
tag without concern that the links will break should the author need to change the location of the remote repository. The event SHOULD contain one or more"c"
tags for any commit that define the"head"
commit of each branch that the user author wants to make discoverable in the repository. The author can include other commits on each branch for key commits like releases.json { "id": "<32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>", "pubkey": "<32-bytes lowercase hex-encoded public key of the event creator>", "created_at": "<Unix timestamp in seconds>", "kind": 34617, "tags": [ ["auth-required", false], ["a", "34550:<Community event author pubkey>:<d-identifier of the community>", "<Optional relay url>"], ["d", "<git remote name>"], ["c", "<commit hash>", "<branch name>","head"], ["c", "<commit hash>", "<branch name>",""], ["c", "<commit hash>", "<other branch name>","head"], ], "content": "<address to remote>" }
Git Checkpoint Definition
The usage of
"c"
marked tags help reference different git-related events, all usingkind:34617
: 1. An event with a two"c"
tags marked"head"
and"output"
should be interpreted as a standard commit checkpoint. 2. An event with three"c"
tags marked"head"
,"compare"
, and"output"
should be interpreted as a merge checkpoint 3. An event with a"c"
tag marked"compare"
, but with no"output"
commit should be interpreted as a pull/merge request 4. An event with at least one"c"
tag but without a"compare"
or"output"
marker should be interpreted as a release/tag and should reply to the appropriate commit. 5. An barekind:34617
event with no"c"
tag should be interpreted as a comment if it is a reply to another event or as an issue if it is not.A commit checkpoint SHOULD include at least one
"a"
tag to akind:34617
remote repository where the tagged git commit in question can be found. Clients may also query for matching"c"
tags to discover other relevant remotes as needed.Clients should interpret any
"a"
tag that includes"34617:*"
as the first place to search for commits referenced in akind:4617
event.Clients MUST use marked event tags (NIP-10) to chain checkpoints by onto the last availably commit checkpoint on the same branch. Identifying the proper
"root"
and"reply"
events allow other clients to follow and discover events in the same chain and forks.Both
kind:34617
andkind:4617
events MAY include a NIP-172 style"a"
tag to establish a moderated repository. This may also help with repository remote discovery and organization as thekind:34550
community event could suggest default remotes for the community.An example of a standard commit checkpoint:
json { "id": "<32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>", "pubkey": "<32-bytes lowercase hex-encoded public key of the event creator>", "created_at": "<Unix timestamp in seconds>", "kind": 4617, "tags": [ ["c", "<current head hash>", "<optional branch name>","head"], ["c", "<expected output hash>", "<branch name>", "output"], ["a", "34617:<compare remote event author pubkey>:<compare remote name>"], ["e", "<event id of first checkpoint with output on checkpoint chain>", "<optional relay url>", "root id"], ["e", "<event id of previous checkpoint with output on checkpoint chain>", "<optional relay url>", "reply"], ["a", "34550:<Community event author pubkey>:<d-identifier of the community>", "<Optional relay url>"] ], "content": "<description of commit>" }
An example of a merge/pull request checkpoint:
json { "id": "<32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>", "pubkey": "<32-bytes lowercase hex-encoded public key of the event creator>", "created_at": "<Unix timestamp in seconds>", "kind": 4617, "tags": [ ["c", "<current head hash>", "<branch name>","head"], ["c", "<compare output hash>", "<branch name>", "compare"], ["a", "34617:<compare remote event author pubkey>:<compare remote name>"], ["e", "<event id of first checkpoint with output on checkpoint chain>", "<optional relay url>", "root id"], ["e", "<event id of previous checkpoint with output on checkpoint chain>", "<optional relay url>", "reply"], ["a", "34550:<Community event author pubkey>:<d-identifier of the community>", "<Optional relay url>"] ], "content": "<description of merge/pull request>" }
An example of a merge checkpoint:
json { "id": "<32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>", "pubkey": "<32-bytes lowercase hex-encoded public key of the event creator>", "created_at": "<Unix timestamp in seconds>", "kind": 4617, "tags": [ ["git-history", "<plain text commands used to execute merge>"], ["c", "<current head hash>", "<branch name>","head"], ["c", "<compare hash>", "<optional branch name>", "compare"], ["c", "<expected output hash>", "<branch name>", "output"], ["a", "34617:<compare remote event author pubkey>:<compare remote name>"], ["e", "<event id of first checkpoint with output on checkpoint chain>", "<optional relay url>", "root id"], ["e", "<event id of previous checkpoint with output on checkpoint chain>", "<optional relay url>", "reply"], ["e", "<event id of merge/pull request>", "<optional relay url>", "mention"], ["a", "34550:<Community event author pubkey>:<d-identifier of the community>", "<Optional relay url>"], ["p", "<hex pubkey of merge/pull request author>"] ], "content": "<description of changes since last checkpoint>" }
When establishing a merge checkpoint that references a merge/pull request the client should include the merge/pull request author as a"p"
tag similar to the usage of replies in NIP-10