feat(centaur): a values knob for uncredentialed egress; allow PyPI so tools can run#559
Merged
Merged
Conversation
… 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)
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.
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 runinvokesuvx --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: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 setsconsole.enabledfor 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_rulesonly 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, andallowlistisn't secret-bearing — so an allowlist transform flows straight through.CODEX_ACCESS_TOKEN_PER_USER_FRAGMENTis the precedent, already load-bearing for the only egress prod has: allowlistchatgpt.com, inject nothing.This change
ironProxy.extraAllowlistDomains→KUBERNETES_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.orgininfra/values.local.yaml. A knob rather than editing the bakedinfra.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.comand cut model egress on prod. Verified three ways:allowlist_domains += domains_from_rules(config)(Proxy.merge_proxy_policy)merge_fragmentis a plaintransforms.extendVerified
cargo test -p centaur-iron-proxy— 11 pass (3 new),clippy+fmtcleanhelm templatewith Atrium's values →value: "pypi.org,files.pythonhosted.org"helm templatewith chart defaults →value: ""Not yet proven end-to-end in a sandbox — that needs a deploy. Plan: deploy, then read
/api/v1/proxy/syncand 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_ALLOWLISTstill gates the catalog to exactly one tool. Deliberate call, per #557.