Files
codex/docs/config.md
Josh McKinney 76d8d174b1 login: add custom CA support for login flows (#14178)
## Stacked PRs

This work is split across three stacked PRs:

- #14178: add custom CA support for browser and device-code login flows,
docs, and hermetic subprocess tests
- #14239: broaden the shared custom CA path from login to other outbound
`reqwest` clients across Codex
- #14240: extend that shared custom CA handling to secure websocket TLS
so websocket connections honor the same CA env vars

Review order: #14178, then #14239, then #14240.

Supersedes #6864.

Thanks to @3axap4eHko for the original implementation and investigation
here. Although this version rearranges the code and history
significantly, the majority of the credit for this work belongs to them.

## Problem

Login flows need to work in enterprise environments where outbound TLS
is intercepted by an internal proxy or gateway. In those setups, system
root certificates alone are often insufficient to validate the OAuth and
device-code endpoints used during login. The change adds a
login-specific custom CA loading path, but the important contracts
around env precedence, PEM compatibility, test boundaries, and
probe-only workarounds need to be explicit so reviewers can understand
what behavior is intentional.

For users and operators, the behavior is simple: if login needs to trust
a custom root CA, set `CODEX_CA_CERTIFICATE` to a PEM file containing
one or more certificates. If that variable is unset, login falls back to
`SSL_CERT_FILE`. If neither is set, login uses system roots. Invalid or
empty PEM files now fail with an error that points back to those
environment variables and explains how to recover.

## What This Delivers

Users can now make Codex login work behind enterprise TLS interception
by pointing `CODEX_CA_CERTIFICATE` at a PEM bundle containing the
relevant root certificates. If that variable is unset, login falls back
to `SSL_CERT_FILE`, then to system roots.

This PR applies that behavior to both browser-based and device-code
login flows. It also makes login tolerant of the PEM shapes operators
actually have in hand: multi-certificate bundles, OpenSSL `TRUSTED
CERTIFICATE` labels, and bundles that include well-formed CRLs.

## Mental model

`codex-login` is the place where the login flows construct ad hoc
outbound HTTP clients. That makes it the right boundary for a narrow CA
policy: look for `CODEX_CA_CERTIFICATE`, fall back to `SSL_CERT_FILE`,
load every parseable certificate block in that bundle into a
`reqwest::Client`, and fail early with a clear user-facing error if the
bundle is unreadable or malformed.

The implementation is intentionally pragmatic about PEM input shape. It
accepts ordinary certificate bundles, multi-certificate bundles, OpenSSL
`TRUSTED CERTIFICATE` labels, and bundles that also contain CRLs. It
does not validate a certificate chain or prove a handshake; it only
constructs the root store used by login.

## Non-goals

This change does not introduce a general-purpose transport abstraction
for the rest of the product. It does not validate whether the provided
bundle forms a real chain, and it does not add handshake-level
integration tests against a live TLS server. It also does not change
login state management or OAuth semantics beyond ensuring the existing
flows share the same CA-loading rules.

## Tradeoffs

The main tradeoff is keeping this logic scoped to login-specific client
construction rather than lifting it into a broader shared HTTP layer.
That keeps the review surface smaller, but it also means future
login-adjacent code must continue to use `build_login_http_client()` or
it can silently bypass enterprise CA overrides.

The `TRUSTED CERTIFICATE` handling is also intentionally a local
compatibility shim. The rustls ecosystem does not currently accept that
PEM label upstream, so the code normalizes it locally and trims the
OpenSSL `X509_AUX` trailer bytes down to the certificate DER that
`reqwest` can consume.

## Architecture

`custom_ca.rs` is now the single place that owns login CA behavior. It
selects the CA file from the environment, reads it, normalizes PEM label
shape where needed, iterates mixed PEM sections with `rustls-pki-types`,
ignores CRLs, trims OpenSSL trust metadata when necessary, and returns
either a configured `reqwest::Client` or a typed error.

The browser login server and the device-code flow both call
`build_login_http_client()`, so they share the same trust-store policy.
Environment-sensitive tests run through the `login_ca_probe` helper
binary because those tests must control process-wide env vars and cannot
reliably build a real reqwest client in-process on macOS seatbelt runs.

## Observability

The custom CA path logs which environment variable selected the bundle,
which file path was loaded, how many certificates were accepted, when
`TRUSTED CERTIFICATE` labels were normalized, when CRLs were ignored,
and where client construction failed. Returned errors remain user-facing
and include the relevant path, env var, and remediation hint.

This gives enough signal for three audiences:
- users can see why login failed and which env/file caused it
- sysadmins can confirm which override actually won
- developers can tell whether the failure happened during file read, PEM
parsing, certificate registration, or final reqwest client construction

## Tests

Pure unit tests stay limited to env precedence and empty-value handling.
Real client construction lives in subprocess tests so the suite remains
hermetic with respect to process env and macOS sandbox behavior.

The subprocess tests verify:
- `CODEX_CA_CERTIFICATE` precedence over `SSL_CERT_FILE`
- fallback to `SSL_CERT_FILE`
- single-certificate and multi-certificate bundles
- malformed and empty-bundle errors
- OpenSSL `TRUSTED CERTIFICATE` handling
- CRL tolerance for well-formed CRL sections

The named PEM fixtures under `login/tests/fixtures/` are shared by the
tests so their purpose stays reviewable.

---------

Co-authored-by: Ivan Zakharchanka <3axap4eHko@gmail.com>
Co-authored-by: Codex <noreply@openai.com>
2026-03-13 00:14:54 +00:00

3.4 KiB

Configuration

For basic configuration instructions, see this documentation.

For advanced configuration instructions, see this documentation.

For a full configuration reference, see this documentation.

Connecting to MCP servers

Codex can connect to MCP servers configured in ~/.codex/config.toml. See the configuration reference for the latest MCP server options:

Apps (Connectors)

Use $ in the composer to insert a ChatGPT connector; the popover lists accessible apps. The /apps command lists available and installed apps. Connected apps appear first and are labeled as connected; others are marked as can be installed.

Notify

Codex can run a notification hook when the agent finishes a turn. See the configuration reference for the latest notification settings:

When Codex knows which client started the turn, the legacy notify JSON payload also includes a top-level client field. The TUI reports codex-tui, and the app server reports the clientInfo.name value from initialize.

JSON Schema

The generated JSON Schema for config.toml lives at codex-rs/core/config.schema.json.

SQLite State DB

Codex stores the SQLite-backed state DB under sqlite_home (config key) or the CODEX_SQLITE_HOME environment variable. When unset, WorkspaceWrite sandbox sessions default to a temp directory; other modes default to CODEX_HOME.

Login Custom CA Certificates

Browser login and device-code login can trust a custom root CA bundle when enterprise proxies or gateways intercept TLS.

Set CODEX_CA_CERTIFICATE to the path of a PEM file containing one or more certificate blocks to use a login-specific CA bundle. If CODEX_CA_CERTIFICATE is unset, login falls back to SSL_CERT_FILE. If neither variable is set, login uses the system root certificates.

CODEX_CA_CERTIFICATE takes precedence over SSL_CERT_FILE. Empty values are treated as unset.

The PEM file may contain multiple certificates. Codex also tolerates OpenSSL TRUSTED CERTIFICATE labels and ignores well-formed X509 CRL sections in the same bundle. If the file is empty, unreadable, or malformed, login fails with a user-facing error that points back to these environment variables.

Notices

Codex stores "do not show again" flags for some UI prompts under the [notice] table.

Plan mode defaults

plan_mode_reasoning_effort lets you set a Plan-mode-specific default reasoning effort override. When unset, Plan mode uses the built-in Plan preset default (currently medium). When explicitly set (including none), it overrides the Plan preset. The string value none means "no reasoning" (an explicit Plan override), not "inherit the global default". There is currently no separate config value for "follow the global default in Plan mode".

Realtime start instructions

experimental_realtime_start_instructions lets you replace the built-in developer message Codex inserts when realtime becomes active. It only affects the realtime start message in prompt history and does not change websocket backend prompt settings or the realtime end/inactive message.

Ctrl+C/Ctrl+D quitting uses a ~1 second double-press hint (ctrl + c again to quit).