Menu bar switcher for Claude Code and Codex sign-ins on a shared Mac.
The problem: two people share one machine, each with their own Claude and ChatGPT subscription. Claude Code and Codex hold one sign-in at a time, and handing the laptop over means logging out and back in through OAuth every time.
The fix: SubSwitch keeps each person's sign-in saved and swaps the live one from the menu bar. Log in once each, save, and from then on switching is one click.
SubSwitch currently requires an Apple Silicon Mac running macOS 12 or later.
Download subswitch.dmg from the latest release, verify it against the attached subswitch.dmg.sha256, drag SubSwitch into Applications, and launch it.
SubSwitch is open source but is intentionally unsigned and not notarized. macOS therefore blocks the first launch. After verifying that the download came from npclfg/subswitch, approve it under System Settings, Privacy & Security, "Open Anyway", or clear the quarantine flag:
xattr -d com.apple.quarantine /Applications/SubSwitch.appOr build from source (Rust and Node required): npm install && npm run tauri build.
SubSwitch has no analytics, telemetry, advertising, crash reporting, or SubSwitch-operated server. Credentials stay on the Mac: saved profiles are stored in the macOS keychain, while ~/.subswitch/state.json contains profile labels only. Usage meters send the live access token directly to the provider's own Anthropic or OpenAI usage endpoint; see How it works for the exact local data touched.
Security issues should be reported privately through GitHub's security advisory form, not a public issue. See SECURITY.md.
- Launch SubSwitch. It lives in the menu bar (the two-arrow icon), and clicking it opens a small panel.
- With your own accounts signed in, click "Save current login" under Claude Code, and again under Codex. The row hints whose sign-in it would save.
- Have the other person sign in the normal way (
claudethen/login, andcodex login), then "Save current login" again. Profiles are named by account email automatically. - From then on, click an email in the panel to switch. Hovering a row reveals an x to remove that saved login.
- A Claude Code sign-in is the "Claude Code-credentials" keychain item plus the account block in
~/.claude.json. A Codex sign-in is~/.codex/auth.json. A switch swaps those and nothing else. - Before every switch, the live sign-in is saved back into its own profile first, so tokens refreshed during a session are never lost. The profile it lands in comes from the credentials themselves, so even a manual
/loginbetween switches gets captured correctly. - Saved sign-ins live in the macOS keychain under the "SubSwitch" service. The only file on disk is
~/.subswitch/state.json, and it holds profile names only. - If Claude Code or Codex is still running, the panel asks before switching. Quit them first for a clean swap.
- The panel shows how much usage the live sign-in has left (Claude: session, per-model, and weekly meters. Codex: weekly). Read from each provider's own usage endpoint with the signed-in token, cached for five minutes, fetched only when the panel opens. Switching always fetches fresh.
- Right after a switch, the meters may say usage updates when the tool next runs. That is normal: sign-in tokens only stay fresh for a few hours and the tool re-mints them on its next run, at which point the meters come back on their own.
Chat history, projects, and settings stay shared: both people see the same ~/.claude and ~/.codex history. For real privacy between users, separate macOS accounts are the right tool. SubSwitch switches whose subscription is signed in, which is the part that affects billing and rate limits.
- No dev server and no frontend build: the panel is plain HTML, CSS, and JS in
dist/, talking to Rust through the global Tauri API.cargo checkandcargo test --libinsidesrc-tauri,npm run tauri buildfor the bundle. - To eyeball the panel UI without launching the app, serve the repo root and open
design/harness.html(?case=saved|confirm|fresh,&theme=light). It stubs the Tauri API with canned state. - The test suite runs against a sandbox (fake home dir, scratch keychain services) and never touches real sign-ins.
cargo test --lib -- --ignored --nocapture real_captureis a read-only diagnostic that prints what a capture of the real live sign-ins would find.SUBSWITCH_HOME,SUBSWITCH_LIVE_SERVICE, andSUBSWITCH_STORE_SERVICEredirect every path and keychain service the app touches, for sandboxed manual testing.
MIT
