Gitfed
bastien-mrq/gitfed/ Commits/ bec2335

Add README

What the project is, how the certificate-based federation model works, what each binary does, and how to run it both locally and for real (pointing at deploy/k8s/README.md for the full deployment walkthrough).

bastien-mrq 2026-07-28 16:00 commit bec23359938bf420632d9eedadb1a8f29dc1c25c parent 2c6a1f95af2da4d0e08e825132521bd0d03ff9d9
1 files changed +160 −0
A README.md +160 −0
README.md
diff --git a/README.md b/README.md new file mode 100644 index 0000000..d4c0c7f --- /dev/null +++ b/README.md @@ -0,0 +1,160 @@ +# gitfed + +A self-hosted git server where pushing to someone else's instance doesn't +mean creating a second account there. Your home instance vouches for you — +literally, via a short-lived SSH certificate it signs — and any instance +that trusts your home instance's certificate authority accepts it. + +Current version: see [CHANGELOG.md](CHANGELOG.md) (also served at +`/changelog` in the web UI, next to a `gitfed X.Y.Z` link in the footer). + +## What this is (and isn't) + +- **Git over SSH only.** No HTTP git protocol, no reimplementation of git + itself — gitfed wraps `git-upload-pack`/`git-receive-pack` as + subprocesses and lets git do what git does. +- **Federated identity, not federated data.** A repo lives on exactly one + instance; there's no replication or mirroring. Federation is purely + about who's allowed to push/pull, carried by certificates instead of + per-instance accounts. +- **Not a GitHub/GitLab clone**, even though the web UI looks like one. + The point of the project is the identity model above — everything else + (file browser, self-service repos, admin panel) exists to make that + model usable day to day, not to compete on feature count. + +Full design rationale and the protocol-level detail live in +[DESIGN.md](DESIGN.md); this file is the practical "what do I actually run" +version. + +## How it works + +- Each instance generates its own ed25519 **certificate authority** on + first run. A local user with a registered SSH key can ask their own + instance for a certificate (`ssh git@host gitfed-cert`, or the + `gitfed-renew-cert` helper below); the certificate embeds the principal + `<username>@<domain>` and expires in a couple of days by default. +- To let someone from another instance collaborate on a repo, an admin (or + the repo owner) grants their principal a role — `read`, `write`, or + `admin`. The first time a given remote domain shows up, the instance + fetches `https://<domain>/.well-known/gitfed.json` to learn that + instance's CA public key, and either trusts it immediately or holds it + **pending** for an admin to approve, depending on the configured trust + policy (whitelist by default, auto-trust/TOFU opt-in). +- Once a domain is trusted, verifying a certificate from it is entirely + local — no network call on every SSH connection, no dependency on the + remote instance being reachable at push/pull time. +- Repos can be public (readable by any authenticated principal, from any + trusted domain, without an explicit grant) or private (owner and + explicitly granted collaborators only). Write access always requires an + explicit grant regardless of visibility. + +## What's included + +- **`gitfed-server`** — the daemon: SSH server (git + cert issuance) and + the `/.well-known/gitfed.json` HTTP endpoint. +- **`gitfed-web`** — the web UI: a public, unauthenticated repo browser + (file tree, rendered README/LICENSE, tags, topics) plus, behind a + username/password login, self-service (your own keys and repos) and an + admin section (users, trust store, audit log) gated by an admin flag on + the account. The web login only ever grants access to this UI — git + push/pull always goes over SSH with a key or certificate, independently + of it. +- **`gitfed-tui`** — a terminal admin tool that talks to the same store + gitfed-web does. It's the way to bootstrap the very first account (there + is no self-registration), and works even if the web UI is down, since it + can also open the database directly when the server isn't running. +- **`gitfed-renew-cert`** — a small client-side tool end users run on + *their own* machine (e.g. from a cron job or systemd timer) to renew + their certificate shortly before it expires. + +## Project layout + +``` +cmd/ + gitfed-server/ SSH server + well-known endpoint + gitfed-web/ web UI (public browsing + self-service + admin) + gitfed-tui/ terminal admin tool + gitfed-renew-cert/ client-side certificate renewal helper +internal/ + ca/ CA key generation + certificate signing + ssh/ SSH server, auth, ACL enforcement, git exec + federation/ .well-known discovery, trust store + acl/ repo access-control checks + gitexec/ git-upload-pack/receive-pack + tree/blob reads + store/ bbolt-backed persistence + admin/ the Ops interface both TUI and web drive + adminrpc/ lets gitfed-web talk to a *running* server + over a Unix socket instead of racing it for + the store's exclusive file lock + opsconnect/ picks live-socket vs. direct-store mode +deploy/ + docker/Dockerfile multi-stage build for server/web/tui + k8s/ manifests + deployment README (k3s/Traefik/ + cert-manager) + update.sh version bump + build + deploy in one command +DESIGN.md architecture and protocol rationale +CHANGELOG.md version history +``` + +## Running it locally + +```sh +go build -o bin/gitfed-server ./cmd/gitfed-server +go build -o bin/gitfed-web ./cmd/gitfed-web +go build -o bin/gitfed-tui ./cmd/gitfed-tui + +./bin/gitfed-server -init localhost -data-dir data -config gitfed.json +# edit gitfed.json if you want different ports (defaults: ssh :2222, http :8443) +./bin/gitfed-server -config gitfed.json & + +./bin/gitfed-tui -config gitfed.json +# Users -> a (add user): username, your SSH public key, a password, admin: y + +./bin/gitfed-web -config gitfed.json & +# open http://localhost:8088, log in with what you just created +``` + +Cloning/pushing always goes over SSH: + +```sh +git clone ssh://git@localhost:2222/<user>/<repo> +``` + +## Deploying for real + +The setup this project actually runs on is a single-node k3s VPS with +Traefik and cert-manager already handling another app (specifically, +[ess-helm](https://github.com/element-hq/ess-helm)) — gitfed reuses both +rather than standing up its own ingress/TLS. Full walkthrough, including +why the deployment is shaped the way it is (bbolt's single-writer lock, +why there are two containers in one pod, why the SSH port can't be 22), +is in **[deploy/k8s/README.md](deploy/k8s/README.md)**. + +Short version: + +```sh +docker build -f deploy/docker/Dockerfile -t gitfed:latest . +docker save gitfed:latest -o gitfed.tar # then import into your cluster's runtime +kubectl apply -f deploy/k8s/ # namespace, configmap, pvc, deployment, service, ingress +``` + +For every update after the first, use `deploy/update.sh` instead of +repeating that by hand — it bumps `VERSION`, builds and imports an image +tagged with the actual version (not just `:latest`), points the deployment +at it, applies, and commits: + +```sh +# add a "## X.Y.Z" section to CHANGELOG.md describing the change first +deploy/update.sh # patch bump +deploy/update.sh minor # or: major, or an explicit X.Y.Z +``` + +## Backups + +Everything that matters lives on one persistent volume: the database +(users, repos, ACLs, sessions, trust store, audit log), the instance's CA +key, the SSH host key, and the bare repos themselves. Losing the CA key +specifically invalidates every certificate this instance has ever issued +and breaks every federated trust relationship pointing at it — there's no +recovery short of everyone re-establishing trust from scratch. Back up the +whole volume, not just the git data.