objgitd is a single-binary git server. It stores repositories as objects in
a Tigris bucket, and not on a local filesystem.
Module path: github.com/tigrisdata/objgit. Go 1.26.
One backend answers three transports:
| Transport | Flag | Default | Notes |
|---|---|---|---|
| Smart HTTP | -http-bind |
:8080 |
The primary transport. Carries HTTP Basic. |
| git:// | -git-bind |
:9418 |
Unauthenticated TCP. Opt-in. |
| SSH | -ssh-bind |
off | Public-key. Opt-in. Host key lives in the bucket. |
All three funnel authorization through one pluggable
internal/auth.Authorizer.
Git LFS rides on the Smart HTTP listener, behind -allow-lfs. It is HTTP even
for an ssh:// remote: SSH only answers git-lfs-authenticate, which names the
HTTP endpoint. See docs/architecture/lfs.md.
A fourth listener serves Prometheus metrics at /metrics and a /healthz
probe endpoint (-metrics-bind, default :9090). An empty value disables it.
go build ./... # build everything
go build -o objgitd ./cmd/objgitd
go test ./... # full test suite
go test ./cmd/objgitd/... # protocol tests
go test -run TestSmartHTTP ./cmd/objgitd/...
go test -run TestSSH ./cmd/objgitd/...
# Run locally.
./objgitd -bucket $BUCKET -http-bind :8080 -allow-push
./objgitd -bucket $BUCKET -ssh-bind :2222 -allow-push # git clone ssh://git@host:2222/repo.git
Notes on the tests and on configuration:
- Protocol tests need
giton PATH. They skip themselves without it. - SSH tests also need
sshandssh-keygenon PATH. They skip themselves without them. - Flags can also come from the environment through
flagenv, which maps each flag to UPPER_SNAKE.-allow-pushbecomesALLOW_PUSH, and-bucketbecomesBUCKET. godotenvloads a.envfile from the working directory at startup.- The
.wasmfiles inbin/are Git LFS objects. Rungit lfs pullbefore you build. Without them,TestRepoBinfails. The image copiesbin/to/usr/libexec/objgit/bin. For a local run with hooks, setWASM_PATH=./bin. TestClusterininternal/kubeneeds a real cluster. It skips itself without theOBJGIT_TEST_KUBE_*variables.- Tigris client credentials come from the standard AWS SDK chain, such as
AWS_PROFILE.
| Path | Purpose |
|---|---|
cmd/objgitd/main.go |
Builds the one *daemon and starts every listener. |
cmd/objgitd/git_protocol.go |
The git:// server. Also holds operationFor and (*daemon).authorize. |
cmd/objgitd/http.go |
Smart HTTP. *daemon is the http.Handler itself. |
cmd/objgitd/ssh.go |
The SSH server and its per-session dispatch. |
cmd/objgitd/shell.go |
The SSH sh command: an interactive shell in the hook sandbox. |
cmd/objgitd/receivepack.go |
The go-git fork that streams hook output, plus writePack. |
cmd/objgitd/hooks.go |
Ref diffing and the sandboxed hook run. |
cmd/objgitd/snapshots.go |
The erofs snapshot run after a push. |
cmd/objgitd/lfs.go |
The Git LFS HTTP handlers and git-lfs-authenticate over SSH. |
internal/auth |
The one authorization interface. |
internal/kube |
The in-cluster API client, kube:apply, and tekton:pipelinerun. |
internal/wasmbin |
Runs the WASI programs in -wasm-path, such as kustomize, as hook commands. |
internal/lfs |
Git LFS: protocol types, the bucket store, and the presigner. |
internal/repofs |
Maps a repository path to a storage.Storer. |
internal/storage/tigris |
Repository storage. A storage.Storer on the bucket. |
internal/bundler |
The async upload queue behind that storer. |
internal/s3fs |
Daemon-level state only, which is the SSH host key. |
internal/mountfs, internal/treefs, internal/kefkash |
The hook sandbox filesystem and shell wiring. |
internal/metrics |
Every Prometheus vector, plus thin helpers. |
internal/gittest |
Isolate, which keeps the real git client in tests away from this repository. |
internal/snapshot |
erofs images of git trees: Ensure, Open, and the Store interface. |
internal/slog.go |
JSON handler init. |
bin/ |
WASI programs for hooks (Git LFS). The image copies them to /usr/libexec/objgit/bin. |
cmd/membench/ |
Push memory benchmark harness. Not shipped; see docs/usage/memory-benchmark.md. |
Read docs/architecture/README.md first. It describes the daemon and links to one page for each subsystem.
| Page | Read it before you change... |
|---|---|
| transports.md | Any transport. It holds two protocol points that are easy to get wrong. |
| auth.md | Credentials, decisions, or a new Authorizer. |
| hooks.md | Push hooks, output streaming, the sandbox, or its Kubernetes commands. |
| metrics.md | Any metric or instrumentation seam. |
| snapshots.md | Snapshot images, the snapshot cache, or runSnapshots. |
| tigris-storer.md | Object layout, refs, packs, the pack cache, or the upload path. |
| s3fs.md | The billy.Filesystem over the bucket. |
| lfs.md | Git LFS: the batch API, presigned transfers, locks, or the blob layout. |
Two more directories carry detail:
docs/reference/— the.cuebinary layout, the failure modes, and the build order.docs/usage/— how to use a feature, such as a hook script, or the push memory benchmark.
- Flags use kebab-case, paired with
flagenvfor the environment fallback. sloguses"err"as the error key, and never"error".- Errgroup owns the server lifecycle.
signal.NotifyContextprovides cancellation. HTTP shutdown uses a 10scontext.WithTimeout. - Tests are table-driven with
tt. Gate a test withexec.LookPath("git")when it shells out to a real git client. Seehttp_test.goandgit_protocol_test.go. - Reuse the shared test helpers
runGit,tryGit, andseedRepo. They live ingit_protocol_test.go. - If a package runs the real git client in its tests, call
gittest.Isolatefrom itsTestMain. The huskycommit-msghook runsgo test, and git exportsGIT_DIRto hooks. WithoutIsolate, a test can write to the config that all worktrees share and setcore.bare = true. - Put a plan for non-trivial work in
docs/plans/.git-http-protocol.mdshows the style. - Put architecture notes in
docs/architecture/, and not in this file. Keep this file an index.
When you work with Tigris buckets, access keys, or IAM, use the
tigris-storage skills.
Tigris is S3-compatible. The client here is
github.com/tigrisdata/storage-go, which is a thin Tigris-aware wrapper.