SSH Transport
Cloudflare Workers only accept HTTP requests, so gitmode includes an SSH-to-HTTP proxy that translates git SSH commands into HTTP requests to the Worker.
Architecture
Section titled “Architecture”Git Client ──SSH──▶ SSH Proxy (Node.js) ──HTTP──▶ Cloudflare Worker localhost:2222 localhost:8787The proxy:
- Accepts SSH connections and authenticates all clients (no auth in dev mode)
- Parses the git exec command (
git-upload-packorgit-receive-pack) - Fetches ref advertisement from the Worker’s HTTP endpoint
- Strips the HTTP service announcement wrapper (SSH protocol doesn’t use it)
- Streams data between the git client and Worker, translating between SSH’s bidirectional stream and HTTP request/response
Start the Proxy
Section titled “Start the Proxy”# Start the dev server firstpnpm run dev
# Start the SSH proxy (default: port 2222, forwarding to http://localhost:8787)pnpm run ssh:proxy
# Or with custom portsnpx tsx ssh/proxy.ts --port 2222 --http http://localhost:8787The proxy auto-generates an ed25519 host key on first run (stored at ssh/host_key).
Git Operations
Section titled “Git Operations”# Clonegit clone ssh://git@localhost:2222/owner/repo.git
# Pushcd repoecho "hello" > file.txtgit add file.txt && git commit -m "add file"git push
# Fetchgit fetch
# Branch operationsgit checkout -b featuregit push -u origin featuregit push origin --delete feature
# Tagsgit tag v1.0git tag -a v2.0 -m "Release v2.0"git push --tags
# ls-remotegit ls-remote ssh://git@localhost:2222/owner/repo.gitProtocol Details
Section titled “Protocol Details”Clone/Fetch (upload-pack)
Section titled “Clone/Fetch (upload-pack)”- Proxy sends
GET /owner/repo.git/info/refs?service=git-upload-packto Worker - Strips the HTTP service announcement (
# service=git-upload-pack\n+ flush) - Sends raw ref advertisement to git client over SSH
- Reads client “want” and “have” lines until
done\n - Forwards to
POST /owner/repo.git/git-upload-pack - Sends packfile response back to client
Push (receive-pack)
Section titled “Push (receive-pack)”- Proxy sends
GET /owner/repo.git/info/refs?service=git-receive-packto Worker - Strips HTTP service announcement, sends refs to client
- Reads client ref-update pkt-lines until flush (
0000) - Detects whether a packfile follows:
- If next 4 bytes are
PACK→ reads until EOF (regular push) - If no data within 200ms → delete-only push (no packfile)
- If next 4 bytes are
- Forwards everything to
POST /owner/repo.git/git-receive-pack - Sends sideband-wrapped response back to client
Sideband Passthrough
Section titled “Sideband Passthrough”The git client advertises side-band-64k capability over SSH. The proxy preserves this — it forwards the capability to the HTTP server and passes sideband-wrapped responses back unchanged. This means progress messages and error reporting work correctly.
Conformance Tests
Section titled “Conformance Tests”The SSH transport is covered by 27 conformance tests:
pnpm run test:sshTests cover: clone, push, incremental push, fetch, branch create/delete, lightweight tags, annotated tags, push --tags, ls-remote, 64KB binary file roundtrip, nested directories, repo isolation, and SSH/HTTP interop.
Limitations
Section titled “Limitations”- Development only — The proxy runs as a standalone Node.js process. It cannot run on Cloudflare Workers (no inbound TCP).
- No authentication — All SSH connections are accepted. Add authentication by checking the public key in the
authenticationevent handler inssh/proxy.ts. - Single server — The proxy is a single process. For production SSH support, you’d need a proper SSH server (e.g., on a VM) proxying to the deployed Worker.