fix(container): pin host.docker.internal to OneCLI's bridge IP in rootless Docker#2168
Open
kpscheffel wants to merge 1 commit into
Open
fix(container): pin host.docker.internal to OneCLI's bridge IP in rootless Docker#2168kpscheffel wants to merge 1 commit into
kpscheffel wants to merge 1 commit into
Conversation
…ass rootless Docker port-forward flake In rootless Docker, OneCLI's gateway port (10255) is bound to 127.0.0.1 inside rootlesskit. The agent container reaches it via HTTPS_PROXY=http://...@host.docker.internal:10255, which on Linux resolves to the bridge gateway (host-gateway feature). Whether the gateway forwards back into rootlesskit's loopback is brittle — it silently breaks across host restarts, leaving the next agent container unable to reach the proxy. The SDK call dies with ConnectionRefused, the runner used to ack the message as completed (the v2 lie-by-omission just patched), and the user gets a silent task day. OneCLI is on the default `bridge` network alongside every spawned agent container, and container-to-container traffic on a shared bridge always works. So pin host.docker.internal to OneCLI's bridge IP at spawn time — the proxy lookup becomes a direct bridge hop, no rootlesskit involved. Falls back to host-gateway when OneCLI isn't on the bridge (rootful Docker, OneCLI not installed, etc.) so the default behaviour is unchanged for those setups. Adds 7 unit tests covering the platform branches, the IP-pinning branch, and the four `getOnecliBridgeIp` failure modes (valid IP, empty output, "<no value>" go-template literal, docker inspect throws). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Type of Change
Description
What. When OneCLI is on the default
bridgeDocker network, pin the agent container'shost.docker.internalmapping to OneCLI's bridge IP at spawn time instead of relying onhost-gateway. Falls back tohost-gatewaywhen OneCLI isn't on the bridge, so the default behaviour for rootful Docker / non-OneCLI installs is unchanged.Why. In rootless Docker, OneCLI's gateway port (
10255) is bound to127.0.0.1inside rootlesskit. The agent container reaches it viaHTTPS_PROXY=http://...@host.docker.internal:10255, which on Linux resolves to the bridge gateway (host-gateway). Whether the gateway forwards back into rootlesskit's loopback is brittle — it silently breaks across host restarts, leaving the next agent container unable to reach the proxy. The SDK call dies withConnectionRefusedand (pre-#2167) the runner used to ack the message ascompleted, producing a silent task day. The systemd-unit hintExecStartPre=-/usr/bin/docker network connect bridge onecliis a no-op when OneCLI is already on the bridge — it doesn't fix the actual routing flake.OneCLI is on
bridgealongside every spawned agent container, and container-to-container traffic on a shared bridge always works. Pinninghost.docker.internalto OneCLI's bridge IP turns the proxy lookup into a direct bridge hop with no rootlesskit involvement — same hostname, different IP, no other agent code changes.How it works.
src/container-runtime.ts— addsgetOnecliBridgeIp()(a 5 s cap'ddocker inspect onecli --format '{{ index .NetworkSettings.Networks "bridge" "IPAddress" }}', returnsnullon any failure / non-IPv4 output / Go template's<no value>literal).hostGatewayArgs(opts?)accepts anonecliBridgeIpand returns--add-host=host.docker.internal:<ip>when it's a valid IP, otherwise the existinghost-gatewayvalue.src/container-runner.ts— callsgetOnecliBridgeIp()once per spawn, passes the result intohostGatewayArgs, logs a single line when it pins.How it was tested.
src/container-runtime.test.tscovering: non-Linux returns empty (host.docker.internal is built-in on macOS/Windows); Linux + no IP falls back tohost-gateway; Linux + IP pins to that IP;getOnecliBridgeIphappy path; OneCLI not on bridge (empty output); Go template<no value>literal;docker inspectthrows.host.docker.internal:10255was `Failed to connect (Couldn't connect to server)` while curl to OneCLI's bridge IP `172.17.0.2:10255` succeeded — confirming the rootless-loopback gap. After deploying, the new log line `Pinning host.docker.internal to OneCLI bridge IP onecliBridgeIp="172.17.0.2"` fires at every spawn, the agent container'shost.docker.internalresolves to172.17.0.2, and an end-to-end SDK call through the proxy succeeds.