Skip to content

feat(centaur): a values knob for uncredentialed egress; allow PyPI so tools can run#559

Merged
gbasin merged 1 commit into
masterfrom
feat/tool-egress-allowlist-knob
Jul 16, 2026
Merged

feat(centaur): a values knob for uncredentialed egress; allow PyPI so tools can run#559
gbasin merged 1 commit into
masterfrom
feat/tool-egress-allowlist-knob

Conversation

@gbasin

@gbasin gbasin commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Closes the gap found while wiring atrium-preview (#547, #548). Full investigation in #557.

The problem

No Centaur tool can execute in an Atrium sandbox. centaur-tools run invokes uvx --from, which resolves a tool's deps and the hatchling build backend from a package index at run time. Under iron-control, sandbox egress is deny-by-default and PyPI has no credential:

Failed to fetch: `https://pypi.org/simple/hatchling/`
tunnel error: unsuccessful

Not per-tool — a zero-dependency probe tool fails identically in a live prod sandbox. This is what TOOL_ALLOWLIST: "none" has been encoding: the 78 upstream tools are hidden because they're dead, not unwanted.

Two correct designs disagree. Upstream's iron-proxy is a credential injector whose default is domains: ["*"]; PyPI-at-run-time is its intended design. Atrium sets console.enabled for per-user BYO credentials, which flips the proxy to managed mode and silently converts egress to deny-by-default. Nothing broke — we opted into a posture the tool mechanism never assumed.

The seam already exists

centaur-console computes a sandbox's allowlist as union(baseline domains, hosts from granted credentials' rules). domains_from_rules only reads hosts off granted secrets, so an uncredentialed host can only ever arrive via the baseline. api-rs builds that baseline from the infra fragment's non-secret transforms, and allowlist isn't secret-bearing — so an allowlist transform flows straight through.

CODEX_ACCESS_TOKEN_PER_USER_FRAGMENT is the precedent, already load-bearing for the only egress prod has: allowlist chatgpt.com, inject nothing.

This change

ironProxy.extraAllowlistDomainsKUBERNETES_IRON_PROXY_EXTRA_ALLOWLIST_DOMAINS → an allowlist-only fragment merged into the infra fragment (i.e. the baseline).

Empty by default — every other deployment's allowlist stays byte-identical. Atrium enables pypi.org + files.pythonhosted.org in infra/values.local.yaml. A knob rather than editing the baked infra.yaml, so it survives subtree pulls as a smaller diff.

Why this is safe to deploy

Safety rests entirely on the merge being a union, not a replace — a replace would strip chatgpt.com and cut model egress on prod. Verified three ways:

  1. the console's allowlist_domains += domains_from_rules(config) (Proxy.merge_proxy_policy)
  2. merge_fragment is a plain transforms.extend
  3. a test asserting both allowlists survive — so a future refactor to replace-semantics fails in CI rather than in production

Verified

  • cargo test -p centaur-iron-proxy11 pass (3 new), clippy + fmt clean
  • helm template with Atrium's values → value: "pypi.org,files.pythonhosted.org"
  • helm template with chart defaults → value: ""

Not yet proven end-to-end in a sandbox — that needs a deploy. Plan: deploy, then read /api/v1/proxy/sync and confirm the allowlist is [chatgpt.com, files.pythonhosted.org, pypi.org] (chatgpt.com surviving is the thing to check) before running a real turn.

The tradeoff, stated plainly

Sandboxes can now reach PyPI. That is a real supply-chain surface — a typosquatted package would execute in the sandbox. It widens an existing surface rather than opening a new class (the sandbox already runs arbitrary model-authored code), and TOOL_ALLOWLIST still gates the catalog to exactly one tool. Deliberate call, per #557.

… tools can run

No Centaur tool can execute in an Atrium sandbox. `centaur-tools run` invokes
`uvx --from`, which resolves a tool's deps — and the hatchling build backend —
from a package index at run time. Under iron-control, sandbox egress is
deny-by-default, and PyPI has no credential, so the fetch is refused:

    Failed to fetch: `https://pypi.org/simple/hatchling/`
    tunnel error: unsuccessful

This is not per-tool: a zero-dependency probe tool fails the same way, in a live
prod sandbox. It is why values.local.yaml carries TOOL_ALLOWLIST: "none" — the
78 upstream tools are hidden because they are dead, not unwanted.

Two correct designs disagree. Upstream's iron-proxy is a credential injector
whose default is domains:["*"], and PyPI-at-run-time IS its intended design.
Atrium sets console.enabled for per-user BYO credentials, which flips the proxy
to managed mode and silently converts egress to deny-by-default. Nothing broke;
we opted into a posture the tool mechanism never assumed.

The seam already exists. centaur-console computes a sandbox's allowlist as
union(baseline domains, hosts from granted credentials' rules) — and
domains_from_rules only reads hosts off granted secrets, so an uncredentialed
host can ONLY arrive via the baseline. api-rs builds that baseline from the
infra fragment's non-secret transforms, and `allowlist` is not secret-bearing,
so an allowlist transform flows straight through. CODEX_ACCESS_TOKEN_PER_USER_FRAGMENT
is the precedent: allowlist chatgpt.com, inject nothing.

So: ironProxy.extraAllowlistDomains -> KUBERNETES_IRON_PROXY_EXTRA_ALLOWLIST_DOMAINS
-> an allowlist-only fragment merged into the infra fragment. Empty by default,
so every other deployment's allowlist is byte-identical to today. Atrium turns on
pypi.org + files.pythonhosted.org.

Safety rests on the merge being a UNION, not a replace — a replace would strip
chatgpt.com and cut model egress on prod. Verified three ways: the console's
`allowlist_domains += domains_from_rules(config)`; merge_fragment being a plain
extend; and a test asserting both allowlists survive so a future refactor to
replace-semantics fails loudly rather than in production.

Verified: cargo test (11 pass), clippy, fmt; helm renders
"pypi.org,files.pythonhosted.org" under Atrium's values and "" under chart
defaults. Not yet proven end-to-end in a sandbox — that needs a deploy.

Background: docs/archive/notes/2026-07-16-centaur-tool-egress-and-deps.md (#557)
@gbasin
gbasin added this pull request to the merge queue Jul 16, 2026
Merged via the queue into master with commit a89dad2 Jul 16, 2026
16 checks passed
@gbasin
gbasin deleted the feat/tool-egress-allowlist-knob branch July 16, 2026 22:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant