- Go 75.5%
- JavaScript 24%
- CSS 0.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
F1: send OIDC nonce + PKCE S256 challenge at login; verify the nonce claim and require the one-time walgit_pkce verifier cookie at the callback. F5: wgtPrincipal rejects non-token wires (session-cookie wire with forged prefix no longer authenticates). F6: code exchange uses a 10 s per-attempt timeout and retries transport errors and 5xx only. F7: JWKS singleflight followers share the leader outcome instead of failing. F8: claimed-ticket hop uses the documented 60 s window. Docs updated to match. Rollback: auth.mode=token. |
||
| .github/workflows | ||
| .woodpecker | ||
| cmd/walhub | ||
| docs | ||
| internal | ||
| web | ||
| .dockerignore | ||
| .gitignore | ||
| AGENTS.md | ||
| CODE_OF_CONDUCT.md | ||
| compose.standalone.yml | ||
| compose.yaml | ||
| DESIGN.md | ||
| DEVIATIONS.md | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
walhub
A git host in Go: git over smart HTTP (v0/v2), LFS, bundle-uri, a JSON API with SSE, and a web UI — where the object store is the only database.
It serves repositories whose entire state — refs, packs, config, policy, events, web UI — lives as objects in a bucket (filesystem, S3, or GCS). Instances are disposable; wipe one and you lose nothing but warmth.
No politics allowed — see our Code of Conduct.
Inspired by walgit
walhub exists because of Tobi Lütke's fantastic walgit. It proved that a git host can put everything on an object store, and it is the direct inspiration for this project. Thank you, Tobi.
We aim to stay object-protocol compliant with walgit: the bucket key layout, the protobuf wire encoding, and git wire behavior all follow walgit's formats. The visual direction is our own.
Quick start
make build # bundles the SDK (esbuild) and compiles the binary — web/ is embedded
./walhub # zero-config: 0.0.0.0:8080, filesystem store, auth "none" (loud warning)
Then open http://localhost:8080 — the SPA renders, /setup configures everything for real use. No config file exists yet, so the first run is deliberately friction-free; save one from /setup and restart. An invalid config file puts the server in setup-only mode (everything but setup/health answers 503) until you fix it through the same UI.
Push something — repositories auto-create on push by default:
mkdir demo && cd demo && git init -b main
echo "# demo" > README.md && git add -A && git commit -m "hi"
git remote add origin http://localhost:8080/you/demo.git
git push -u origin main # browse it at http://localhost:8080/you/demo
Auth: none (everyone anonymous — dev only), token (static bearer/basic tokens), or oidc (any OpenID Connect issuer, plus walgit-issued wgt_ access tokens minted in the UI).
Run with docker compose (prebuilt image)
GitHub Actions publishes the image on every push to ghcr.io/crueber/walhub — pull it instead of building:
# docker-compose.yml — walhub alone: filesystem store on a named volume
services:
walhub:
image: ghcr.io/crueber/walhub:latest # :main tracks main; vX.Y.Z releases; sha-<sha> per commit
ports:
- "8080:8080"
- "2222:2222" # git over SSH (17_ssh.md)
environment:
WALHUB__SERVER__SSH__LISTEN: 0.0.0.0:2222
volumes:
- walhub-data:/var/lib/walhub
restart: unless-stopped
volumes:
walhub-data:
docker compose up -d # update later with: docker compose pull && docker compose up -d
Then open http://localhost:8080/setup — the same zero-config first boot as the binary: save a config from the setup page and restart the container. Repositories auto-create on push under http://localhost:8080/<owner>/<repo>.git.
Git also works over SSH: the stack publishes port 2222 and enables the SSH transport (WALHUB__SERVER__SSH__LISTEN). Add your public key on the /keys page, then:
git clone ssh://git@localhost:2222/<owner>/<repo>.git
The host key auto-generates into the data volume, so it is stable across container restarts.
For an S3-backed store (rustfs/MinIO/GCS), see compose.yaml — the shipped stack builds from source; to run it from the published image instead, replace the walhub service's build: . with image: ghcr.io/crueber/walhub:latest (the rustfs service and the WALHUB__STORE__* env stay as they are). compose.standalone.yml is the same standalone shape as above, built from source instead of pulled.
v1 release requirements
- git storage
- issues
- pull requests
- tags
- releases
- packages
- fork
- webhooks
- oidc
v1.1
Backlog
- file edit
- pr/merge protection rules
- projects
- wiki
- insights
- moderation
- sponsorships
- forum
- ownership transfer
Development
make ci # vet + fast tests + race + ≥95% coverage per package — the merge gate
make contract # store contract suite (memory + filesystem; S3 via `make dev-store`)
make e2e # real-git end-to-end flows
make test-web # node --test over the headless JS modules
make image # OCI image
Backend: Go 1.27 (module git.packden.us/crueber/walhub), exactly three third-party modules (chi, BurntSushi/toml, x/net). Frontend: any Node for tests; pnpm 11 for the vite/esbuild build (pnpm --dir web install). CI is Woodpecker on the Forgejo origin (pipeline.yaml); GitHub Actions (.github/workflows/docker.yml) tests the mirror and publishes the image to GHCR on every push. The container build is Dockerfile, with compose examples in compose.standalone.yml (filesystem store) and compose.yaml (S3-backed via rustfs).
License
MIT © Christopher Rueber