Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ and this project follows semantic versioning for tagged releases.

- npm package metadata and public install documentation for the published
`codex-profile` package.
- Experimental `app-instance` command for launching profile-specific Codex
Desktop app clones with isolated `CODEX_HOME`, Electron user data, and
profile-local instance logs.
- `logs <profile> --instance` for reading experimental app-instance logs.
- Branded README demo asset showing two scoped Codex Desktop profile instances
side by side.
- README launch-mode and isolation-boundary tables for the experimental
parallel Desktop workflow.
- README origin-story section explaining the real multi-account workflow that
motivated the project.

### Changed

- Refreshed README positioning around profile-scoped Codex Desktop instances
and included media assets in the npm package file list.

### Fixed

Expand All @@ -20,6 +35,14 @@ and this project follows semantic versioning for tagged releases.
### Tests

- Added npm package installation coverage.
- Added coverage for app-instance launch isolation, app clone rebuilds, and
completion/help output.

### Fixed

- Fixed experimental app-instance launches on macOS by preserving Codex's
`CFBundleName` for Electron helper lookup and launching cloned bundles
through `open -a` with workspace folders passed as documents.

## 0.2.0 - 2026-05-21

Expand Down
161 changes: 143 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
# codex-profiles

Two Codex profiles. One Mac. No token swapping.

