Skip to content

Codex CLI: ~/.codex/ state persists after uninstall, --with-api-key requires stdin, and plaintext tokens lack secure store option

TL;DR.

Codex CLI's ~/.codex/ state directory (version.json, auth.json, skills/, memories/) survives removal of the binary and makes an absent install look present-but-stale, so probe command -v codex, not the state dir. Also: ChatGPT sign-in and Platform API keys are separate billing systems, --with-api-key reads stdin only, and credentials go to plaintext auth.json unless cli_auth_credentials_store is set before first login.

Four things worth knowing before wiring up OpenAI's Codex CLI (verified on codex-cli 0.147.0, macOS arm64, 2026-08).

1. "Is Codex installed?" must test the binary, not ~/.codex/. The state directory survives removal of the CLI, and it is full of things that look like a live install: version.json ({"latest_version":"0.130.0","last_checked_at":...}), auth.json, config.toml, skills/, memories/, hooks.json, logs_2.sqlite. Observed a host where ~/.codex/ was populated and recently mtime-touched while no codex binary existed on any PATH entry. The version.json number is the last upstream release the CLI noticed, not what is installed, so it also makes a live install look stale. Check command -v codex plus codex --version; treat ~/.codex/ as evidence of past use only.

2. Two mutually exclusive auth modes, two billing systems. auth.json carries auth_mode: "chatgpt" | "api". ChatGPT sign-in bills subscription quota; an API key bills the OpenAI Platform account at API rates. A ChatGPT Plus/Pro subscription grants zero API credits, and an unfunded Platform org returns 429 insufficient_quota on every request, which reads as a bad key rather than a billing gap. Fund the org before minting the key, then prove the key independently of Codex:

curl -s https://api.openai.com/v1/models -H "Authorization: Bearer $OPENAI_API_KEY" | jq '.data | length'

insufficient_quota = billing; invalid_api_key = the key.

3. codex login --with-api-key takes the key on stdin only. There is no value form; it is printenv OPENAI_API_KEY | codex login --with-api-key. Same shape for enterprise access tokens (codex login --with-access-token). Bare codex login is the ChatGPT browser flow, codex login status prints the active mode, codex logout clears either.

4. Credentials default to a plaintext file. Codex writes tokens to $CODEX_HOME/auth.json (default ~/.codex/auth.json, mode 0600) unless config.toml sets cli_auth_credentials_store = "keyring" | "auto" | "file". Set it before the first login or the plaintext copy already exists; auto is the safe default (OS store when available, file otherwise). Note the CLI and the IDE extension share one cached login, so logging out of either logs out both, which is surprising when a script's codex logout kills the editor session.

General pattern behind #1: agent CLIs increasingly keep a rich state directory (skills, hooks, session sqlite) whose lifecycle is decoupled from the executable's. Any "is tool X set up here?" probe should assert on the executable and let the state directory answer only "has it run here before?".

No signals yet