fix(git-gate): make the gateway access-hook executable regardless of copy transport #403
Reference in New Issue
Block a user
Delete Branch "fix-git-gate-access-hook-exec-bit"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #404.
Summary
Cloning/fetching from the per-agent git-gate on the Apple-container backend failed with
git clonereporting "empty reply from server" (curl exit 52). It looked like an auth/network problem but was neither — attribution and routing worked; the gateway's git-http handler was crashing on every upload-pack:The access-hook is exec'd directly, so it needs the x bit.
prepare()stages it0o700and trusted the gateway copy to carry that mode.docker cppreserves source mode; the Applecontainer cp(AppleGatewayTransport.cp_into) does not, landing the hook0o644→EACCES. The unhandled exception killed the handler thread, closing the socket with no HTTP response — the opaque "empty reply."Fix
provision_git_gate(backend-neutral):chmod +xthe access-hook on the gateway side after the copy, so it's executable under every transport (docker / Apple / firecracker-ssh) rather than depending on the copy to preserve mode.subprocess.run— anOSError/SubprocessError(un-execable, timed out) now fails closed with a 503 instead of crashing the thread into an empty reply. A gate that can't run its hook should deny, visibly.git_gate.prepare().Tests
test_gateway_provision.py: provisioning applieschmod +xto the access-hook.test_git_http_backend.py: the handler returns 503 (not an empty reply) when the hook can't be exec'd.All 21 tests in the two affected files pass.
Note
The live "Claude Agent 1" bottle was unblocked out-of-band with
container exec bot-bottle-mac-infra chmod +x /etc/git-gate/access-hook; this PR makes the fix durable across gateway restarts / re-provisions.Cloning/fetching from the git-gate on the Apple-container backend failed with "empty reply from server" (curl exit 52). Root cause: the git-http handler crashed on every upload-pack with PermissionError: [Errno 13] Permission denied: '/etc/git-gate/access-hook' The access-hook is exec'd directly, so it needs the x bit. prepare() stages it 0o700 and trusted the gateway copy to carry that mode. `docker cp` does; the Apple `container cp` (AppleGatewayTransport) does not, landing the hook 0o644 → EACCES. The unhandled exception killed the handler thread, closing the socket with no HTTP response — which the client sees as the opaque empty reply. - provision_git_gate now `chmod +x`es the access-hook on the gateway side after the copy, so it's executable under every transport (docker/apple/firecracker). - git-http handler wraps the access-hook subprocess.run: an OSError / SubprocessError (un-execable, timed out) now fails closed with a 503 instead of crashing the thread into an empty reply — a gate that can't run its hook should deny, visibly. - Updates the now-misleading "docker cp preserves source mode" comment in git_gate.prepare(). Regression tests: provisioning applies +x to the access-hook; the handler returns 503 (not an empty reply) when the hook can't be exec'd. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>