How it works
ngit doesn't replace git. It replaces the forge — the layer GitHub or GitLab adds on top of git: the canonical copy, the pull requests, the issues, the accounts.
Two layers, pulled apart
A git repository is really two things:
- State — the refs. Which commit
mainpoints at. Which commits are taggedv2.0. A small, constantly-changing set of names and hashes. - Data — the objects. The commits, trees and blobs themselves. Large, immutable, content-addressed.
On a forge, both live in the same place, and that place is authoritative. ngit separates them:
| Lives on | Role | |
|---|---|---|
| State (refs) | nostr relays, as signed events | The source of truth |
| Data (objects) | ordinary git servers | Interchangeable storage |
When you git fetch, git-remote-nostr reads the current ref state from the relays, then fetches the matching objects from whichever git servers the repository announcement lists.
The consequence is the interesting part. Because the refs are signed by the maintainer and replicated across relays, the git server is no longer authoritative — it's a cache. A maintainer can add one, drop one, or move between providers without contributors changing a thing. Nobody re-clones. Nobody updates a remote. The nostr:// URL never changes.
What nostr provides
Nostr is a protocol where users publish signed events to relays — simple servers anyone can run. There's no central authority: identity is a keypair, and data is replicated across as many relays as you like.
ngit uses it for the things a forge would otherwise own:
- Identity — you are a keypair, not an account on someone's server.
- Discovery — repository announcements say where a repo's data and relays live.
- State — the signed refs described above.
- Collaboration — pull requests, issues, comments, labels and status changes are all events.
GRASP servers
Running a relay and a git server sounds like work. GRASP servers collapse them into one hosted service — relay.ngit.dev is both.
This is what makes the first-run experience painless. When ngit init publishes an announcement naming a GRASP server, that server creates the git repository automatically. No signup, no access token, no pre-created empty repo to push into.
You are not required to use one. Separate relays and git servers work fine, and a repository can list several of each for redundancy — see Repositories.
nostr:// URLs
nostr://<npub>/<identifier>
nostr://<npub>/<relay-hint>/<identifier>The relay hint is a bare domain, like relay.ngit.dev. It's optional but worth including: without it, ngit has to discover which relays carry the announcement, which is slower.
# with a relay hint — preferred
git clone nostr://npub1abc.../relay.ngit.dev/my-project
# without — works, but discovery is slower
git clone nostr://npub1abc.../my-project
# NIP-05 address, if someone gave you one
git clone nostr://danconwaydev.com/relay.ngit.dev/ngitTIP
Don't guess NIP-05 addresses. user@domain forms only work if someone published one — use the npub1... form unless you were given an address.
Once installed, these URLs work with plain git commands. git-remote-nostr is a remote helper: git discovers it by filename whenever it meets a nostr:// URL, which is why both binaries have to be on your PATH.
Where the web UI fits
gitworkshop.dev reads the same events. It's a browser client for the same data, not a server that owns it — nothing in ngit depends on it being up. Browse repositories at gitworkshop.dev/repos.
Next
- Install — get the two binaries onto your
PATH - Quickstart — publish a repo and open a PR