mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-15 00:17:09 +00:00
docs(config): preserve active developer contracts
This commit is contained in:
1 parent
deb1ed0e67
commit
fd0d4362b3
2 files changed
+91
-569
No files matched your search
+58
-202
@@ -1,217 +1,73 @@
|
||||
# Codex Auth Profile Isolation (`ccsx auth`)
|
||||
# Codex Auth Developer Contract
|
||||
|
||||
Run two Codex accounts simultaneously — one per terminal — with full auth isolation.
|
||||
The canonical [Codex Adapter guide](https://docs.ccs.kaitran.ca/features/workflow/codex-adapter)
|
||||
owns user setup. This local contract documents active `ccsx auth` invariants
|
||||
that contributors and operators must preserve.
|
||||
|
||||
## Why
|
||||
## Command Surface
|
||||
|
||||
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` owns `create`, `login`, `switch`, `use`, `show`, `remove`, and
|
||||
`import-default`. Keep syntax and option changes sourced from
|
||||
[`src/codex-auth/codex-auth-help.ts`](../src/codex-auth/codex-auth-help.ts)
|
||||
rather than copying a long command reference here.
|
||||
|
||||
`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`, plus its own session data. Shared `config.toml`, `agents/`, `skills/`,
|
||||
and plugin cache resources come from `~/.codex/` so configuration and installed plugin
|
||||
skills stay in sync across profiles.
|
||||
- `create <name>` is idempotent and starts native `codex login` for new
|
||||
profiles. `--force` repairs shared resources without replacing `auth.json`.
|
||||
- `switch <name>` changes the persistent registry default.
|
||||
- `use <name>` emits only shell-evaluable `CODEX_HOME` and
|
||||
`CCS_CODEX_PROFILE` assignments to stdout. It affects the current shell after
|
||||
`eval`/`source`; diagnostics stay on stderr.
|
||||
- `ccsx <name>` launches a named profile directly without changing the
|
||||
persistent default.
|
||||
|
||||
## Quick start (4 commands)
|
||||
## Import Safety
|
||||
|
||||
```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
|
||||
`import-default <name>` imports native `~/.codex/auth.json` without deleting the
|
||||
source. The implementation in
|
||||
[`import-default-command.ts`](../src/codex-auth/commands/import-default-command.ts)
|
||||
must continue to:
|
||||
|
||||
# Activate per terminal (ephemeral — only this shell)
|
||||
# Terminal A:
|
||||
eval "$(ccsx auth use work)"
|
||||
codex
|
||||
- refuse import while a current-user Codex process may be refreshing tokens,
|
||||
unless the operator explicitly accepts the race with
|
||||
`--force-while-running`;
|
||||
- retry and validate JSON/JWT shape, reject CLIProxy auth-file formats, and fail
|
||||
without registering a profile when a torn write persists;
|
||||
- write the destination atomically with private permissions;
|
||||
- omit history and sessions unless `--with-history` is requested;
|
||||
- refuse an existing profile unless `--force` is used, and preserve its current
|
||||
`auth.json` as `auth.json.bak-<timestamp>` before overwrite.
|
||||
|
||||
# Terminal B:
|
||||
eval "$(ccsx auth use personal)"
|
||||
codex
|
||||
## Storage And Cross-Platform Fallback
|
||||
|
||||
# Or launch a named profile directly through ccsx
|
||||
ccsx work
|
||||
```
|
||||
|
||||
## 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 <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:
|
||||
|
||||
```bash
|
||||
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
|
||||
# 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
|
||||
|
||||
```
|
||||
```text
|
||||
~/.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)
|
||||
└── plugins/ # Profile-local parent; may hold local metadata
|
||||
└── cache/ -> ~/.codex/plugins/cache/ (symlink — shared)
|
||||
|
||||
~/.codex/
|
||||
├── config.toml # Single shared model/provider config
|
||||
├── agents/ # Shared Codex agent role config files
|
||||
├── skills/ # Shared Codex skills
|
||||
└── plugins/
|
||||
└── cache/ # Shared installed plugin payloads
|
||||
├── codex-profiles.yaml
|
||||
└── codex-instances/<name>/
|
||||
├── auth.json, history.jsonl, sessions/ # profile-local
|
||||
├── config.toml -> ~/.codex/config.toml
|
||||
├── agents/ -> ~/.codex/agents/
|
||||
├── skills/ -> ~/.codex/skills/
|
||||
└── plugins/
|
||||
└── cache/ -> ~/.codex/plugins/cache/
|
||||
```
|
||||
|
||||
Only `plugins/cache/` is shared. The profile's parent `plugins/` directory remains a
|
||||
real local directory so Codex can keep profile-specific plugin metadata beside the
|
||||
shared cache.
|
||||
The `plugins/` parent stays profile-local. Shared config, resources, and plugin
|
||||
cache repair must preserve existing profile-local content. On Windows or other
|
||||
systems where symlinks are unavailable, CCS copies missing shared content into
|
||||
the profile and warns that later upstream edits will not propagate
|
||||
automatically. See
|
||||
[`codex-config-symlink.ts`](../src/codex-auth/codex-config-symlink.ts),
|
||||
[`codex-profile-resources.ts`](../src/codex-auth/codex-profile-resources.ts),
|
||||
and
|
||||
[`codex-profile-plugin-cache.ts`](../src/codex-auth/codex-profile-plugin-cache.ts).
|
||||
|
||||
`ccsx auth create <name>` and direct `ccsx <name>` launches repair these links
|
||||
idempotently before Codex starts. This keeps relative entries such as
|
||||
`agents/foo.toml` valid and prevents stale first-launch skill warnings after a plugin
|
||||
install or update changes the cache.
|
||||
## `ccsx` And `ccsxp` Isolation
|
||||
|
||||
## Caveats
|
||||
`ccsx auth` applies only to native Codex profiles. `ccsxp` ignores
|
||||
`CCS_CODEX_PROFILE`, uses native `~/.codex` history by default, and routes
|
||||
through its separate CLIProxy Codex pool. `CCSXP_CODEX_HOME` is its explicit
|
||||
home override. Never make a `ccsx auth switch` silently redirect `ccsxp`, merge
|
||||
their auth stores, or consume `ccsx` import backups as pool credentials.
|
||||
|
||||
### Windows symlinks
|
||||
|
||||
On Windows, creating symlinks requires Developer Mode or elevated privileges.
|
||||
If symlink creation fails, CCS falls back to copying `config.toml`, `agents/`,
|
||||
`skills/`, and the current `plugins/cache/` snapshot. Copies do not update live with
|
||||
`~/.codex/`; after a plugin update, another profile launch or
|
||||
`ccsx auth create <name> --force` repair copies newly missing cache entries. Existing
|
||||
profile-local cache files are preserved.
|
||||
|
||||
### 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.
|
||||
Behavior locks live under `tests/unit/codex-auth/` and
|
||||
`tests/integration/codex-auth/`.
|
||||
Reference in new issue
Block a user