[![CI](https://github.com/Ducksss/codex-profiles/actions/workflows/ci.yml/badge.svg)](https://github.com/Ducksss/codex-profiles/actions/workflows/ci.yml)
[![Latest release](https://img.shields.io/github/v/release/Ducksss/codex-profiles?sort=semver)](https://github.com/Ducksss/codex-profiles/releases)
[![npm](https://img.shields.io/npm/v/codex-profile.svg)](https://www.npmjs.com/package/codex-profile)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Shell: Bash](https://img.shields.io/badge/shell-bash-4EAA25.svg)](bin/codex-profile)
[![Platform: macOS + Linux](https://img.shields.io/badge/platform-macOS%20%2B%20Linux-lightgrey.svg)](#platform-support)

Switch Codex CLI and Desktop accounts with isolated `CODEX_HOME` profiles
instead of copying `auth.json` token files around.
Switch Codex CLI and Desktop accounts with isolated `CODEX_HOME` profiles.
Keep personal, work, school, and client state separated without copying
`auth.json` token files around.

`codex-profiles` is a small Bash wrapper around Codex's `CODEX_HOME` support.
Each profile gets its own Codex home directory, so auth, settings, sessions,
connectors, plugins, caches, logs, and local state stay separated while the
wrapper launches Codex CLI or Codex Desktop with the selected profile.

![Two Codex Desktop profiles running side by side](media/codex-profile-parallel-instances.png)

```sh
codex-profile cli personal
codex-profile cli work exec "review this repo"
codex-profile app edu
codex-profile app-instance personal ~/Dev/app-a
codex-profile app-instance work ~/Dev/app-b
```

## Why It Exists
Expand All @@ -37,6 +43,22 @@ connector state, plugins, caches, and logs shared.

`codex-profile` gives the clean boundary a short command.

## The Workflow That Started It

This started as an account-switching problem between profiles with different
strengths:

- A school Codex account with higher limits for heavy coding sessions, but no
connector setup.
- A personal Codex account with medium limits, but the connector access needed
for email, outreach, and day-to-day automation workflows.

Logging out, logging back in, reopening Desktop, and rebuilding context every
time was slow enough to break focus. Copying token files would have been the
wrong shortcut. The goal was a small command that keeps each account's Codex
state separate, then makes it possible to open the right profile for the job,
including two Desktop profiles side by side when the workflow calls for it.

## Why Not Swap Auth Files?

Auth-file switchers only move `auth.json`. That can change who Codex logs in as,
Expand All @@ -54,16 +76,22 @@ codex-profile <profile> -> one CODEX_HOME per profile
That makes it a better fit for work, personal, education, and client accounts
where local Codex state should not bleed between contexts.

## Demo
## Desktop Demo

![codex-profiles promo frame](media/codex-profiles-saas-promo-frame.png)
The screenshot above shows the experimental Desktop flow: two Codex profiles
side by side, each with its own app clone, `CODEX_HOME`, Electron user data,
and profile-local desktop log. The settings/account panel is visible on purpose
so the profile boundary is easy to inspect.

[Watch the short reveal video](media/codex-profiles-apple-reveal.mp4)

## Highlights

- Isolated Codex homes per profile.
- CLI and Codex Desktop launch support.
- Experimental parallel Codex Desktop app instances for power users on macOS.
- Profile-specific app clones with distinct macOS bundle identifiers.
- Separate Electron user data for each experimental Desktop instance.
- No token copying, parsing, printing, or migration.
- Read-only `list`, `status`, and `doctor` commands for diagnostics.
- JSON output for automation.
Expand Down Expand Up @@ -141,6 +169,21 @@ codex-profile app personal ~/Dev/my-project
codex-profile app work
```

Run an experimental parallel Codex Desktop instance with its own app clone and
Electron user data directory:

```sh
codex-profile app-instance personal ~/Dev/project-a
codex-profile app-instance work --rebuild ~/Dev/project-b
```

Desktop launch modes are intentionally split:

| Command | Use when | Behavior |
| --- | --- | --- |
| `codex-profile app <profile>` | You want the normal Desktop app on one active profile. | Quits the canonical `Codex.app`, then relaunches it with the selected `CODEX_HOME`. |
| `codex-profile app-instance <profile>` | You want multiple Desktop profiles open side by side. | Creates or reuses a profile-specific app clone, separate Electron user data, and a profile-local instance log. |

Check what exists and what is logged in:

```sh
Expand Down Expand Up @@ -231,6 +274,39 @@ codex-profile logs personal
codex-profile logs personal --tail 100
```

Experimental instance logs use their own file:

```sh
codex-profile logs personal --instance --path
codex-profile logs personal --instance --tail 100
```

### Run Parallel Desktop Instances

`app-instance` is the visual power-user workflow: two Codex Desktop profiles,
same macOS user, separate Codex state.

```sh
codex-profile app-instance personal ~/Dev/personal-app
codex-profile app-instance work ~/Dev/work-app
```

The command creates or reuses profile-specific app clones under
`~/Library/Application Support/codex-profile/app-instances`, patches each clone
with a distinct bundle identifier, re-signs it, and launches it without
quitting existing Codex windows.

The separate command name is deliberate. `codex-profile app` remains the
predictable single-app switcher for existing workflows and scripts.
`codex-profile app-instance` is the explicit contract for cloned bundles,
parallel windows, and experimental Desktop behavior.

If Codex Desktop updates or a clone looks stale:

```sh
codex-profile app-instance work --rebuild ~/Dev/work-app
```

### Clone Safe Config

Copy known non-secret config files from one profile to another:
Expand Down Expand Up @@ -336,14 +412,15 @@ alias codex-app-work='codex-profile app work'

```text
codex-profile app <profile> [workspace]
codex-profile app-instance <profile> [--rebuild] [workspace]
codex-profile cli <profile> [codex-args...]
codex-profile login <profile> [codex-login-args...]
codex-profile init <profile>
codex-profile remove <profile> [--yes]
codex-profile status [profile]
codex-profile status --json [profile]
codex-profile path <profile>
codex-profile logs <profile> [--path|--tail [lines]]
codex-profile logs <profile> [--instance] [--path|--tail [lines]]
codex-profile clone-config <source-profile> <target-profile> [--force]
codex-profile list
codex-profile doctor [--json]
Expand All @@ -355,15 +432,16 @@ codex-profile --version

## Environment Overrides

```text
CODEX_APP Override Codex.app path
CODEX_APP_BIN Override Codex Desktop binary path
CODEX_CLI Override Codex CLI binary path
CODEX_PROFILE_UPGRADE_REPO Override upgrade repository
CODEX_PROFILE_UPGRADE_REF Override upgrade git ref
CODEX_PROFILE_UPGRADE_CACHE Override upgrade cache checkout
CODEX_PROFILE_UPGRADE_PREFIX Override upgrade install prefix
```
| Variable | Purpose |
| --- | --- |
| `CODEX_APP` | Override the `Codex.app` path. |
| `CODEX_APP_BIN` | Override the Codex Desktop binary path. |
| `CODEX_CLI` | Override the Codex CLI binary path. |
| `CODEX_PROFILE_APP_INSTANCE_ROOT` | Override the experimental app-instance clone root. |
| `CODEX_PROFILE_UPGRADE_REPO` | Override the upgrade repository. |
| `CODEX_PROFILE_UPGRADE_REF` | Override the upgrade git ref. |
| `CODEX_PROFILE_UPGRADE_CACHE` | Override the upgrade cache checkout. |
| `CODEX_PROFILE_UPGRADE_PREFIX` | Override the upgrade install prefix. |

Examples:

Expand All @@ -384,6 +462,17 @@ The `app` command is macOS-only because it launches `Codex.app` and uses macOS
app-control tooling to quit the running desktop app before relaunching it with a
different `CODEX_HOME`.

The experimental `app-instance` command is also macOS-oriented. It creates a
profile-specific copy of `Codex.app`, patches its display name and bundle
identifier when macOS tooling is available, re-signs the clone, and launches it
without quitting other Codex windows.

Existing clones are checked before launch. If required metadata is missing,
malformed, stale, or was created by an older `codex-profile` version,
`app-instance` rebuilds the clone automatically. Use `--rebuild` after Codex
Desktop updates or whenever you want to force a fresh copy from the installed
`Codex.app`.

## Desktop App Notes

Codex Desktop should run one profile at a time. `codex-profile app <profile>`
Expand All @@ -394,6 +483,34 @@ selected `CODEX_HOME`.
For predictable account switching, launch Codex Desktop through `codex-profile`
instead of Dock or Spotlight.

`app` and `app-instance` stay separate by design. Launching two windows from
`app` would make existing scripts surprising and would hide the important
implementation detail that parallel mode clones and re-signs an app bundle.
The command names describe the contract: `app` switches the canonical app,
while `app-instance` launches a profile-specific Desktop clone.

### Experimental Parallel Instances

`codex-profile app-instance <profile>` is an opt-in escape hatch for users who
need two Codex Desktop profiles open at once. It keeps the normal `app` command
conservative and instead launches a profile-specific app clone with:

- `CODEX_HOME` set to the selected profile home.
- Electron `--user-data-dir` set to `<profile-home>/electron-user-data`.
- A distinct macOS bundle identifier derived from the raw profile name.
- Desktop logs written to `<profile-home>/logs/desktop-instance.log`.
- Instance logs available through `codex-profile logs <profile> --instance`.
- App clones stored under
`~/Library/Application Support/codex-profile/app-instances` by default.

The isolation boundary is intentionally narrow and inspectable:

| Isolated per profile | Still shared by the macOS user |
| --- | --- |
| Codex auth, config, sessions, plugins, caches, logs, and local Codex state under the selected `CODEX_HOME`. | SSH keys, GitHub CLI auth, cloud CLI auth, browser cookies, OS keychain items, npm state, git credentials, and other credentials outside `CODEX_HOME`. |
| Electron user data for the cloned Desktop app. | The same macOS account, filesystem permissions, network environment, Dock, login items, and system keychains. |
| Profile-specific app clone metadata and bundle identifier. | The installed source `Codex.app` bundle used as the clone template. |

## Security Model

`codex-profile` does one security-sensitive thing: it sets `CODEX_HOME` before
Expand All @@ -407,6 +524,10 @@ default repository is this project. `--dry-run` prints the source ref, cache
path, and install prefix before anything changes. Do not point upgrade at a
repository you do not trust.

`app-instance` adds Desktop app clone metadata and Electron user-data isolation,
but it is still profile-level process isolation. It is not a VM, container, or
separate macOS account.

Separate Codex homes are cleaner than swapping `auth.json`, but they are not
full OS-level isolation. Your operating system user still shares SSH keys,
GitHub CLI auth, browser cookies, cloud CLI credentials, npm state, and other
Expand Down Expand Up @@ -441,8 +562,11 @@ are a cleaner boundary.

### Can I run two desktop profiles at once?

Not safely. Codex Desktop is treated as one active profile at a time. The `app`
command quits the current Codex app before launching the selected profile.
The default `app` command intentionally treats Codex Desktop as one active
profile at a time. For an opt-in experimental path, use
`codex-profile app-instance <profile>`. It launches a profile-specific app clone
with separate `CODEX_HOME` and Electron user data, but it does not isolate
external OS-level credentials.

### Does this isolate external tools too?

Expand All @@ -466,7 +590,8 @@ make lint
The test suite covers Bash syntax, profile path mapping, install smoke tests,
CLI/login pass-through, list/version output, npm package installation, source
upgrades, fresh-profile status checks, hardened status discovery, private
desktop log placement, and missing-CLI doctor output.
desktop log placement, app-instance clone metadata validation, parallel
Desktop launch coverage, and missing-CLI doctor output.

## Contributing

Expand Down
5 changes: 5 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ codes, connector credentials, or private logs.
`codex-profiles` does not read or copy Codex auth tokens. It only sets
`CODEX_HOME` before launching Codex.

The experimental `app-instance` command also creates profile-specific Codex app
clones and launches them with separate Electron user data directories. That is
profile-level process isolation for Codex Desktop state, not OS-level
isolation.

It does not isolate non-Codex credentials such as SSH keys, GitHub CLI auth,
cloud CLI credentials, browser cookies, or OS keychain items. Use separate OS
users for stronger isolation.
Loading