mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 20:17:45 +00:00
Adds opt-in `ccsx auth import-default <name>` to migrate the existing
~/.codex/auth.json into a new profile, plus the cross-system integration
tests and user-facing documentation.
- import-default-command (C3 torn-write protection):
- readFileSync + JSON.parse with 3x retry / 100ms backoff to survive
Codex's truncate-then-write auth.json refresh race
- decode-id-token sanity-check on JWT shape (catches mid-write JWT
corruption that JSON.parse alone wouldn't notice)
- pgrep -f codex best-effort detection; warns + refuses without
--force-while-running flag if a live codex process is found
- rejects cliproxy-format auth files ({type: "codex", ...} wrapper)
with a clear "use ccs cliproxy ..." pointer
- atomic write to <dest>.tmp.<pid>.<rand> + rename
- --with-history defaults to false per D8 (auth-only is the safer
default; opt in for bulkier data)
- --force backs up existing auth.json to .bak-<ts> before overwrite
- non-destructive — never modifies ~/.codex/; legacy mode keeps
working without ever running this command
- integration tests:
- two-terminal-isolation: two profiles with separate CODEX_HOMEs
write to their own auth.json/history.jsonl with no crosstalk
- ccsxp-independence: codex-auth profile set; ccsxp still uses its
own CCSXP_CODEX_HOME / ~/.codex pool (H5 stderr notice present)
- legacy-fallback: no profiles registered → codex-runtime-router
leaves CODEX_HOME unset → codex falls back to ~/.codex
- import-default.integration: real fs copy + decode + register
- docs/codex-auth.md: user guide covering quick start, two-terminal
example, migration, dashboard, and caveats (cmd.exe, Windows
symlinks, ccsx vs ccsxp distinction)
155 codex-auth-scope tests green (45 Phase 1 + 57 Phase 2 + 19 Phase 3
+ 15 Phase 4 + 19 Phase 5). Full suite 3051/3082 — the 1 failure is a
pre-existing test-pollution issue between ccsxp-runtime.test.ts and
codex-runtime-integration.test.ts that exists on dev today; the test
passes in isolation.
166 lines
5.4 KiB
Markdown
166 lines
5.4 KiB
Markdown
# 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)
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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 (all new shells) |
|
|
| `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>` | All future shells | Writes to `~/.ccs/codex-profiles.yaml` |
|
|
| `eval "$(ccsx auth use <name>)"` | Current shell only | Sets `CODEX_HOME` + `CCS_CODEX_PROFILE` in your shell |
|
|
|
|
Shell syntax for `use`:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# 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.
|