// HACKER NEWS — CYBERSECURITY
Walgit – a Git server that is one binary in front of an object store
walgit hosts git repositories with no database, no leader and no local state that matters. You run a
single binary, point it at an S3 or GCS bucket, and you have: smart HTTP (v0/v2) fetch and push, bundle-uri
clones served as static files, Git LFS, a browsing web UI, a JSON API with an SDK, per-repository push policy,
webhooks — and a server that scales to repositories larger than the machine it runs on. Every machine that
runs walgit is a disposable cache; the bucket is the repository.
That is the whole deployment. Add more machines pointed at the same bucket and they serve the same repositories,
consistently, with nothing to coordinate. Kill them all and you lose warmth, nothing else.
It is a Rust implementation of the architecture Cursor described in
Git at any scale (the system they call Continuity), with the changes
needed to run it on machines that are smaller than the repository. The post is worth reading first; it is kept
verbatim in docs/reference/cursor-git-at-any-scale.md.
Git is distributed, and that makes hosting it miserable for one reason: packfiles. Everything in a repository
is compressed into large binary packs laid out to be small, not to be read in order; every git operation is a
random walk over gigabytes. That is fine on a laptop with the file in page cache and catastrophic over a network
filesystem, which is why "just put the repositories on NFS" failed at every large host that tried it. The design
that survived (GitHub's Spokes) keeps real repositories on local NVMe so upstream git does the work, and
replicates at the packfile level with strict consistency — paid for with three-phase commit across a fixed replica
set, a database that maps every repository to its machines, and a fleet of pets.
Continuity's insight changes the economics: make a write-ahead log in object storage the source of truth, and
make every on-disk repository a cache. A push is stored as an immutable object in the bucket and becomes visible
only when a tiny manifest is rewritten with a compare-and-swap. That CAS is the consensus — no election, no
quorum, no primary. Any instance may accept a push; two racing instances cannot both win. A replica that has never
seen a repository reads the log and has it. Reads are consistent without coordination because every read first
asks the store whether anything changed (a conditional GET, usually a 304). Compaction is done once by whoever holds
a lease and published into the log, so replicas download compacted packs instead of repacking. And because the
WAL is the truth, there is complete provenance: every push and every repack, replayable to any point.
walgit takes that as-is, and adds what a monorepo on small machines needs: serving refs and web pages for a
repository whose packs will never fit on the instance (a remote reader over HTTP range requests), keeping
commits and trees local while blobs stay in the bucket (the history pack), and moving clone bytes out of the
server entirely (bundle-uri: fresh clones and catch-ups are static files the bucket or a CDN hands out).
The repository is a WAL in the bucket. Under repos///: manifest.pb (tiny, CAS-rewritten:
head sequence, the live pack set, checkpoint pointer, settings — the linearization point), log/.pb
(immutable entries: PUSH, COMPACT, CHECKPOINT, SETTINGS), wal/.pack|.idx|.rev|.bitmap|.commit-graph
(immutable, content-addressed packs with their side-files), checkpoints// (folded ref snapshot + pack
inventory so a cold start is snapshot + tail), bundles/, leases/ (CAS with TTL — the only cross-instance
mutex), policy.json, lfs/objects/, events/cursor.json.
A push: our receive-pack indexes the pack (git index-pack --fix-thin --rev-index in a scratch dir), checks
connectivity and policy, uploads pack ∥ idx ∥ log entry, then CASes the manifest. On a 412 it re-reads,
re-validates every ref's old value and retries. Concurrent pushes to one repository on one instance are group
committed into one CAS. The