Skip to content

Instantly share code, notes, and snippets.

@coopernetes
Created July 8, 2026 23:19
Show Gist options
  • Select an option

  • Save coopernetes/fd3971bd21823202b2b10af9a47075fe to your computer and use it in GitHub Desktop.

Select an option

Save coopernetes/fd3971bd21823202b2b10af9a47075fe to your computer and use it in GitHub Desktop.
go-git v6 viability for a fogwall-equivalent Git proxy (2026-07-08)

go-git v6 viability for a fogwall-equivalent Git proxy

Assessment date: 2026-07-08
go-git version surveyed: v6 (module github.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


Context

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?


What already exists in go-git v6

Feature Status Key type / package
HTTPS server termination (receive-pack over smart HTTP) Complete backend.Backend implements http.Handler; routes POST /git-receive-packtransport.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

Gaps — what you'd write yourself

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.

Comparison to JGit

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.


Maturity risk

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.


Bottom line

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment