feat(lfs): git-lfs-authenticate SSH handoff + fix locking over SSH remotes #42
fix/lfs-ssh-authenticate
into main
Problem
Reported: over an SSH remote, git lfs prints “Remote does not support the Git LFS locking API” and git push hangs.
Root cause
Git LFS does not transfer over SSH. For an SSH remote the client runs git-lfs-authenticate <path> <upload|download> over the SSH connection and uses the returned JSON to discover the HTTP LFS endpoint, then drives Batch / transfer / locks over HTTP.
The library shipped no handler for this command (rg git-lfs-authenticate returned nothing). So an SSH client could not discover the endpoint — it fell back to a guessed HTTPS URL, POST /locks/verify failed (→ the “not supported” message), and the object transfer hung against the unreachable guess. This was invisible because every existing LFS interop test sets an explicit lfs.url and pushes over HTTP (issue #58 scoped LFS to HTTP + PAT auth only; SSH was never considered).
Reproduced directly with the real git lfs 3.7 binary before fixing — a missing/guessed authenticate href produces exactly the reported symptom (connection refused on batch + “does not support locking API”).
Changes
- #65 —
ExGitObjectstore.Lfs.Authenticate.handle/3: a pure module (same style asBatch/Transfer/Locks) that builds the spec JSON (href,header,expires_in/expires_at) a server writes to stdout for the SSH command. The library does not invent an auth scheme: the consumer supplies:href(it alone knows its public HTTP base) and any:header; the module validates operation (:bad_operation), href (:missing_href) and lfs-configured (:lfs_not_configured). Telemetry at[:ex_git_objectstore, :lfs, :authenticate]. - #66 —
InteropSshTest: drives realgit lfsover anssh://remote with no explicit lfs.url, standing in for a server’s SSH dispatcher with a fakecore.sshCommandthat declinesgit-lfs-transfer(forcing the authenticate fallback) and answersgit-lfs-authenticatewith bytes fromAuthenticate.handle/3. Proves: push round-trips the object, locking is reachable (no “does not support locking”, no hang), and push-time/locks/verifysucceeds even withlocksverify=true.
The locking defect (#66) had the same root cause as #65; the authenticate handoff makes the endpoint discoverable, so locking works and the push completes.
Testing
- 9 unit tests for
Authenticate(ops, header passthrough, expiry, validation, telemetry). - 3 SSH interop tests against real
git lfs3.7 (push, lock/list/unlock, push-time verify withlocksverify=true). - Full suite: 985 passed, 0 failures.
mix formatclean,credo --strictclean on all new files,anvil requirement statuspasses (REQ-LFS-007, REQ-LFS-008).
Consumer follow-up (not in this library PR)
The Anvil git server must wire its SSH command dispatcher to recognise git-lfs-authenticate and call Authenticate.handle/3 (docs/example in the module). Tracked separately from this library change.
🤖 Generated with Claude Code