Summary
Add SSH transport alongside the existing HTTP store-and-forward flow so users can git push to jgit-proxy over SSH and have the same pre-receive hook chain (validation, approval, forwarding) run against their push.
Transparent-proxy mode is out of scope — SSH has no meaningful transparent-proxy equivalent outside of SOCKS.
Feasibility analysis
Verdict: feasible. Moderate effort, one real hard problem (auth forwarding).
Library choice
JGit ships no SSH server — only SSH client transports (org.eclipse.jgit.ssh.apache, org.eclipse.jgit.ssh.jsch). The server has to come from Apache MINA SSHD directly, specifically its sshd-git module, which provides GitPackCommandFactory — a ready-made SSH command handler for git-receive-pack / git-upload-pack that hands us a ReceivePack instance to configure.
Docs: https://github.com/apache/mina-sshd/blob/master/docs/git.md
Hook chain reuse
The good news: JGit's ReceivePack.receive(InputStream, OutputStream, OutputStream) is transport-agnostic. Hooks set via setPreReceiveHook / setPostReceiveHook fire on parsed ReceiveCommand objects — they never touch HTTP. rp.sendMessage() sideband also works over raw SSH streams because it's part of the pack protocol, not HTTP.
The current factory jgit-proxy-core/.../StoreAndForwardReceivePackFactory.java is parameterized on HttpServletRequest. For SSH we'd write a parallel adapter parameterized on MINA SSHD's ServerSession, reusing the exact same hook instantiation logic. The hooks themselves transplant unchanged. Only credential extraction and repo resolution differ (SSH principal + command arg vs. Basic header + path).
Minimal wiring sketch
SSH "git-receive-pack <repo>"
→ MINA SSHD GitPackCommandFactory
→ GitPackConfiguration.configureReceivePack(session, rp)
(install existing hook chain via SSH-flavored ReceivePackFactory)
→ rp.receive(sshIn, sshOut, sshErr)
→ existing hooks fire identically; sideband `remote:` messages stream live
The hard problem: auth forwarding
HTTP mode forwards the client's Basic credentials upstream. SSH pubkey auth gives us nothing forwardable — the private key never leaves the client. Three realistic options:
- Proxy-owned bot PAT (recommended start). Map the SSH principal to a stored upstream token. Reuses
ForwardingPostReceiveHook with zero change. Simplest path to a working SSH listener.
- Per-user upstream token mapping. Needs a user → upstream-token store; same push-then-forward shape as today. Natural second phase.
- SSH agent forwarding. MINA SSHD supports server-side agent forwarding, but wiring JGit's
SshSessionFactory to use the forwarded agent for the upstream push is a real spike with cross-network edge cases. Most transparent UX if we can get it working.
Risks to validate with a spike
- Sideband flush timing under long-running hooks (approval polling, secret scan) — confirm
remote: lines stream live over SSH the way they do over HTTP, not buffered until the channel closes.
- Orphan refs on upstream-push failure — store-and-forward already has this risk; auth expiry over SSH may make it more likely.
- Option 3 (agent forwarding) viability if we want true transparent auth — worth a throwaway spike before committing to it as the target design.
Effort estimate
~2-3 weeks for option-1 SSH server (MINA SSHD integration + SSH-flavored ReceivePackFactory + e2e tests with a real OpenSSH client).
Summary
Add SSH transport alongside the existing HTTP store-and-forward flow so users can
git pushto jgit-proxy over SSH and have the same pre-receive hook chain (validation, approval, forwarding) run against their push.Transparent-proxy mode is out of scope — SSH has no meaningful transparent-proxy equivalent outside of SOCKS.
Feasibility analysis
Verdict: feasible. Moderate effort, one real hard problem (auth forwarding).
Library choice
JGit ships no SSH server — only SSH client transports (
org.eclipse.jgit.ssh.apache,org.eclipse.jgit.ssh.jsch). The server has to come from Apache MINA SSHD directly, specifically itssshd-gitmodule, which providesGitPackCommandFactory— a ready-made SSH command handler forgit-receive-pack/git-upload-packthat hands us aReceivePackinstance to configure.Docs: https://github.com/apache/mina-sshd/blob/master/docs/git.md
Hook chain reuse
The good news: JGit's
ReceivePack.receive(InputStream, OutputStream, OutputStream)is transport-agnostic. Hooks set viasetPreReceiveHook/setPostReceiveHookfire on parsedReceiveCommandobjects — they never touch HTTP.rp.sendMessage()sideband also works over raw SSH streams because it's part of the pack protocol, not HTTP.The current factory
jgit-proxy-core/.../StoreAndForwardReceivePackFactory.javais parameterized onHttpServletRequest. For SSH we'd write a parallel adapter parameterized on MINA SSHD'sServerSession, reusing the exact same hook instantiation logic. The hooks themselves transplant unchanged. Only credential extraction and repo resolution differ (SSH principal + command arg vs. Basic header + path).Minimal wiring sketch
The hard problem: auth forwarding
HTTP mode forwards the client's Basic credentials upstream. SSH pubkey auth gives us nothing forwardable — the private key never leaves the client. Three realistic options:
ForwardingPostReceiveHookwith zero change. Simplest path to a working SSH listener.SshSessionFactoryto use the forwarded agent for the upstream push is a real spike with cross-network edge cases. Most transparent UX if we can get it working.Risks to validate with a spike
remote:lines stream live over SSH the way they do over HTTP, not buffered until the channel closes.Effort estimate
~2-3 weeks for option-1 SSH server (MINA SSHD integration + SSH-flavored
ReceivePackFactory+ e2e tests with a real OpenSSH client).