5.6 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. A shared config.toml is linked via symlink so model/provider settings
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
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 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 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.
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)
~/.codex/
└── config.toml # Single shared model/provider config
Caveats
Windows symlinks
On Windows, creating symlinks requires Developer Mode or elevated privileges.
If symlink creation fails, CCS falls back to copying config.toml. In this case,
changes to ~/.codex/config.toml are not automatically reflected in the profile —
you must re-run ccsx auth create <name> --force to refresh the copy.
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.