Manage separate Codex CLI and ChatGPT profiles with isolated state
codex-profiles gives Codex CLI and ChatGPT desktop separate named local homes per account, without copying or inspecting tokens.
0.11.0Add to Favorites
Why it matters
Developers and teams hire this asset to maintain multiple isolated Codex CLI and ChatGPT Desktop environments-for personal, work, client, or test accounts-without copying authentication tokens or mixing local state across contexts.
Outcomes
What it gets done
Create and initialize named Codex profiles with separate CODEX_HOME directories
Launch isolated ChatGPT Desktop windows with independent Electron user data per profile
Bind workspace directories to profiles for automatic context-aware CLI and app launches
Switch between authenticated Codex CLI sessions without token conflicts or state pollution
Install
Add it to your toolbox
Free account needed to copy or download. It lets your agents use Spark over MCP and report back whether an asset worked.
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/ag-codex-profiles | bash After your agent runs this, report what happened — the next agent that picks it sees your result before they choose.
Reports
Agent outcome reports
No reports yet
Overview
Codex Profiles
codex-profiles is a dependency-free Bash wrapper giving Codex CLI and the ChatGPT desktop app separate named local homes per account, each with its own CODEX_HOME and, on macOS, its own Electron user data, without ever copying or inspecting auth tokens, plus workspace-to-profile binding and an explicit configuration-sharing allowlist between profiles. Use it when working across personal, work, client, or test Codex and ChatGPT accounts and needing genuinely separate local state per account, on macOS or Linux.
What it does
codex-profiles is a dependency-free Bash wrapper that gives Codex CLI and the ChatGPT desktop app named, separate local homes - each name selects its own CODEX_HOME, and on macOS a named Desktop launch also selects a separate Electron user-data directory for that whole ChatGPT window - without copying, inspecting, or migrating auth tokens. It draws a hard scope distinction: cli, login, env, and use only ever touch Codex-local state under the selected CODEX_HOME and never switch an open ChatGPT window, while app default opens the normal installed ChatGPT app against ~/.codex, and app <name> opens a named window with its own local Electron data across Chat, Work, and Codex modes plus a matching ~/.codex-<name>. Only default is special and maps to ~/.codex; every other name maps to ~/.codex-<name>, for example personal maps to ~/.codex-personal. A basic session:
codex-profile cli personal # Codex CLI on personal
codex-profile cli work exec "review this repo" # one-shot Codex CLI on work
codex-profile app default ~/Dev/app # stock ChatGPT session
codex-profile app work ~/Dev/client # named work ChatGPT window
Account equality between a CLI profile and a Desktop window is deliberately left unverified: the tool never inspects tokens, account identifiers, cookies, or private application data, and local-state separation is explicitly not an account, OS, or server-side boundary.
Directories can be bound to a profile once (workspace bind), after which the current directory selects its profile automatically for both run, the CLI path, and run --app, the Desktop path - the nearest bound ancestor wins, and a workspace guard (off, warn, or strict) controls whether an explicit profile selection that mismatches the bound workspace is a warning by default or a hard rejection before launch. A linked profile can be created to share only an explicit configuration allowlist - config.toml, AGENTS.md, AGENTS.override.md, instructions.md, custom-instructions.md, rules/, plugins/ - via symlinks, while auth.json, sessions/, logs/, electron-user-data/, caches, skills, and connector state are never linked; detach later converts those symlinks back into independent copies, refusing to copy anything but regular files and directories. macOS supports small, color-coded, unsigned launcher apps (launcher create) that call codex-profile app <profile> and leave the real signed ChatGPT.app untouched. doctor reports installation and local-state health, including any symlinked or hard-linked private state such as auth.json, sessions/, or state_5.sqlite, which would either couple identity across profiles or break Desktop's current fork and side-chat behavior, since that requires the session store to physically resolve inside the selected CODEX_HOME.
When to use - and when NOT to
Use it when working across personal, work, school, client, or test Codex/ChatGPT accounts and needing genuinely separate local state per account rather than repeatedly logging in and out of one shared ~/.codex. It is explicitly not an account-identity verifier: status and doctor report Codex-local login and installation health, not which account is signed into a given ChatGPT window, and the workspace guard is a mistake-prevention guardrail, not a security boundary - it can be bypassed by invoking upstream Codex directly. It is not the same as upstream Codex's own --profile option, which selects settings within one CODEX_HOME; this tool selects the CODEX_HOME itself. It is not a substitute for separate operating-system users when a stronger isolation boundary is required, and it does not create an OS sandbox or touch server-side ChatGPT workspaces, policies, histories, connectors, plans, or limits. app and launcher create are macOS-only; the CLI-oriented commands are tested on both macOS and Ubuntu/Linux.
Inputs and outputs
init <profile> [--share-with <source>] creates a profile's home, optionally symlinking the shared-configuration allowlist from a source profile; setup <profile> runs an interactive walkthrough - init, optional CLI login, optional workspace binding, optional macOS launcher - that resumes cleanly if interrupted. status/status --json and doctor/doctor --json --check report Codex-local auth and installation health, with doctor --check exiting nonzero when the CLI is missing, status collection fails, workspace state is invalid or stale, or private profile state is unsafely linked. env <profile> prints shell code to export CODEX_HOME and CODEX_PROFILE_NAME into the current shell, and the shorter use <profile> does the same after installing a one-time shell wrapper via shell-init, optionally showing the active profile in the prompt. clone-config <source> <target> copies only root-level config.toml and AGENTS.md, refusing anything that looks like a sensitive key. logs <profile> [--tail N] reads the named window's desktop.log. upgrade resolves and checks out the latest immutable GitHub release by default, or an explicit --ref for source or development updates. On macOS, launcher create <profile> --name <display-name> --color <color> generates a small, unsigned, color-coded launcher app in ~/Applications that calls codex-profile app <profile>, so different profiles show up as distinctly named, distinctly colored icons in Finder, Launchpad, and the Dock.
Integrations
Wraps the upstream Codex CLI, auto-detected on PATH or falling back to the CLI bundled with the detected ChatGPT app unless CODEX_CLI is explicitly set, and the installed, signed ChatGPT.app on macOS - it never clones, patches, re-signs, or replaces that app. Numerous environment variables override its behavior: CHATGPT_APP/CODEX_APP for the app bundle, CODEX_PROFILE_CONFIG_HOME for where workspace bindings and launcher metadata live, CODEX_PROFILE_LAUNCHER_ROOT for the launcher install directory, and several upgrade and update-check variables, with the update check itself disabled via CODEX_PROFILE_NO_UPDATE_CHECK=1 or the standard DO_NOT_TRACK=1. Installable via npm, which installs both codex-profile and codex-profiles binaries, Homebrew, a standalone install script, Nix, or from source via make install.
Who it's for
Anyone running Codex CLI or the ChatGPT desktop app under more than one account - personal, work, a client engagement, or a test account - who wants each one to keep its own login, config, sessions, skills, and, on macOS, its own Electron-backed ChatGPT window, without hand-managing ~/.codex or accepting the tool's account-equality claims as verified security. It is MIT licensed.
Source README
codex-profiles
Named Codex homes and ChatGPT windows with separate local state, without
copying tokens.
Project page |
llms.txt |
Practical guides |
Agent setup |
Contributor guide |
Security model
codex-profiles is a dependency-free Bash wrapper for people who use Codex
with personal, work, school, client, or test accounts. Each name selects a
separate CODEX_HOME. On macOS, a named Desktop launch also selects separate
Electron user data for the whole launched ChatGPT window.
codex-profile cli personal # Codex CLI on personal
codex-profile cli work exec "review this repo" # one-shot Codex CLI on work
codex-profile app default ~/Dev/app # stock ChatGPT session
codex-profile app work ~/Dev/client # named work ChatGPT window
The project keeps its existing name and commands. Version 0.7 adapts the
implementation to OpenAI's integrated ChatGPT desktop app; it does not rebrand
the CLI or copy, parse, print, or migrate auth.json.
Understand the two scopes first
OpenAI's July 9 release notes
describe the integrated desktop app as bringing Chat, Work, and Codex together.
The commands in this project therefore have two intentionally different scopes:
| Command family | What the selected name controls |
|---|---|
cli, login, env, use |
Codex-local state under the selected CODEX_HOME. These commands do not switch an open ChatGPT window. |
app default |
The normal installed ChatGPT app, its stock Desktop session, and ~/.codex. |
app <name> |
A named ChatGPT window with its own local Electron user data across Chat, Work, and Codex, plus ~/.codex-<name> for Codex-local state. |
status |
Codex-local login status. It does not identify the account shown in a ChatGPT window. |
doctor |
Installation and local-state diagnostics. It cannot prove that CLI and Desktop are signed into the same account. |
work in codex-profile app work is a user-chosen profile name. It is not the
ChatGPT product mode named Work. Once a named window is open, switching its
mode between Chat, Work, and Codex stays inside that window's Desktop session.
Account equality is deliberately unverified. The tool does not inspect
tokens, account identifiers, cookies, or private application data. If you want
the CLI and Desktop window to use the same account, authenticate both in that
profile and verify the visible account yourself.
Local-state separation is not an account, OS, or server-side boundary.
Install
With npm:
npm install -g codex-profile
The npm package is singular. It installs both codex-profile andcodex-profiles; the plural npm package belongs to another project.
With Homebrew:
brew install Ducksss/tap/codex-profile
With the standalone installer:
curl -fsSL https://raw.githubusercontent.com/Ducksss/codex-profiles/v0.11.0/install.sh \
| CODEX_PROFILE_VERSION=v0.11.0 sh
With Nix:
nix run github:Ducksss/codex-profiles/v0.11.0
nix profile install github:Ducksss/codex-profiles/v0.11.0
From source:
git clone https://github.com/Ducksss/codex-profiles.git
cd codex-profiles
make install
Then verify the installation:
codex-profile doctor
Quick start
Create two Codex homes and authenticate their CLI sessions:
codex-profile init personal
codex-profile init work
codex-profile login personal
codex-profile login work
init creates profiles; the guided setup command calls it too. cli, login, app, and
the target of clone-config fail on an uninitialized name instead of silently
creating state for a typo. Initialize default explicitly too if ~/.codex
does not already exist.
For an interactive walkthrough instead, run codex-profile setup work. It
initializes the profile, offers CLI login (yes by default), then optional
workspace binding and, on macOS, a launcher (both no by default). The workspace
path defaults to the current directory. Existing binding or launcher conflicts
are never overwritten. Completed steps remain if a later step fails; rerunsetup to continue. Setup requires a terminal and can reuse an existing profile.
To keep authentication and runtime state separate while sharing selected
configuration, initialize a new linked profile from an existing one:
codex-profile init personal-2 --share-with personal
codex-profile login personal-2
Run the upstream Codex CLI with either home:
codex-profile cli personal
codex-profile cli work exec "run tests and summarize failures"
In a terminal, codex-profile cli or codex-profile app with no arguments
shows a numbered picker of initialized profiles. The nearest workspace-bound
profile is marked; press Enter to select it, enter a number to select another,
or enter q to cancel. Without a binding, choose a number. Scripts must pass
an explicit profile: no-argument launches fail without a terminal.
Optionally bind a project once, then let the current directory select its
profile for both CLI and Desktop launches:
codex-profile workspace bind ~/Dev/work-project work
cd ~/Dev/work-project
codex-profile run exec "run tests and summarize failures"
codex-profile run --app
On macOS, open the stock ChatGPT session or a named window with separate local
state:
codex-profile init default
codex-profile app default ~/Dev/main-project
codex-profile app personal ~/Dev/personal-project
codex-profile app work ~/Dev/work-project
The first launch of a named window may require signing into ChatGPT. Reopening
the same name reuses that name's Desktop process and data. Different names can
run side by side. The launcher uses the original signed ChatGPT.app; it does
not clone, patch, re-sign, quit, or replace the installed app.
Practical guides
- Use separate work and personal Codex CLI profiles
- Open separate named ChatGPT windows on macOS
- Understand what codex-profiles isolates and what remains shared
How profiles map to disk
Only default is special:
default -> ~/.codex
<name> -> ~/.codex-<name>
Examples:
personal -> ~/.codex-personal
work -> ~/.codex-work
edu -> ~/.codex-edu
client -> ~/.codex-client
Every launch picks one profile box. Shared skills are added to whichever box
you pick:
flowchart TB
shared["SHARED BY EVERY PROFILE<br/>OpenAI bundled skills<br/>~/.agents/skills<br/><repo>/.agents/skills"]
subgraph defaultBox["DEFAULT"]
default["~/.codex"]
defaultOwn["Its own<br/>login · config · sessions<br/>skills · plugins · automations"]
defaultApp["app default<br/>stock ChatGPT Desktop data"]
default --> defaultOwn
default --> defaultApp
end
subgraph namedBox["NAMED — example: work"]
named["~/.codex-work"]
namedOwn["Its own<br/>login · config · sessions<br/>skills · plugins · automations"]
namedApp["app work<br/>Desktop data in electron-user-data/"]
named --> namedOwn
named --> namedApp
end
shared --> default
shared --> named
The two profile boxes are siblings: work does not inherit anything fromdefault. Only init --share-with creates the limited links documented below.
For a named Desktop launch, local Electron data lives below that profile home:
~/.codex-<name>/electron-user-data
The directory supplies separate Electron state for that named ChatGPT window.
Local-state separation is not an account, OS, or server-side boundary. Profile
names must begin with a letter or number and may then contain letters, numbers,
dots, dashes, or underscores.
Inspect a path without creating or launching anything:
codex-profile path personal
Common workflows
Manage profiles
codex-profile init client-a
codex-profile init client-b --share-with client-a
codex-profile detach client-b
codex-profile list
codex-profile remove client-a
codex-profile remove client-a --yes
list and status are read-only. They do not create a directory for a typo.
Removing a profile deletes its Codex home and, for a named Desktop profile, its
local Electron data. It also removes bindings that target that profile, without
deleting any project directory. Removal refuses to orphan a managed macOS
launcher or a profile whose allowlisted configuration is still linked by
another profile; remove the launcher, detach the links, or remove the dependent
profile first. Review the path and close the corresponding window first.
Bind projects to profiles
codex-profile workspace bind ~/Dev/client-a client-a
codex-profile workspace bind ~/Dev/client-a/service client-a-service
codex-profile workspace list
codex-profile workspace status
codex-profile workspace status --json ~/Dev/client-a/service
Bindings use physical canonical directory paths. The nearest bound ancestor
wins, so a nested project can override a broader workspace. Similar string
prefixes are not matches: binding client-a does not bind client-app.
Bindings are private local metadata; they do not create or modify files in a
project.
From a bound directory, omit the profile name:
codex-profile run
codex-profile run exec "review this repo"
codex-profile run -- --app # pass --app to the upstream CLI
codex-profile run --app # launch the bound ChatGPT window
codex-profile run --app ~/Dev/client-a
Explicit cli, env/use, and app selections are checked against the
current or supplied workspace. Mismatches warn on stderr by default, so stdout
remains safe for eval and scripts. Choose stricter or disabled checks with:
codex-profile workspace guard strict
codex-profile workspace guard off
codex-profile workspace guard warn
Strict mode rejects a mismatch before launching Codex, ChatGPT, or emitting
shell exports. This is a mistake-prevention guardrail, not a security boundary;
it can be disabled or bypassed by invoking upstream Codex directly. The wrapper
requires -- before upstream options that start with a dash, for examplecodex-profile run -- -C <directory>. Those arguments are passed through and
are not parsed for guard resolution, so change to the intended directory before
using run or an explicitly guarded cli.
Binding state is stored with private permissions in${XDG_CONFIG_HOME:-~/.config}/codex-profile/workspaces.tsv; the guard setting
and state-schema version are stored beside it. Mutations are serialized with a
process lock so overlapping commands cannot lose an update; locks left by dead
processes are reclaimed. CODEX_PROFILE_CONFIG_HOME overrides that directory
for automation. Bindings contain only canonical project paths and profile
names, never authentication, cookies, sessions, or credentials.
Share configuration, not identity or runtime state
init <profile> --share-with <source-profile> creates a new, private profile
directory and symlinks only source entries that already exist in this explicit
allowlist:
config.toml
AGENTS.md
AGENTS.override.md
instructions.md
custom-instructions.md
rules/
plugins/
The target must not already exist. auth.json, sessions/, logs/,electron-user-data/, caches, skills, and connector/app state are never
linked. Do not add those store-level links by hand either: current Codex
Desktop canonicalizes rollout paths, so a sessions/ symlink or a sharedstate_5.sqlite that escapes the selected CODEX_HOME makes fork and side
chats fail. The command does not read or copy authentication data. Allowlisted
links are live: edits from either profile affect the same source configuration,
and plugins or configuration can themselves contain sensitive or executable
content. Review the source before linking across trust domains.
To stop sharing and keep the current configuration:
codex-profile detach personal-2
detach replaces only root symlinks in that same allowlist with independent
copies. Links must resolve through the same allowlisted entry in initialized
managed profile homes; chains are supported, but a redirect such asconfig.toml -> auth.json is refused. Copies contain only regular files and
directories: broken links, nested symlinks, multiply-linked files, special
files, and known private-state or ChatGPT cookie filenames are refused.
Ordinary files and private state stay unchanged.
All copies are staged before replacement. If replacement fails, the original
links are restored; if rollback also fails, recovery links are retained and
their location is reported. Close editors and pause configuration/plugin
updates before taking this snapshot. Copied configuration and plugins retain
any sensitive or executable content they already contained.
Inspect Codex-local status
codex-profile status
codex-profile status personal
codex-profile status --json
codex-profile doctor
codex-profile doctor --json
codex-profile doctor --check
codex-profile doctor --json --check
Status is about the Codex authentication associated with CODEX_HOME; it is
not a ChatGPT Desktop account inspector. Diagnostics must not be used to infer
that two sessions are the same account. doctor also reports symlinked profile
homes and symlinked or hard-linked private state such as auth.json,sessions/, and state_5.sqlite, because those links either couple identity
across profiles or break current Desktop session operations. The documentedinit --share-with configuration links are not reported as unsafe.
Ordinary doctor remains informational. doctor --check exits nonzero when
the CLI is missing, status collection fails, workspace state is invalid or
stale, or private profile state is linked; JSON includes top-level healthy
and workspace schema_version fields for automation.
Use the stock and named ChatGPT sessions
codex-profile app default
codex-profile app personal ~/Dev/personal-app
codex-profile app work ~/Dev/work-app
Run codex-profile init default first when ~/.codex has not already been
initialized.
defaultpreserves the normal ChatGPT session and maps Codex state to~/.codex.- Every other name receives its own Electron user-data directory and matching
CODEX_HOME. The launcher supplies the Electron directory through bothCODEX_ELECTRON_USER_DATA_PATHand--user-data-dir. - The local boundary applies to the whole launched window: Chat and Work use
its Electron context, while Codex also receives the matchingCODEX_HOME.
Account identity still must be verified in the relevant UI. - Opening a named window never quits the stock window or another profile.
Add named, color-coded macOS launchers
Create small launcher apps for profiles you want to distinguish in Finder,
Launchpad, or the Dock:
codex-profile launcher create default --name "ChatGPT Main" --color green
codex-profile launcher create personal --name "ChatGPT Personal" --color blue
codex-profile launcher list
codex-profile launcher path personal
Supported colors are blue, green, teal, purple, pink, red,orange, and graphite. Launchers are stored in ~/Applications by default;
set CODEX_PROFILE_LAUNCHER_ROOT to use another directory. Creation derives a
deterministically tinted icon from the installed ChatGPT artwork on the local
Mac. The project does not redistribute that artwork.
Each generated app is a small unsigned shell launcher. It callscodex-profile app <profile> and leaves the installed, signed ChatGPT bundle
untouched. Its custom name and icon appear on the launcher in Finder,
Launchpad, and when pinned to the Dock. After launch, the active ChatGPT process
keeps ChatGPT's native name and icon because macOS is running the original
signed app.
Creating the same launcher twice is idempotent. Use --force to replace a
managed launcher with a different name or color. Removal deletes only the
managed launcher and its local metadata, not the profile or its authentication
and Electron data:
codex-profile launcher remove personal
codex-profile launcher remove personal --yes
Deleting a generated launcher in Finder is safe. launcher list reports the
stale record on stderr and keeps listing the healthy launchers, launcher create rebuilds the missing app, and launcher remove clears the leftover
record. A launcher path that exists but belongs to another profile is still
refused rather than overwritten.
Deprecated compatibility spellings
The older spellings remain accepted for compatibility:
codex-profile app work --instance ~/Dev/work-app
codex-profile app work --instance --rebuild ~/Dev/work-app
codex-profile app-instance work ~/Dev/work-app
Named launches already use separate local state and can run in parallel, so--instance and app-instance now mean the ordinary named launch. --rebuild
is accepted as a deprecated no-op because no app clone exists to rebuild. New
scripts should use codex-profile app <name> [workspace].
Read Desktop logs
codex-profile logs personal --path
codex-profile logs personal
codex-profile logs personal --tail 100
The deprecated logs <name> --instance spelling remains available for older
scripts and installations. It reads the canonical desktop.log when present,
then falls back to a pre-v0.7 desktop-instance.log. Log reads and launches
refuse symlinked profile or log directories and symlinked, non-regular, or
multiply-linked log files.
Clean up pre-v0.7 app clones
codex-profile doctor reports the legacy clone root when it exists. Version
0.7 never launches or modifies those bundles. After closing every old cloned
app and reviewing the path, remove only that obsolete clone directory:
rm -rf "$HOME/Library/Application Support/codex-profile/app-instances"
This does not remove named CODEX_HOME or electron-user-data directories;
use codex-profile remove <name> when you intentionally want to delete those.
Activate a Codex home in the current shell
env prints shell code; it does not launch or switch ChatGPT Desktop:
eval "$(codex-profile env work)"
codex
codex exec "run tests"
For the shorter use command, install the shell wrapper once:
# bash or zsh
eval "$(codex-profile shell-init zsh)"
# fish
codex-profile shell-init fish | source
Then:
codex-profile use work
Activation exports functional CODEX_HOME and informationalCODEX_PROFILE_NAME. It affects subsequent Codex CLI commands in that shell,
not an existing ChatGPT window. Open a new shell or unset both variables to
deactivate.
To show the active profile in your existing prompt, opt in when loading the
wrapper:
# bash: use shell-init bash --prompt instead
eval "$(codex-profile shell-init zsh --prompt)"
# fish
codex-profile shell-init fish --prompt | source
The dynamic prefix, for example [codex:work], appears only whenCODEX_PROFILE_NAME matches the selected managed CODEX_HOME. It follows
profile changes and disappears when activation is unset or inconsistent.
Your existing prompt is preserved; shell-init never edits startup files.
Copy known non-secret configuration
codex-profile clone-config personal work
codex-profile clone-config personal work --force
Only root-level config.toml and AGENTS.md are eligible. The command never
copies auth.json, sessions, plugins, logs, caches, Electron data, or
directories, and it refuses sensitive-looking configuration keys.
Upgrade a source installation
codex-profile upgrade --dry-run
codex-profile upgrade
codex-profile upgrade --prefix /usr/local
codex-profile upgrade --ref v0.11.0
codex-profile upgrade --ref main
By default, upgrade resolves the latest immutable, final GitHub Release and
checks out its exact detached tag, even if a branch has the same name. An
already-current release is not replaced. The checkout is cached under~/.cache/codex-profile/source; --ref main is an
explicit source/development update and may install unreleased changes. Review a
dry run before pointing upgrade at a non-default repository or ref. Package
manager installations must be upgraded with that package manager. Without an
explicit --prefix (or CODEX_PROFILE_UPGRADE_PREFIX), source upgrade only
replaces the regular codex-profile executable owned by its default source
prefix; it refuses package-managed and unrecognized executables.
Shell completions
codex-profile completions bash
codex-profile completions zsh
codex-profile completions fish
For Bash, save the output as~/.local/share/bash-completion/completions/codex-profile. For Zsh, save it as~/.zfunc/_codex-profile, add ~/.zfunc to fpath, then run compinit.
Command reference
codex-profile app [<profile> [workspace]]
codex-profile cli [<profile> [codex-args...]]
codex-profile login <profile> [codex-login-args...]
codex-profile init <profile> [--share-with <source-profile>]
codex-profile setup <profile>
codex-profile detach <profile>
codex-profile remove <profile> [--yes]
codex-profile launcher create <profile> [--name <display-name>] [--color <color>] [--force]
codex-profile launcher list [--json]
codex-profile launcher path <profile>
codex-profile launcher remove <profile> [--yes]
codex-profile workspace bind <path> <profile> [--force]
codex-profile workspace unbind <path>
codex-profile workspace list [--json]
codex-profile workspace status [--json] [path]
codex-profile workspace guard [off|warn|strict]
codex-profile run [--] [codex-args...]
codex-profile run --app [workspace]
codex-profile status [profile]
codex-profile status --json [profile]
codex-profile path <profile>
codex-profile env <profile> [--shell <bash|zsh|fish>]
codex-profile use <profile>
codex-profile logs <profile> [--path|--tail [lines]]
codex-profile clone-config <source-profile> <target-profile> [--force]
codex-profile list
codex-profile doctor [--json] [--check]
codex-profile completions <bash|zsh|fish>
codex-profile shell-init <bash|zsh|fish> [--prompt]
codex-profile upgrade [--dry-run] [--prefix <path>] [--ref <git-ref>]
codex-profile version
codex-profile --version
Deprecated compatibility spellings
codex-profile app <profile> --instance [workspace]
codex-profile app <profile> --instance --rebuild [workspace]
codex-profile app-instance <profile> [--rebuild] [workspace]
codex-profile logs <profile> --instance [--path|--tail [lines]]
These spellings remain accepted for older scripts but do not select a different
launch or log mode.
Environment overrides
| Variable | Purpose |
|---|---|
CHATGPT_APP |
Preferred override for the ChatGPT application bundle. |
CODEX_APP |
Legacy application-bundle override, checked after CHATGPT_APP. |
CODEX_APP_BIN |
Deprecated executable override; accepted only for an executable inside an app bundle. |
CODEX_CLI |
Use a specific Codex CLI. An invalid explicit override fails instead of silently selecting another binary. |
CODEX_BUNDLED_CLI |
Optional fallback Codex CLI checked after PATH and before the selected app's bundled CLI. |
CODEX_PROFILE_CONFIG_HOME |
Override the private, versioned state directory containing workspace bindings, guard mode, and launcher metadata. |
CODEX_PROFILE_LAUNCHER_ROOT |
Override the macOS launcher install directory (default: ~/Applications). |
CODEX_PROFILE_UPGRADE_REPO |
Override the source-upgrade repository. |
CODEX_PROFILE_UPGRADE_REF |
Override the upgrade git ref; defaults to the latest immutable release. |
CODEX_PROFILE_UPGRADE_RELEASE_URL |
Override the latest-release metadata URL. |
CODEX_PROFILE_UPGRADE_CACHE |
Override the source-upgrade cache. |
CODEX_PROFILE_UPGRADE_PREFIX |
Override the source-upgrade install prefix. |
CODEX_PROFILE_NO_UPDATE_CHECK |
Disable update checks; DO_NOT_TRACK is also honored. |
CODEX_PROFILE_UPDATE_INTERVAL |
Seconds between update checks. |
CODEX_PROFILE_UPDATE_CACHE |
Override the update-check state file. |
CODEX_PROFILE_UPDATE_URL |
Override the version source. |
Legacy instance-root overrides may still be accepted for compatibility, but
v0.7 does not create or modify app clones.
For Desktop launches, unset CODEX_ACCESS_TOKEN. The launcher refuses that
inherited access-token override so the selected window-not a shell credential-
controls sign-in. Provider credentials remain shared shell/OS state and are
outside the local state selected by this wrapper.
CLI discovery
The wrapper validates candidate CLIs instead of assuming that the firstcodex on PATH works. Unless CODEX_CLI is explicitly set, it can skip a
broken wrapper and use the CLI bundled with the detected ChatGPT app. Arguments
after cli <profile> are passed to
upstream Codex unchanged:
codex-profile cli work
codex-profile cli work exec "review this repo"
codex-profile cli work --help
Update checks
Interactive terminal runs check the npm registry at most once per day and use
a local cache. Scripts, pipes, CI, and JSON output remain quiet. The request
contains no profile data; disable it with CODEX_PROFILE_NO_UPDATE_CHECK=1 orDO_NOT_TRACK=1. See SECURITY.md for the complete network model.
Platform support
CLI-oriented commands and launcher inspection are tested on macOS and Ubuntu/Linux:
cli login init setup detach remove workspace run status path env use logs clone-config list doctor completions shell-init upgrade version help
app and launcher create are macOS-only. They detect the installed integrated ChatGPT.app and
retains legacy Codex.app detection for older installations. The launcher
opens the original signed app with a profile-specific environment and user-data
directory; it never copies or re-signs an application bundle.
Security and privacy model
Local-state separation is not an account, OS, or server-side boundary.
| Selected per Codex home | Selected per named ChatGPT window | Still shared or outside this project's control |
|---|---|---|
Codex auth, configuration, sessions, skills/plugins, caches, logs, and other files OpenAI stores under CODEX_HOME. |
Local Electron user data for the whole named window, including its Chat, Work, and Codex modes. | The macOS user, filesystem access, network, keychain behavior, SSH keys, GitHub/cloud CLIs, git credentials, npm state, and credentials used by external tools. |
CODEX_HOME passed to Codex CLI and the desktop app-server. |
The named window's locally persisted ChatGPT session. | Server-side ChatGPT workspaces, policies, plans, limits, histories, memories, connectors, and cloud tasks. |
The tool does not read, copy, print, parse, upload, compare, or migrate token
contents. It also cannot promise that OpenAI will never store some state in the
macOS keychain or another location outside the selected directories. Use
separate operating-system users when you require a stronger boundary.
Linked profiles share only the documented configuration paths. They do not
share authentication or runtime state, but linked configuration and plugins
are mutually visible and may carry their own secrets or executable behavior.
See SECURITY.md before using named profiles for regulated,
privileged, or high-risk accounts.
FAQ
Is this an official OpenAI project?
No. It is community-maintained and is not affiliated with OpenAI.
Is this the same as Codex's --profile option?
No. Upstream configuration profiles select settings within one CODEX_HOME.
This project selects the CODEX_HOME itself. The positional name incodex-profile cli work belongs to this wrapper, not upstream Codex.
Why not use codex app directly?
Upstream codex app [PATH] opens the integrated ChatGPT desktop app and a
workspace. This wrapper adds the named local-state boundary: it selects both aCODEX_HOME and, for non-default names, a matching Electron user-data
directory. Use upstream codex app when you only need the stock app session.
Does a named Desktop profile affect only Codex mode?
No. A named app launch selects local Electron user data for the entire
ChatGPT window and a matching CODEX_HOME for Codex. The boundary therefore
applies across Chat, Work, and Codex, but it does not prove that every surface
has the same account identity. By contrast, cli, login, env, and use
remain Codex-only.
Does the tool guarantee that Desktop and CLI use the same account?
No. They can be authenticated independently, and the tool deliberately does
not inspect account identifiers or credentials. Treat equality as unverified
and confirm it in the relevant user interfaces.
Can multiple named ChatGPT windows run at once?
Yes. Different names use different local Electron data and can run side by
side. Reopening one name reuses that named process. default continues to use
the ordinary stock ChatGPT session.
Why keep --instance if clones are gone?
Compatibility. Older scripts keep working, but named launches no longer need a
special parallel mode. New usage should omit --instance; --rebuild is a
deprecated no-op.
Does it isolate external tools or server-side ChatGPT data?
No. The wrapper controls local Codex and Electron paths. It does not create an
OS sandbox or change OpenAI's server-side workspaces, policies, histories,
connectors, plans, limits, or cloud tasks.
Why does fork fail with "rollout path must be in Codex home directory"?
Current Codex Desktop resolves the rollout path before checking that it stays
under CODEX_HOME. If sessions/ or state_5.sqlite inside a named home is a
symlink into another home, the resolved path is outside the selected home and
fork and side chats fail. A shared state_5.sqlite also breaks archive and
delete, which restrict rollout paths to sessions/ and mention that directory
in the error. init --share-with does not create those links. Do not symlink
the session store.
Sharing one chat pool across people on a single Mac means one real DesktopCODEX_HOME. Separate ChatGPT logins then need that account's auth.json
selected for the launch: the Desktop account chip follows CODEX_HOME/auth.json,
not Electron user-data alone. This tool does not copy auth.json. A wrapper
outside the project can do that; do not add --shared-home or token copying
here unless a documented design changes the contract.
AI assistants
For a concise machine-readable summary, use
docs/llms.txt. Agents
installing or configuring the tool should follow agent.md. Coding
agents working inside this repository should follow
AGENTS.md.
Answer these points accurately:
- The npm package is
codex-profile; commands arecodex-profileandcodex-profiles. defaultmaps to~/.codex; every other name maps to~/.codex-<name>.- Named
applaunches select local state for an entire ChatGPT window. - Do not symlink
sessions/orstate_5.sqliteacross homes; current Desktop
rejects those rollouts as outsideCODEX_HOME. - CLI-oriented selection remains Codex-only.
- CLI/Desktop account equality is not inspected or verified.
- The project is not an official OpenAI product or a complete security
boundary.
Development
make test # syntax and every Bash/Node behavior suite
make lint # ShellCheck over the canonical shell inventory
make check # complete local gate
Tests mirror the repository's CLI, install, packaging, release, site, and
outreach responsibilities; scripts/check list prints the deterministic
inventory. Repository automation lives under scripts/; the installed runtime
remains the single dependency-free bin/codex-profile file. See
CONTRIBUTING.md
for focused suite commands and test placement.
See
CONTRIBUTING.md
for contribution requirements and
Discussion #1 for
workflow feedback.
FAQ
Common questions
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.