Skip to content

lawzava/subswapper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

subswapper

CI License: MIT

subswapper is a small Go CLI that manages multiple Claude Code and Codex subscription accounts on one machine. Capture each account's credential files once, then switch between them without logging in again — manually, or automatically when the active account approaches its usage limits.

Features

  • Capture once, switch instantly — snapshots the credential files of the logged-in account into a private backup, then swaps bundles transactionally with rollback and interrupted-operation recovery.
  • Live usage tracking — reads real usage windows (5-hour, weekly, and Claude's Fable-scoped weekly) straight from each provider using the stored credentials; no scraping, no extra logins.
  • Automatic switching — a monitor loop moves each service to the least-used healthy account when the active one crosses a configurable threshold, with cooldown and minimum-improvement pacing to avoid churn.
  • Safe by design — cross-process locking, atomic replacement of individual files, transactional bundle recovery, 0600 files under 0700 directories, and corruption guards that never overwrite a good credential backup with a broken or foreign live file.
  • Extensible — any other service can be managed by listing its credential files in the config; plug in a custom usage_command for usage probing.

Installation

Requires Go 1.26.5 or newer.

go install github.com/lawzava/subswapper/cmd/subswapper@latest

Or build from source:

git clone https://github.com/lawzava/subswapper.git
cd subswapper
go build ./cmd/subswapper

For Codex usage probing, the codex CLI must be on PATH (see Usage probes).

Quick start

# Create the default config (Claude Code + Codex)
subswapper init

# Log in to an account with the normal client, then snapshot it
subswapper capture -service claude -account personal
subswapper capture -service codex -account personal

# Log in to the next account and snapshot that too
subswapper capture -service claude -account work

# See every account's usage windows
subswapper status

# Switch to a specific account, or let subswapper pick the least-used one
subswapper switch -service claude -account work
subswapper switch -service all -account auto

# Keep switching automatically in the background
subswapper monitor

status prints one row per captured account:

subswapper status 2026-07-02T14:07:31Z

SERVICE    ACCOUNT                  ACTIVE  5H                           WEEKLY                       FABLE5                       SCORE    STATE
-------    -------                  ------  --                           ------                       ------                       -----    -----
claude     personal                 yes     62% reset Jul02 15:00        31% reset Jul05 23:00        18% reset Jul05 23:00        62%      ready
claude     work                             12% reset Jul02 19:00        8% reset Jul07 11:00         4% reset Jul07 11:00         12%      ready
codex      personal                 yes     91% reset Jul02 16:30        44% reset Jul06 09:00        -                            91%      ready

FABLE5 is the weekly window scoped to Claude's Fable models (- for accounts without one). SCORE is the worst of an account's windows — the value auto-switching compares.

Commands

Command Description
init Write a starter config file.
capture -service <name> -account <name> [-email <label>] Snapshot the currently logged-in account's credential files.
switch -service <name> [-account <name>|auto] Restore an account's bundle; auto picks the least-used healthy account.
switch -service all -account auto Auto-pick the best account for every service at once.
status (alias list) Show every captured account with usage windows, score, and state.
monitor [-interval 5m] [-once] [-no-auto] [-verbose] Poll usage on a loop and auto-switch when thresholds are hit. Continuous mode logs events; -verbose prints every table.
remove -service <name> -account <name> [-force] (alias rm) Delete a captured account and its backup.
import-cswap [-root <dir>] Import accounts from an existing claude-swap (cswap) install.
version Print the subswapper version.

All commands accept -config <path> (default ~/.config/subswapper/config.json on Linux).

How auto-switching works

monitor evaluates every service each cycle. With automatic switching enabled, a service moves to the captured account with the lowest worst-window usage only when all of these hold:

  • the active account has reached the switch threshold (default 90%) in its 5-hour, weekly, or Fable weekly window;
  • the best alternative improves the worst-window score by at least the minimum improvement (default 10 percentage points);
  • the cooldown since the service last switched accounts — manually or automatically — has passed (default 30 minutes).

Both pacing rules are skipped when the active account is exhausted or its stored credentials stop working — the monitor escapes to the best healthy account on the next cycle. Accounts whose credentials are missing, rejected, or do not match the stable identity of the captured account are never selected, whatever their cached usage says. A manual switch -account auto always forces the best account immediately.

Before every switch, the outgoing account's live files are synced back into its backup so credentials rotated while it was active are never lost.

Configuration

subswapper init writes a config like this:

{
  "monitor": {
    "interval": "5m",
    "auto_switch": true
  },
  "services": [
    { "name": "claude", "kind": "claude", "display_name": "Claude Code" },
    { "name": "codex", "kind": "codex", "display_name": "Codex" }
  ]
}

The monitor block accepts these knobs (defaults shown):

"monitor": {
  "interval": "5m",
  "auto_switch": true,
  "switch_threshold": 0.90,
  "min_improvement": 0.10,
  "cooldown": "30m"
}

Top-level backup_root and state_path override where backups and state are stored. Each service may list explicit files (path + backup_name + optional required: false) to manage any credential layout; services of kind claude/claude-code and codex get sensible defaults:

Claude Code:

  • credentials: ${CLAUDE_CONFIG_DIR:-~/.claude}/.credentials.json
  • config: ${CLAUDE_CONFIG_DIR:-~}/.claude.json, or legacy ${CLAUDE_CONFIG_DIR:-~/.claude}/.config.json when present

Codex:

  • auth: ${CODEX_HOME:-~/.codex}/auth.json

Codex can also store credentials in an OS keyring. subswapper manages file-backed credentials only, so configure Codex with:

cli_auth_credentials_store = "file"

Then log in before capturing.

Usage probes

Claude usage is fetched from Anthropic's OAuth usage endpoint using the stored credentials, refreshing expired tokens automatically. The weekly limit scoped to Anthropic's Fable models is tracked as its own window — the FABLE5 column in status output, JSON key fable_weekly — shown alongside the standard windows and included in autoswitch scoring. Other model-scoped limits are ignored.

Codex usage is read through the local codex app-server JSON-RPC interface. For each captured account, subswapper starts the app-server with a temporary CODEX_HOME containing that account's stored auth.json and maps windows by their reported duration. Current plans may expose only a weekly window; any available provider window is displayed and included in scoring. This requires ChatGPT auth in file storage; API-key mode has no subscription limits to read.

Custom services (or overrides) can set usage_command. The command runs once per captured account with these environment variables:

  • SUBSWAPPER_SERVICE
  • SUBSWAPPER_ACCOUNT
  • SUBSWAPPER_EMAIL
  • SUBSWAPPER_ACCOUNT_DIR
  • SUBSWAPPER_BACKUP_ROOT

It must print JSON with both five_hour and weekly windows (output missing either is rejected):

{
  "five_hour": { "pct": 25, "resets_at": "2026-07-01T23:00:00Z" },
  "weekly": { "pct": 16, "resets_at": "2026-07-05T23:00:00Z" }
}

Windows may alternatively be given as { "used": 25, "limit": 100 }. An optional third window, fable_weekly, is also accepted and counts toward exhaustion and autoswitch scoring.

Importing from claude-swap

To replace an existing cswap install, import its stored Claude accounts directly:

subswapper import-cswap

This reads the claude-swap data directory (default ~/.local/share/claude-swap on Linux), decodes its stored credential backups, copies config snapshots, and imports the usage cache, naming each slot cswap-N. cswap's active slot is adopted as the active account when the live credential files still match it. After import, the cswap binary is no longer needed.

Data & security

Defaults on Linux (macOS and Windows use their native config/data folders):

  • config: ~/.config/subswapper/config.json
  • state: ~/.local/share/subswapper/state.json
  • backups: ~/.local/share/subswapper/accounts/

Linux and macOS are tested in CI; Windows builds are cross-compiled but currently untested — treat Windows support as experimental.

Before subswapper syncs token rotations back into a captured account, it checks Claude's account UUID or Codex's account ID. If the live client was logged into a different account outside subswapper, synchronization and automatic switching stop with an identity mismatch instead of overwriting a credential backup. Capture or reconcile that login explicitly before continuing.

Credential backups and state are written with 0600 permissions under 0700 directories. Individual files are staged and renamed atomically. Multi-file bundle changes use rollback snapshots and a recovery journal so a failed or interrupted operation is restored before the next state operation. Still: treat the backup directory like a password store — it holds working OAuth tokens for every captured account.

Running as a service

To keep the monitor running, a systemd user unit works well:

# ~/.config/systemd/user/subswapper.service
[Unit]
Description=subswapper account monitor

[Service]
ExecStart=%h/go/bin/subswapper monitor
Restart=on-failure

[Install]
WantedBy=default.target
systemctl --user enable --now subswapper

The default five-minute monitor writes startup, switch, failure-transition, and recovery events. Use monitor -verbose only for interactive diagnostics when a complete status table every cycle is useful.

Contributing

Contributions are welcome — see CONTRIBUTING.md for the development workflow. Please report security issues privately (see SECURITY.md).

License

MIT

About

Capture, switch, and monitor Claude Code and Codex subscription accounts — auto-switching to the least-used account before limits hit

Resources

License

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages