Repository navigation
fix(container-gateway): find the current docker context without a docker CLI - #1966
Conversation
…ker CLI On macOS the gateway took its docker backend from `docker context inspect`. The hook that starts it pins PATH to /usr/bin:/bin:/usr/sbin:/sbin, where neither a Homebrew nor a Docker Desktop CLI lives, so the call failed silently and discovery fell back to ~/.docker/run/docker.sock. With Colima (or any other non-default context) and a socket file left behind by an earlier Docker Desktop, the gateway served that dead socket and every request failed with "backend ... is unreachable ([Errno 61] Connection refused)". Read the current context from the CLI's own files instead: currentContext from ~/.docker/config.json and the endpoint from the context store, keyed by the SHA-256 of the context name. Only that one key is taken from config.json, never auths, credsStore or credHelpers; the README and spec now say so rather than that the file is never read. Not running `docker` also keeps DOCKER_HOST out of it, which in a session names the gateway's own socket. A candidate socket is used only when something accepts a connection on it, so a stale socket file no longer wins over a later candidate. Generated-by: Claude Code (Opus 5.5)
…rder - Read the docker CLI's config.json and context store from $DOCKER_CONFIG when it is set, as the CLI does, else ~/.docker; with a test. - Say in the troubleshooting notes that discovery runs once at gateway start, so a runtime started after the session is not picked up until the gateway restarts, now that a socket must answer to be used. - Restamp the isolated-setup fingerprint for the gateway change. Generated-by: Claude Opus 5
potiuk
left a comment
There was a problem hiding this comment.
Thanks, @paulk-asert — reading the current context from the CLI's own files instead of a docker binary the hook's PATH never had is the right fix, it keeps away from auths / credsStore / credHelpers, and the tests cover the context store, the silent socket and every fallback well.
Two small points I fixed on your branch rather than ask for another round (755335d):
docs/setup/sandbox-troubleshooting.md: discovery runs once at gateway start, and now that a socket has to answer, a Docker Desktop or Colima started after the session is not picked up until the gateway restarts (before, the leftover socket file was served and worked once the daemon came up). The Notes now say to start the runtime first and what the log shows otherwise.tools/container-gateway/src/container_gateway/backends.py: the context files are read from$DOCKER_CONFIGwhen it is set, as the docker CLI does, else~/.docker; new testtest_docker_current_context_read_from_docker_config. The isolated-setup fingerprint is restamped for it.
Heads-up: #1967 edits the same Notes list, so whichever merges second may need a quick rebase.
This review was drafted by an AI-assisted tool and
confirmed by an Apache Magpie maintainer. The maintainer
approving this PR has read the findings and signed off. If
something feels off, please reply on the PR and a maintainer
will follow up.More on how Apache Magpie handles maintainer review:
Contributing guide.
Summary
docker context inspect, but the hook that starts it pinsPATH=/usr/bin:/bin:/usr/sbin:/sbin, where neither a Homebrew nor a Docker Desktop CLI lives. Discovery silently fell back to~/.docker/run/docker.sock; with Colima active and a socket file left behind by an earlier Docker Desktop, the gateway served that dead socket and every call failed withbackend /Users/<you>/.docker/run/docker.sock is unreachable ([Errno 61] Connection refused).currentContextin~/.docker/config.json, the endpoint from the context store keyed by the SHA-256 of the context name), so nodockerbinary is needed andDOCKER_HOST(which names the gateway's own socket in a session) cannot leak in.socket_answers), so a stale socket file no longer wins.Type of change
tools/*/withpyproject.toml)docs/,README.md,CONTRIBUTING.md)Test plan
prek runon the changed files passes, exceptlychee, skipped locally (needs a Rust toolchain download; CI runs it).isolated-setup-fingerprintregeneratedisolated_fingerprint.py, included.uv run pytest/ruff check/ruff format --check/mypy(via the workspace hooks) pass fortools/container-gatewaytest_backends.py: a non-default context (Colima) is read from the context store with no docker CLI called; a context whose socket does not answer falls back;default, missing, metadata-less andtcp://contexts and a non-JSONconfig.jsonfall back to the Desktop socket;socket_answersrejects a regular file and a missing path, and (where Unix bind is allowed; skipped in the agent sandbox as the existing bind tests are) accepts a live listener but not the socket file it leaves behindsocket_answerschecked by hand against a running gateway socket (True) and a missing path (False)docker: with the old discovery the gateway loggedbackend docker at ~/.docker/run/docker.sockand every call was refused; pointing that path at Colima's socket madedocker versionreport the Colima server through the gatewayRFC-AI-0004 compliance
~/.docker; the sandbox's own~/.dockerread denial is unchanged.Linked issues
None.
Notes for reviewers (optional)
~/.docker/config.json. The README and spec said the gateway never reads this file. It now reads it, but takes onlycurrentContextand neverauths,credsStoreorcredHelpers; both docs are restated accordingly. The old code already depended on that file indirectly, sincedocker context inspectreads it. The alternative considered was runningdockerfrom fixed trusted locations (/opt/homebrew/bin,/usr/local/bin), which would need the hook's writable-root checks re-implemented in Python, and would still have to scrubDOCKER_HOST/DOCKER_CONTEXTfrom its environment.DOCKER_CONTEXTandDOCKER_CONFIGare deliberately not honoured: likeDOCKER_HOST, they can come from the session's settings environment.socket_answersuses a 1 s connect timeout per candidate; discovery runs once at gateway start.🤖 Generated with Claude Code