maninak

Git Is Distributed. Your Project Isn’t.

Git escaped centralization. Everything we built on top of git walked right back into it.

Published
Reading time
8 minutes
Contents

The Grove Build Log, chapter 0: in which I explain what I have gotten myself into.

So. You use git. Of course you do, you found your way here, and you strike me as the kind of person who commits early and often. Then you already know the famous thing about git, the one every tutorial opens with: git is distributed. Every clone is a full copy. Full history, every branch, the works. No central server, no single point of failure. Linus built it so a thousand kernel hackers could collaborate without asking one blessed machine for permission.

Now humor me for a minute. Open one of your repos on GitHub. Look at the issues. The pull requests, the review threads, the labels, the stars, your very username and everything it has ever said. If GitHub vanished tonight, which of those would you still have tomorrow morning?

I’ll wait…

The code, obviously. Every clone has the code. Everything else, the entire social life of your project, its memory of every decision and every argument and every “fixed in #482”, lives in exactly one place, on someone else’s computer. We took the most successful distributed version-control system ever shipped… and we all pointed it at the same building.

Two drawings side by side. On the left, git as designed: six clones linked to one another, every copy reachable from every other, with no special node among them. On the right, git as practiced: the same six clones, each wired to a single box in the middle, and the only path from any clone to any other runs through it.

git remote -v. Git as designed, on the left. Git as practiced, on the right.

The great escape (to another server)

That contradiction sat wrong with me for years, and I’m clearly not alone: a whole genre of software exists to get out of it. Every project below saw the same problem I did, they just took different forks in the road. (I’m sorry, I’ll let myself out… ^^ )

The baseline: GitHub (opens in a new tab) and GitLab (opens in a new tab). This is not a “GitHub bad” post, I’ve spent most of my career happily inside both. It’s just that your account is a row in someone else’s database, and the row isn’t yours. Now and then that bites. A takedown lands on the wrong repo. An anti-fraud system locks the wrong org. A sanctions policy fences off a country and thousands of developers wake up locked out of their own history. Genuinely rare. Until it’s you.

Escape one: become the server. Gitea (opens in a new tab), Forgejo (opens in a new tab), Codeberg (opens in a new tab). Self-hosting is the classic move: put the forge on hardware you control, and no company can close your account. Except now you are the server. The uptime, the backups, the spam, the migrations at 2am, all yours. And for everyone who contributes, the center of truth is still one machine, it just has your name on it.

Escape two: teach the servers to talk. Forgejo has been building federation (opens in a new tab) on ActivityPub, the protocol behind Mastodon. Your issues live on your instance, mine live on mine, and the instances trade mail so our projects can collaborate across the fence. It’s good, hard work and I hope it lands well. But your account and your project still live on an instance. Fewer eggs per basket, more baskets… still baskets.

Escape three: make the user portable. Tangled (opens in a new tab) is a young forge built on atproto (opens in a new tab), the protocol under Bluesky. It’s VC-backed, and its clever trick is portable identity: fall out with your host and you can move without losing your name or your social graph. A real step forward. Your repos and their social layer still live on hosted infrastructure, though.

Four drawings in a row, labelled GitHub, self-hosted, federated and Tangled. The outline around each one is a different shape, but inside every outline is the identical picture: four peers wired to a single box in the middle, and the box is the only thing joining them.

Four buildings. One floor plan.

At some point I stopped asking “which server should hold my project?” and started asking why a distributed version control system needs an appointed keeper at all.

The project as the sovereign thing

That question has an answer, and it’s called Radicle (opens in a new tab). When I first understood it properly, it rearranged my head.

Radicle is a peer-to-peer code collaboration network built on plain git. Your identity isn’t a username in someone’s database, it’s a cryptographic keypair that you generate and hold. Nobody issues it, so nobody can revoke it. Issues, patches (Radicle’s pull requests) and discussions are signed data stored inside git itself, versioned and replicated just like your commits, so the social layer finally travels with the code.

Nodes gossip about repositories and replicate the ones they care about. Who speaks for a project is written into the project: its maintainers are listed, as public keys, in a document inside the repository. Seeds exist, but they just carry copies and have no say over the project. Delete every copy on every seed and any peer holding the project can re-seed it whole, issues and all. And there’s no account to ban. The project itself is the sovereign thing, and machines just hold copies of it.

Four peers drawn as small squares, one at each corner of a loose rectangle, with a dot inside every one of them. They are joined to their neighbours and one pair is joined straight across the middle. Nothing encloses them and there is nothing in the middle for them to be wired to.

Same four peers. The box surrounding them is gone, and everybody has their own full copy of the project.

I’m not a drive-by tourist here, by the way. These days my Radicle work lives at radicle.tools (opens in a new tab), but it started before that. I built the Radicle VS Code extension (opens in a new tab), which I still maintain, led the development of Radicle Planning Boards, and sat in the Radworks Product Org around the time Garden (opens in a new tab) was getting started. That’s multiple years in, long enough to earn some opinions.

And the one that wouldn’t leave me alone: Radicle had a web problem, and that web problem had exactly two doors.

The two doors

