Assessment date: 2026-07-08
go-git version surveyed: v6 (modulegithub.com/go-git/go-git/v6), pre-stable
Reference implementation: fogwall — a Java/JGit Git proxy with store-and-forward, filter chains, and sideband progress streaming
fogwall is a server-side Git proxy that:
- Terminates HTTPS and SSH git push (acts as a real Git server, not just a TCP forwarder)
- Runs a composable pre-receive filter chain (author email, commit message, custom validators)
- Parks received packs locally, waits for external approval, then replays upstream ("store-and-forward")
- Streams live progress messages (sideband-2) back to the git client mid-push
- Supports a transparent proxy mode (inspect pack while forwarding without full termination)
The question: could fogwall be reimplemented in Go using go-git v6, and what would need to be added?
| Feature | Status | Key type / package |
|---|---|---|
| HTTPS server termination (receive-pack over smart HTTP) | Complete | backend.Backend implements http.Handler; routes POST /git-receive-pack → transport.ReceivePack(); TLS via standard net/http; working example at _examples/http-server/main.go |
| Pre-receive hook interface | Complete | transport.ReceivePackHooks{PreReceive, PostReceive} — PreReceiveInfo exposes Storer, Commands []*packp.Command, PushOptions, Progress io.Writer |
| Sideband-2 progress streaming | Complete | plumbing/protocol/packp/sideband.Muxer; PreReceiveInfo.Progress is already wired to the muxer — write "remote: Waiting for approval...\n" to it and it streams live to the client |
| In-memory pack parking | Complete | storage/memory.Storage is a full storage.Storer; pass it as st to transport.ReceivePack to absorb the pack objects without writing to disk |
| Upstream push (replay after approval) | Complete | remote.PushContext from the in-memory store to the upstream URL + creds |
| Pluggable provider routing | N/A (application code) | transport.Loader interface is trivial; provider routing (inbound path → upstream URL + creds) is entirely application logic — identical situation to JGit |
| Gap | Estimated effort | Notes |
|---|---|---|
| SSH daemon | ~150 lines | gliderlabs/ssh is already in go-git's go.mod; wire session stdin/stdout into transport.ReceivePack — the wiring just doesn't exist in the library yet. go-git's own SSH tests shell out to the real git-receive-pack binary instead. |
Inject hooks via backend.Backend |
~30 lines | backend.Backend.Serve() calls transport.ReceivePack with zero-value Hooks. Bypass backend.Backend for receive-pack and call transport.ReceivePack directly with your hook struct. |
| Composable filter chain runner | ~50 lines | The hook is a single func(ctx, *PreReceiveInfo) error, not a chain. Build a slice of hook funcs + a runner inside PreReceive. Standard Go pattern. |
| Store-and-forward orchestration (park → wait → replay, keep writer alive) | ~300–500 lines | Goroutine suspension, approval callback, timeout, keeping the HTTP/SSH response writer open during the wait. No library analog — same scope of hand-rolled code as JGit. |
| Transparent proxy (inspect pack while forwarding without termination) | ~400–600 lines | No passthrough transport exists. io.TeeReader the raw stream + plumbing/format/packfile parser (packfile.NewParser, packfile.UpdateObjectStorage) are available, but the inspect-and-forward harness is from scratch. Comparable effort to fogwall's ParseGitRequestFilter + EnrichPushCommitsFilter. |
JGit's server-side API (ReceivePack, PreReceiveHook/PostReceiveHook interfaces, sideband streaming, JGitInternalHttpGitServlet) has ~10 years of production use. go-git v6 is roughly 80–85% of the way there for the store-and-forward path. The transparent proxy path is roughly equal effort in both.
The store-and-forward orchestration and the transparent proxy harness are application-layer concerns in both languages — neither library helps or hinders you there.
go-git v6 is pre-stable (no tagged release at time of writing). The hooks API in plumbing/transport/receive_pack.go appears recent. If you build on the v6 server primitives today you are an early adopter and the API could still shift before stabilization.
The client-side go-git API is mature and widely used in production. The server-side is less so.
Viable. The foundational server primitives exist. The gaps are wiring work (~300–800 lines of new Go across all modes), not missing foundational pieces. If contribution uptake is the driving concern, Go is a significantly easier sell than Java. The tradeoff is taking an early-adopter bet on go-git v6's server API stability.