6.2 KiB
PRD 0029: Codex host credentials through egress
- Status: Draft
- Author: didericis-codex
- Created: 2026-05-29
- Issue: #109
Summary
Allow Codex bottles to use a host-authorized ChatGPT/device-login
access token by forwarding it only into the egress sidecar, gated by an
explicit agent_provider.forward_host_credentials manifest flag.
Problem
Codex bottles can reach chatgpt.com after the host is added to egress,
but requests to chatgpt.com/backend-api/codex/... still fail with
HTTP 403. The egress proxy strips agent-originated Authorization
headers and only re-injects auth for routes that declare an egress-owned
token. A bare chatgpt.com route therefore forwards Codex requests
without the ChatGPT bearer token.
Copying ~/.codex/auth.json into the agent would solve auth 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
chatgpt.com/backend-api/codex/...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,tokens.access_token,tokens.refresh_token, orauth.json. - Egress route files remain non-secret: they contain only host/path/auth slot metadata, never token values.
- Missing, non-ChatGPT, 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.jsoninto the agent. - Allowing arbitrary host credential forwarding. This PRD covers Codex ChatGPT/device-login credentials only.
- Hot-applying a new authenticated
chatgpt.comroute 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. - Read host Codex auth from
$CODEX_HOME/auth.jsonwhenCODEX_HOMEis set, otherwise from~/.codex/auth.json. - Extract only
tokens.access_token. - Validate that
auth_modeischatgptand the access token is present, JWT-shaped, and not expired. - Add or upgrade a
chatgpt.comegress route to inject that access token via anEGRESS_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
auth_mode == "chatgpt". - 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 an authenticated chatgpt.com route. If the bottle
already declares chatgpt.com 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 chatgpt.com route,
fail rather than guessing whether to override operator-provided auth.
The rendered route should look like any other egress-owned auth route:
routes:
- 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"]
A["Codex agent"] -->|HTTPS via proxy, auth stripped| E["egress"]
E -->|Bearer injected from env| C["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
chatgpt.comroute when the flag is enabled, and add tests for bare route upgrade, missing-route insertion, and authenticated-route conflict. - 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.