Operators can now declare:
agent_provider:
template: claude
auth_token: BOT_BOTTLE_CLAUDE_OAUTH_TOKEN
and the provisioner injects a provider-owned api.anthropic.com egress
route (Bearer, tls_passthrough) rather than requiring a manually
declared route with the former claude_code_oauth role.
Changes:
- Add auth_token field to AgentProvider; validate claude-only.
- Remove claude_code_oauth from EGRESS_ROLES / PROVIDER_EGRESS_ROLES.
Manifests that declare the role now fail at parse time with "unknown
role" — the provisioner owns the route.
- agent_provision_plan: replace manifest_egress_routes/has_provider_auth
with auth_token; Claude branch injects the api.anthropic.com route,
placeholder env, and nonessential-traffic flags when auth_token is set.
- Add hidden_env_names: frozenset[str] to AgentProvisionPlan; Claude
branch populates it with CLAUDE_CODE_OAUTH_TOKEN.
- Remove auth_role from AgentProviderRuntime and placeholder_env_for().
- print_util.visible_agent_env_names: accept hidden_env_names from the
plan instead of dispatching on agent_provider_template.
- Both backends: drop manifest_egress_routes call, pass auth_token.
- PRD 0029 rescoped to cover both Codex and Claude provider auth.
Assisted-by: Claude Code
7.8 KiB
PRD 0029: Provider auth credentials through egress
- Status: Draft
- Author: didericis-codex
- Created: 2026-05-29
- Issue: #109
Summary
Allow provider bottles to inject host credentials into the egress
sidecar without exposing them to the agent. Codex uses
agent_provider.forward_host_credentials for ChatGPT/device-login
access tokens. Claude uses agent_provider.auth_token to name the host
env var holding its OAuth token, which egress injects on
api.anthropic.com requests.
Problem
Codex bottles can reach OpenAI hosts after they are added to egress, but
requests to Codex's ChatGPT-backed API endpoints still fail with HTTP
403 when the egress route is unauthenticated. The egress proxy strips
agent-originated Authorization headers and only re-injects auth for
routes that declare an egress-owned token. Bare api.openai.com or
chatgpt.com routes therefore forward Codex requests without the
ChatGPT bearer token.
Copying the host ~/.codex/auth.json into the agent would solve auth
mode detection but would also put access and refresh material inside the
agent sandbox. That cuts against bot-bottle's credential minimization
model: provider credentials should live in the sidecar boundary when
possible, not in the agent.
Goals / Success Criteria
- A Codex bottle with host ChatGPT auth can call Codex's
api.openai.comandchatgpt.comendpoints through egress. - Host credential forwarding happens only when the bottle declares
agent_provider.forward_host_credentials: true. - The agent container does not receive
OPENAI_API_KEY,CODEX_ACCESS_TOKEN, or realtokens.access_token/tokens.refresh_tokenvalues. - The agent container receives only a dummy Codex
auth.jsonthat preserves the host auth-mode shape, keeps the selected ChatGPT account id, and replaces credential values with placeholders. - Egress route files remain non-secret: they contain only host/path/auth slot metadata, never token values.
- Missing, API-key, malformed, or expired host Codex auth fails launch with a clear operator-facing message.
- Existing Claude OAuth placeholder behavior remains unchanged.
Non-goals
- Refreshing Codex tokens in the sidecar. The first cut reads the host's current access token at launch; operators can restart after host Codex refreshes auth.
- Copying host
~/.codex/auth.jsoncredentials into the agent. - Allowing arbitrary host credential forwarding beyond the two providers covered here (Codex ChatGPT/device-login and Claude OAuth).
- Hot-applying new authenticated Codex routes to an existing running sidecar. The current hot-apply path cannot safely populate new token env slots in an already-running container.
Scope
In scope
- Add
agent_provider.forward_host_credentialsto the bottle manifest schema, defaulting tofalse. - Support the flag for
agent_provider.template: codex. - Add
agent_provider.auth_tokento the bottle manifest schema. - Support the field for
agent_provider.template: claude: the named host env var is forwarded only into the egress sidecar as the Bearer token forapi.anthropic.com, and a placeholderCLAUDE_CODE_OAUTH_TOKENis set in the agent so the Claude Code CLI starts without a real credential. - Remove the
claude_code_oauthegress route role, which previously required operators to declare the OAuth route manually. The provisioner now injects it fromauth_token. - Read host Codex auth from
$CODEX_HOME/auth.jsonwhenCODEX_HOMEis set, otherwise from~/.codex/auth.json. - Extract only
tokens.access_tokenfor egress injection. - Generate a dummy agent-side
auth.jsonfrom the host auth file's mode and key shape, without copying real token values. - Validate that host auth is not API-key mode and the access token is present, JWT-shaped, and not expired.
- Add or upgrade
api.openai.comandchatgpt.comegress routes to inject that access token via a sharedEGRESS_TOKEN_Nsidecar env slot. - Pass the extracted token only into the sidecar compose/run environment, alongside other egress token values.
Out of scope
- Sidecar-owned refresh using
tokens.refresh_token. - Sharing full Codex auth state with the agent.
- Supporting host credential forwarding for non-Codex providers.
Design
Manifest
Extend agent_provider:
agent_provider:
template: codex
forward_host_credentials: true
The field defaults to false. If set on a non-Codex provider, manifest
validation should reject it until that provider has a concrete,
credential-minimizing implementation.
Host auth extraction
At prepare/launch time, when the flag is enabled for Codex:
- Resolve the host Codex home directory from
$CODEX_HOME, falling back to~/.codex. - Parse
auth.json. - Require user/device auth mode rather than API-key auth.
- Require a non-empty
tokens.access_token. - Parse the JWT payload enough to require an
expclaim in the future. - Return only the access token value to the launch path.
Errors should name the missing or invalid condition and point the
operator at codex login --device-auth, without printing token values.
Egress route
When forwarding host Codex credentials, the effective egress route table
should contain authenticated api.openai.com and chatgpt.com routes.
If the bottle already declares either host as a bare-pass route, upgrade
it in the effective route table rather than requiring a duplicate
manifest entry. If the bottle already declares an authenticated route for
either host, fail rather than guessing whether to override
operator-provided auth, unless that route already uses the synthetic
Codex host credential token reference.
The rendered route should look like any other egress-owned auth route:
routes:
- host: "api.openai.com"
auth_scheme: "Bearer"
token_env: "EGRESS_TOKEN_N"
- host: "chatgpt.com"
auth_scheme: "Bearer"
token_env: "EGRESS_TOKEN_N"
The access token value is supplied through the sidecar process
environment for that EGRESS_TOKEN_N slot. It must not be written to
routes.yaml, compose files, env files, logs, or user-facing output.
Data flow
flowchart LR
H["Host ~/.codex/auth.json"] --> L["bot-bottle launch"]
L -->|access token only| S["egress sidecar env"]
L -->|dummy auth.json only| A
A["Codex agent"] -->|HTTPS via proxy, auth stripped| E["egress"]
E -->|Bearer injected from env| C["api.openai.com / chatgpt.com"]
Implementation chunks
- PRD first. Land this document as the first commit on the feature branch.
- Manifest schema. Add
forward_host_credentials, validation, and unit tests. - Host Codex auth reader. Add a small stdlib-only helper for parsing and validating host Codex auth without printing values.
- Effective egress route. Add/upgrade the Codex API routes when the flag is enabled, and add tests for bare route upgrade, missing-route insertion, and authenticated-route conflict.
- Agent auth marker. Provision a dummy Codex
auth.jsoninto the agent home so Codex selects the host's user/device auth branch while real credentials stay in egress. - Launch wiring. Pass the host access token into the egress sidecar env for Docker and smolmachines without exposing it to the agent.
- Docs and tests. Update README examples and run the unit suite.
Open questions
- Should a later version support sidecar refresh using the host refresh token, or should restart-on-expiry remain the policy?
- Should telemetry hosts such as
ab.chatgpt.comstay blocked by default even when Codex ChatGPT auth is enabled?
References
- Gitea issue #109: Codex ChatGPT auth should inject host access token via egress.
- PRD 0017: Egress-proxy — universal MITM with path filtering + auth injection.
- PRD 0026: Agent provider templates.