Files
ccs/docs/codex-auth.md

7.5 KiB

Codex Auth Profile Isolation (ccsx auth)

Run two Codex accounts simultaneously — one per terminal — with full auth isolation.

Why

Codex stores its OAuth credentials in a single directory (~/.codex/). When you run two codex sessions in separate terminals, they both write to the same auth.json. A token refresh in one session overwrites the other's credentials.

ccsx auth solves this by giving each account its own profile directory under ~/.ccs/codex-instances/<name>/. Each profile holds its own auth.json and history.jsonl. Shared config.toml, agents/, and skills/ resources are linked via symlink so model/provider settings and relative agent role config files stay in sync.

Quick start (4 commands)

# Create and authenticate two profiles
ccsx auth create work       # creates ~/.ccs/codex-instances/work/ and prompts for login
ccsx auth create personal   # same for personal account

# Activate per terminal (ephemeral — only this shell)
# Terminal A:
eval "$(ccsx auth use work)"
codex

# Terminal B:
eval "$(ccsx auth use personal)"
codex

# Or launch a named profile directly through ccsx
ccsx work

Two-terminal example

# Terminal A — work account
eval "$(ccsx auth use work)"
codex                         # runs with CODEX_HOME=~/.ccs/codex-instances/work

# Terminal B — personal account (simultaneously)
eval "$(ccsx auth use personal)"
codex                         # runs with CODEX_HOME=~/.ccs/codex-instances/personal

# No token clobbering. Each session refreshes its own auth.json only.

Command reference

Command Description
ccsx auth create <name> Create profile dir + auto-login
ccsx <name> Launch a named Codex auth profile
ccsx auth login <name> (Re-)authenticate an existing profile
ccsx auth switch <name> Set the persistent default profile for future ccsx launches
ccsx auth use <name> Emit shell exports for this shell only (use with eval)
ccsx auth show [name] List all profiles or show details for one
ccsx auth remove <name> Delete profile dir + registry entry
ccsx auth import-default <name> Migrate legacy ~/.codex/auth.json into a new profile

Persistent vs ephemeral switching

Method Scope How
ccsx <name> One launch Resolves <name> from the Codex profile registry
ccsx auth switch <name> Future ccsx launches Writes to ~/.ccs/codex-profiles.yaml
eval "$(ccsx auth use <name>)" Current shell only Sets CODEX_HOME + CCS_CODEX_PROFILE in your shell

Native codex shells only see the persistent default when launched through the ccsx Codex runtime. For an already-open shell or a plain native codex binary, use auth use.

Do not use ccs persist codex for Claude Code or the Claude Code Extension. That path would persist Claude settings that send Claude traffic through the Codex translator. CCS blocks Codex CLIProxy profiles from Claude extension setup; use ccsxp or ccs codex --target codex for ChatGPT/Codex subscriptions. If old settings were already persisted, clear them with:

ccs persist default --yes

The command prints a config receipt after writing settings: cleared managed keys, written managed keys, whether and where any /api/provider/codex translator URL remains, and the native Codex targets to use next.

Shell syntax for use:

# bash / zsh
eval "$(ccsx auth use work)"

# fish
ccsx auth use work | source

# PowerShell
ccsx auth use work | Invoke-Expression

Migration from ~/.codex

If you already have a logged-in session in ~/.codex/auth.json, import it without disturbing the original:

# Auth only (default — recommended)
ccsx auth import-default legacy

# Auth + history + sessions (opt-in)
ccsx auth import-default legacy --with-history

# Make it the default
ccsx auth switch legacy

The source ~/.codex/ directory is never modified. If import-default is not run, codex continues to work exactly as before.

Torn-write safety

Codex writes auth.json with truncate+write (not atomic rename). Running import-default while a token refresh is in flight can produce a corrupt copy. The command detects a running codex process via pgrep and refuses unless you pass --force-while-running. The safest approach is to quit Codex before importing.

Dashboard

The CCS dashboard shows active profile metadata at the Auth Profiles tab on the Codex page:

  • Profile name and whether it is the current default
  • Decoded email address (from id_token — no signature verification; display only)
  • Plan tier (Plus, Pro, Free) when present in the token
  • Last-used timestamp

No OAuth tokens are ever returned by the API endpoint or shown in the UI.

Profile disk layout

~/.ccs/
├── codex-profiles.yaml          # Registry: version, default, profiles metadata
└── codex-instances/
    └── <name>/
        ├── auth.json            # OAuth credentials (Codex writes here)
        ├── history.jsonl        # Per-profile prompt history (optional)
        ├── sessions/            # Per-profile chat session dirs (optional)
        ├── config.toml -> ~/.codex/config.toml   (symlink — shared)
        ├── agents/ -> ~/.codex/agents/           (symlink — shared)
        └── skills/ -> ~/.codex/skills/           (symlink — shared)

~/.codex/
├── config.toml                  # Single shared model/provider config
├── agents/                      # Shared Codex agent role config files
└── skills/                      # Shared Codex skills

ccsx auth create <name> and ccsx <name> both repair these links idempotently. This keeps relative Codex config entries such as agents/foo.toml valid inside each isolated CODEX_HOME.

Caveats

On Windows, creating symlinks requires Developer Mode or elevated privileges. If symlink creation fails, CCS falls back to copying config.toml, agents/, and skills/. In this case, changes to ~/.codex/ resources are not automatically reflected in the profile; re-run ccsx auth create <name> --force to refresh the copy.

Native Codex project-local config warnings

ccsx preserves your current working directory. If you launch from your home directory, native Codex can also see ~/.codex/config.toml as ./.codex/config.toml, a project-local config file. Codex rejects user-level-only keys such as model_providers and notify in project-local config. That warning comes from native Codex config layering, not from the ccsx auth profile resource links. Launch from a project directory or move project-local Codex config out of $HOME/.codex/config.toml if the warning is noisy.

ccsx vs ccsxp

ccsx auth profiles apply only to the native codex CLI. They have no effect on ccsxp (the CLIProxy round-robin pool). ccsxp unconditionally sets its own CODEX_HOME on startup and ignores CCS_CODEX_PROFILE.

If you run eval "$(ccsx auth use work)" and then invoke ccsxp, a notice is emitted to stderr:

[i] CCS_CODEX_PROFILE is ignored by ccsxp; profile applies to native 'codex' only

cmd.exe

ccsx auth use emits set FOO=bar syntax for cmd.exe. Native eval is not available in legacy cmd — use PowerShell (Invoke-Expression) instead.

Backup files from --force

When re-importing with --force, the existing auth.json is backed up as auth.json.bak-<timestamp> in the profile directory. These accumulate over time; remove them manually when no longer needed.