| 📁 | .cargo |
| 📁 | examples |
| 📁 | scripts |
| 📁 | src |
| 📁 | tests |
| 📄 | .gitignore |
| 📄 | AGENTS.md |
| 📄 | Cargo.lock |
| 📄 | Cargo.toml |
| 📄 | LICENSE |
| 📄 | README.md |
| 📄 | TODOS.md |
| 📄 | notes |
| 📄 | package-lock.json |
| 📄 | package.json |
| 📄 | vitest.config.mjs |
| 📄 | wrangler.toml |
Recent commits
- 21dba30 fix img preview 1 day ago
- 9670866 add error test 1 day ago
- bdbd041 add sideband progress and fatal git responses 1 day ago
- 791335e conflict tests 1 day ago
- c2b8733 issue, pr, ownership tests 1 day ago
ripgit
A self-hostable git remote backed by Cloudflare Durable Objects. One DO per repo, SQLite storage, FTS5 search, delta compression via xpatch. Built in Rust with workers-rs.
Live example: git.theagents.company — deathbyknowledge’s repos including agents (~1k commits) and curl (~40k commits)
git remote add origin https://your-worker.workers.dev/username/myproject
git push origin main
Features
- Standard git remote —
git push,git clone,git fetchwith any git client - Auth via Service Binding — sits behind an auth worker; public read, owner-only write. GitHub OAuth example in
examples/github-oauth/ - Agent-first UI — browsable pages also negotiate
text/markdownandtext/plain, with explicit actions and curl-friendly paths - Web UI — file browser, commit history, diffs, code search, syntax highlighting, branch selector, markdown README, repo settings
- Full-text search — FTS5 over file content and commit messages. Supports
@author:,@message:,@path:,@ext:,@content:query prefixes - Raw file serving —
/:owner/:repo/raw/:ref/*path - Read API — refs, commits, trees, files, diffs, search, stats
- Repo registry — repos listed on the owner profile page after first push
- Delta compression — 5–20x compression on real repos depending on file churn
- One DO per repo — strict isolation, scales horizontally
URLs
/:owner/ owner profile — lists your repos
/:owner/:repo/ repo home — file tree, README, recent commits
/:owner/:repo/commits commit history
/:owner/:repo/commit/:sha commit detail with diff
/:owner/:repo/tree/:ref/* directory browser
/:owner/:repo/blob/:ref/* file viewer
/:owner/:repo/raw/:ref/* raw file bytes
/:owner/:repo/search-ui full-text search
/:owner/:repo/settings stats, index rebuilds, config, delete (owner only)
API
All endpoints under /:owner/:repo/.
| Endpoint | Description |
|---|---|
GET /refs | List branches and tags |
GET /log?ref=main&limit=50 | Commit history |
GET /commit/:hash | Single commit |
GET /tree/:hash | Directory listing |
GET /blob/:hash | File content |
GET /file?ref=main&path=src/lib.rs | File at ref + path |
GET /search?q=TODO | Code search |
GET /search?q=fix&scope=commits | Commit message search |
GET /diff/:sha | Commit diff |
GET /compare/base...head | Two-commit comparison |
GET /stats | Compression and storage stats |
Text Mode For Agents
Browsable pages support negotiated text mode for curl, scripts, and agents.
curl -H 'Accept: text/markdown' https://your-worker.workers.dev/alice/repo/
curl -H 'Accept: text/plain' https://your-worker.workers.dev/alice/repo/commits
curl 'https://your-worker.workers.dev/alice/repo/tree/main/src?format=md'
Accept: text/markdownreturns a markdown viewAccept: text/plainreturns the same content as plain text?format=mdor?format=textworks when you can’t keep headers attached while following links- Text pages list bare GET paths under headings like
Files (GET paths)and spell out POST actions, fields, and requirements in anActionssection
Authentication
ripgit reads identity from trusted X-Ripgit-Actor-* headers, which are only settable by an upstream auth worker via Service Binding (not from the public internet).
- Anonymous — read access to all repos
- Authenticated — read + write to repos under your username
GitHub OAuth example
examples/github-oauth/ is a TypeScript Cloudflare Worker that authenticates with GitHub, issues session cookies for browsers and long-lived tokens for agents/scripts, and forwards requests to ripgit via Service Binding. Its landing page and /settings also support the same text-mode negotiation for curl-driven agents.
See examples/github-oauth/README.md for a focused deploy/setup guide.
Local dev:
cd examples/github-oauth
npm install
npm run dev:full # auth worker on :8787, ripgit as service binding
Visit http://localhost:8787 → sign in → go to /settings → create a token → push:
git remote add origin http://username:TOKEN@localhost:8787/username/myrepo
git push origin main
First-time setup:
- Create a GitHub OAuth App — callback URL:
http://localhost:8787/oauth/callback - Set
GITHUB_CLIENT_IDinexamples/github-oauth/wrangler.toml wrangler secret put GITHUB_CLIENT_SECRETwrangler secret put SESSION_SECRETwrangler kv namespace create OAUTH_KV→ fill IDs intowrangler.toml
Deploy:
wrangler deploy # ripgit worker
cd examples/github-oauth && wrangler deploy # auth worker
Update the GitHub OAuth App’s callback URL to your deployed auth worker URL.
Push test script
./scripts/push-test.sh -u username -t TOKEN -w https://your-worker.dev -r /path/to/repo
Setup (ripgit only, no auth)
Prerequisites: Rust, wrangler, LLVM (for zstd-sys).
brew install llvm
git clone https://github.com/your-org/ripgit
cd ripgit
wrangler kv namespace create REGISTRY # fill ID into wrangler.toml
wrangler deploy
Without the auth worker in front, all repos are publicly readable and writable by anyone with the URL.
Architecture
browser / git client / agent
│
▼
Auth Worker (examples/github-oauth — optional, recommended)
│ validates session/token, sets X-Ripgit-Actor-* headers
│ Service Binding
▼
ripgit Worker (entry, routing)
│ /:owner/:repo/* → DO named "{owner}/{repo}"
│ /:owner/ → profile page (queries REGISTRY KV)
▼
Repository Durable Object (one per repo)
├── schema.rs 11 tables + 3 FTS5 virtual tables
├── pack.rs streaming pack parser + pack generator
├── git.rs smart HTTP protocol (receive-pack, upload-pack)
├── store.rs delta compression, commit graph, FTS rebuild
├── api.rs read API
├── diff.rs tree diff + line-level diffs
└── web.rs server-rendered HTML (9 pages)
▼
SQLite (up to 10 GB per DO)
KV namespaces:
REGISTRY — "repo:{owner}/{repo}" written on first push
OAUTH_KV — tokens, sessions (auth worker only)
Pushing large repos
Cloudflare Workers has a 100 MB request body limit. Push large repos incrementally:
./scripts/push-test.sh -u username -t TOKEN -r /path/to/repo -s 200
Or manually in checkpoints:
STEP=250
for FP in $(seq $STEP $STEP $(git rev-list --first-parent --count main)); do
SHA=$(git rev-list --reverse --first-parent main | sed -n "${FP}p")
git push origin "${SHA}:refs/heads/main"
done
git push origin main
Known limitations
- DO storage timeout — pushes with >~10K objects per push can exceed the 30 s timeout; push incrementally
- 100 MB request body limit — hard Workers platform constraint
- No force push — may produce inconsistent state
- No annotated tags — silently dropped; lightweight tags work
License
AGPL-3.0. See LICENSE.