Door one: install things. Run a node and drive it from the CLI. Or run Radicle Desktop (opens in a new tab). Or use the VS Code extension (opens in a new tab) (I built that door; I am fond of that door). Good doors, I use them daily. But every one of them starts with install, and that’s where most of the internet politely turns around and leaves.

Door two: the browser. The stock web Explorer (opens in a new tab) is read-only. You can look at a repo through it, but you can’t touch it. The team’s hosted product, radicle.garden (opens in a new tab), lets you touch it, and credit where due: sign up with an email and a credit card and you can open issues, comment, react and review patches, right from the browser. No install.

Now look at how that works. Garden generates a Radicle keypair for you and keeps it in a keystore on Garden’s servers. When you write, Garden signs the change with that key, on your behalf. That’s a reasonable trade, and for plenty of people the right one. But the key that proves a change is really yours, the one thing Radicle made sovereign, sits in someone else’s keystore.

Both doors rest on the same, perfectly reasonable assumption. So either you install the real thing, or somebody’s server does it for you, key and all.

I got stuck on that assumption. A browser tab can’t do the real thing.

… Says who?

The node. In the tab. For real.

Grove (opens in a new tab) is a Radicle node that runs inside a browser tab. Not a web client politely asking some server’s API to do git things on your behalf.

On the left, one browser tab drawn as a window, holding three things: a stack of git objects, a signing key inside a dashed boundary it never crosses, and the protocol engine. A single thin line labelled relay runs from the tab to the right, where five peers are wired to some of their neighbours and not to all of them. There is no server in the middle of that mesh, and no building anywhere in the picture.

The whole node, inside the tab. The relay is drawn as a line because all it does is move bytes.

When Grove clones a repository, real git objects land on your own disk, in the browser’s private file storage (a.k.a. OPFS (opens in a new tab)). An actual git object database, actual refs, yours. Fun fact: no browser ships a way to debug OPFS, and the couple of extensions that claim to do so never worked for me, so much of Grove was built on a filesystem I could not see. Full chapter later.

Grove generates a signing key that never leaves your browser. When you open an issue or publish a patch, you sign it, with your key. Nobody signs on your behalf. Nobody can.

It speaks the actual binary Radicle protocol, the same handshake, gossip and replication stream a native node speaks. And it verifies everything it clones by itself, signature by signature, because there’s no server whose word it could take instead.

And because it’s a web app, it’s just a URL. Open it on your phone and you have a real Radicle peer in your pocket. Open it on the locked-down work machine where you’ll never get approval to install a node (IT said no; IT always says no), same thing.

It works offline too. The repo is on your disk, and so are its issues and patches, so there’s nothing to be “down”. Read, write, commit and review a patch on the plane, merge it even, and sync when the wheels touch.

One catch, before someone in the comments points it out: a browser can’t open a raw TCP socket (probably wise, all things considered), and Radicle nodes speak TCP. So between the tab and the wider network sits a thin relay that pumps bytes between a WebSocket and a TCP connection. “Aha,” you say, “so there IS a server.”

There’s a pipe. One file, about two hundred lines. It holds no state and no keys. It can’t author a change as you, forge a signature or decide which branch is canonical. The node in your tab verifies everything the relay hands it, so a lying relay only wastes its own bandwidth. Don’t like mine? Run it yourself, straight from the repo:

npm run relay

Or skip mine entirely: if a Radicle node already runs on your machine, one line of websocat (opens in a new tab) bridges your tab straight to it:

websocat -b ws-l:127.0.0.1:8080 tcp:127.0.0.1:8776

It’s a dumb pipe on purpose, and I find it fitting that the one server left in the picture is the one that can’t forge anything, given that everyone calls the incumbents “forges”.

And when the peer on the other side is another Grove, the two tabs connect directly, browser to browser, and a repository replicates with no relay and no seed anywhere in the data path. Two servers are still involved today, at the edges: a rendezvous service where the two tabs find each other, and a public STUN server that tells a tab its own address. Neither one carries a byte of the repository, and neither can forge one.

(There is also Taiga, a Radicle network explorer I am building alongside Grove, but it can have its own chapter later.)

What you are signing up for

Grove is in alpha, and this series is written from the workshop floor, sawdust included. Take cloning a real production repository into a tab. For weeks it didn’t clone at all: it would grind for ages and then crash. The first time it made it all the way through, it took all night. All. Night. Today that same repo clones in about three seconds. In between came a pile of confident theories that got challenged, measured and proven wrong one by one, and publishing your own wrong turns is, as far as I can tell, the only way to write a build log worth reading.

The upcoming chapter one has a title, which so far is the most finished part of it: “I Put a Whole Git Node in a Browser Tab. It Fought Back.” One repo’s issue list getting parsed overnight, four wrong theories, three bugs in one trench coat and a good look under Grove’s hood. It joins this series very soon. Come watch me argue with a browser.


Everything above is one craftsman’s read of the forge landscape. I fork code, not people.

Replies

Reply by email to maninak@pm.me. Those as well as Bluesky (opens in a new tab) and Mastodon (opens in a new tab) posts about this blog post may show up here.