fix(macos): supply proxy env only at exec, so Codex keeps its identity token #442

Merged
didericis merged 1 commits from fix/duplicate-proxy-env-drops-identity-token into main 2026-07-20 22:54:54 -04:00
Owner

Fixes #441.

Problem

container exec --env appends to the run-time environment on Apple Container rather than replacing it. The launch path baked a token-less *_PROXY at container run and relied on the exec-time token-bearing value superseding it, so the agent's environ ends up with two HTTPS_PROXY entries, token-less first.

Which one a runtime reads is luck: Node takes the last (Claude worked), Rust's std::env::var takes the first (Codex proxied with no identity token). /resolve requires a matching (source_ip, identity_token) pair and fail-closes, so every request from a Codex bottle was denied while its registry row was perfectly correct.

Confirmed in /proc/<pid>/environ on live containers: Claude and Codex agents both carry the duplicate, in the same order.

Change

Drop the run-time proxy vars; the exec-time env is now the single source of *_PROXY.

A token-less proxy URL has no legitimate consumer — the init process is sleep and everything that egresses arrives via exec. Its one benefit was a tidy 403 for unattributed callers, which is not worth silently dropping attribution for; and a process egressing before the exec-time env still fails closed, since the agent network is host-only and the gateway is the only route off it.

Also corrects the two comments asserting the false "exec --env wins" invariant (launch.py:269, bottle.py:71-73) — that assumption is why this survived review.

Tests

Replaces test_run_time_proxy_carries_no_identity_token (which encoded the buggy behaviour) with test_run_time_env_sets_no_proxy_vars_at_all, plus a narrower check that the run-time env carries no token. 1880 unit tests pass, pyright clean, pylint 9.82.

Note

Independent of #440 — branched from main, no overlapping files. Landing either one recreates the shared infra container on the next launch.

Fixes #441. ## Problem `container exec --env` **appends** to the run-time environment on Apple Container rather than replacing it. The launch path baked a token-less `*_PROXY` at `container run` and relied on the exec-time token-bearing value superseding it, so the agent's `environ` ends up with two `HTTPS_PROXY` entries, token-less first. Which one a runtime reads is luck: Node takes the last (Claude worked), Rust's `std::env::var` takes the first (Codex proxied with no identity token). `/resolve` requires a matching `(source_ip, identity_token)` pair and fail-closes, so **every** request from a Codex bottle was denied while its registry row was perfectly correct. Confirmed in `/proc/<pid>/environ` on live containers: Claude and Codex agents both carry the duplicate, in the same order. ## Change Drop the run-time proxy vars; the exec-time env is now the single source of `*_PROXY`. A token-less proxy URL has no legitimate consumer — the init process is `sleep` and everything that egresses arrives via exec. Its one benefit was a tidy 403 for unattributed callers, which is not worth silently dropping attribution for; and a process egressing before the exec-time env still fails closed, since the agent network is host-only and the gateway is the only route off it. Also corrects the two comments asserting the false "exec `--env` wins" invariant (`launch.py:269`, `bottle.py:71-73`) — that assumption is why this survived review. ## Tests Replaces `test_run_time_proxy_carries_no_identity_token` (which encoded the buggy behaviour) with `test_run_time_env_sets_no_proxy_vars_at_all`, plus a narrower check that the run-time env carries no token. 1880 unit tests pass, pyright clean, pylint 9.82. ## Note Independent of #440 — branched from `main`, no overlapping files. Landing either one recreates the shared infra container on the next launch.
didericis added 1 commit 2026-07-20 22:41:08 -04:00
fix(macos): supply proxy env only at exec, so Codex keeps its identity token
test / stage-firecracker-inputs (pull_request) Successful in 1s
test / integration-docker (pull_request) Successful in 7s
tracker-policy-pr / check-pr (pull_request) Successful in 5s
lint / lint (push) Successful in 41s
test / unit (pull_request) Successful in 30s
test / build-infra (pull_request) Successful in 3m36s
test / integration-firecracker (pull_request) Successful in 1m48s
test / coverage (pull_request) Successful in 2m2s
test / publish-infra (pull_request) Has been skipped
c1b110c92d
`container exec --env` does not override the run-time environment on Apple
Container — it appends. The launch path baked a token-less `*_PROXY` into
`container run` and relied on the exec-time, token-bearing value superseding
it, so the agent's `environ` ended up with two `HTTPS_PROXY` entries,
token-less first.

Which entry a runtime reads is then luck. Node reads the last, so Claude
bottles picked up the token and worked. Rust's `std::env::var` reads the
first, so Codex proxied with no identity token at all; `/resolve` requires a
matching (source_ip, identity_token) pair and fail-closes, so every request
from a Codex bottle was denied — its model calls, its `wss://` reconnects,
and its MCP servers alike. The registry row was correct the whole time,
which is what made this read as a registration bug.

Drop the run-time proxy vars entirely. A token-less proxy URL has no
legitimate consumer: the init process is `sleep` and everything that
egresses arrives by exec. Its only benefit was a tidy 403 for unattributed
callers, which is not worth silently dropping attribution for — and a
process that egresses before the exec-time env still fails closed, since the
agent network is host-only and the gateway is the only route off it.

Also corrects the two comments that asserted the false "exec --env wins"
invariant, so the next reader doesn't rebuild the same assumption.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
didericis merged commit ad100b8a84 into main 2026-07-20 22:54:54 -04:00
didericis deleted branch fix/duplicate-proxy-env-drops-identity-token 2026-07-20 22:54:55 -04:00
Sign in to join this conversation.