{"slug": "show-hn-predictive-load-balancing-for-claude-accounts", "title": "Show HN: Predictive load balancing for Claude accounts", "summary": "Developer Yasyf released cc-pool, a macOS tool that predictively load-balances Claude Code sessions across multiple Claude Max/Pro subscriptions by selecting the account with the most remaining usage headroom before each session starts. The tool installs via Homebrew, wraps the `claude` command, and stores secrets in the macOS Keychain, ensuring no proxy or manual switching is needed.", "body_md": "**Predictive, start-of-session load-balancing across multiple Claude subscriptions — for macOS.**\n\nRun several Claude Max/Pro subscriptions and you hit the same wall every week:\none account pegged at its 5-hour or weekly limit while another sits idle.\ncc-pool launches every Claude Code session on the **emptiest** account, picked\nfrom live 5-hour / 7-day usage *before* the session starts — predictive, not\nreactive. No proxy in the request path, no manual switching, no waiting for a\nrate-limit error to learn you picked wrong.\n\nAfter setup, `claude`\n\nis an alias for `ccp run`\n\nand the pooling disappears\ninto the background. Two hard guarantees underwrite the design: plain `claude`\n\non `~/.claude`\n\nkeeps working untouched — the pool can never log it out — and\nsecrets stay in the macOS Keychain, never in cc-pool's database. [How it\nworks](#how-it-works) has the details.\n\n```\nbrew tap yasyf/cc-pool https://github.com/yasyf/cc-pool\nbrew install yasyf/cc-pool/cc-pool\n```\n\nmacOS only. The binary installs as `cc-pool`\n\nwith a `ccp`\n\nsymlink; the default\nbuild is pure Go.\n\nPool two subscriptions and launch Claude on the emptiest one, in about five minutes.\n\n**1. Run ccp.** On an empty pool it walks you through logging in each\nsubscription — every account gets its own\n\n`claude /login`\n\n:\n\n``` bash\n$ ccp\n✓ Set up cc-pool on this machine.\n\nHow do you want to log in?\n> Log in now, in this terminal\n\n# claude opens its /login flow right here; finish it and ccp closes claude for you\n\nName for this account (optional)\n> work@example.com\n\n✓ Added work@example.com.\n\nAdd another account?\n> Yes\n\n# the second login runs the same way\n✓ Added personal@example.com.\n\nYour pool now has 2 accounts.\n\nLaunch Claude on the emptiest account:\n\n    ccp run\n\nWrap `claude` to always launch on the emptiest account?\n> Yes\n\n✓ Wrapped `claude` — added an alias to ~/.zshrc.\nRestart your shell or run `source ~/.zshrc` to use it now.\nRun `command claude` for plain ~/.claude.\n```\n\n**2. Check the pool.** `ccp status --plain`\n\nprints the table once; bare\n`ccp status`\n\n(or bare `ccp`\n\n, now that accounts exist) opens a live TUI with\nper-account score breakdowns:\n\n``` bash\n$ ccp status --plain\n  ACCOUNT                     SCORE  5h used  7d used  LIVE RESETS\n▸ work@example.com             68.1      22%      46%     0 6:00 PM\n  personal@example.com         34.8      61%      70%     0 4:30 PM\n▸ = next pick · score higher = emptier · 5h/7d = % used\nupdated 3:58 PM\n```\n\n**3. Launch.** Run `claude`\n\n(now wrapped). It announces its pick on stderr,\nthen execs the real `claude`\n\n— cc-pool is gone from the process tree before\nClaude Code draws its first frame:\n\n``` bash\n$ claude\nSelected work@example.com · 5h 22% used · 7d 46% used\n```\n\nThe `Selected`\n\nline names the account marked `▸`\n\nin step 2 — that match is\nyour verification. From here on, every `claude`\n\nlands on whichever account\nhas the most headroom.\n\nThe quickstart wraps `claude`\n\nwith `alias claude='ccp run'`\n\n. `command claude`\n\nbypasses it — plain claude on `~/.claude`\n\n, one keystroke away. Prefer to\nleave `claude`\n\nuntouched? Decline the prompt (or pass `ccp add --no-alias`\n\n)\nand pick your own name:\n\n```\nalias cl='ccp run'\n```\n\n`ccp run`\n\nforwards every argument to `claude`\n\nverbatim — no `--`\n\nseparator\nneeded. Bare `ccp`\n\nwith flags is shorthand for the same thing:\n\n```\nccp run --resume\nccp -p \"summarize this repo\"   # auto-converts to `ccp run -p ...`\n```\n\n`CCP_ACCOUNT=2 ccp run`\n\nforces account 2 instead of auto-selecting. Repeated\nlaunches from the same directory stick to one account for prompt-cache\ncontinuity; those announce `Reusing work@example.com (pinned)`\n\ninstead of\n`Selected`\n\n.\n\nSelection refuses to burn a nearly-reset window: an account with an exhausted\nplan window is never picked while any account has headroom, and\n`ccp select --wait`\n\nblocks until one does. If the whole pool is exhausted, the\nlaunch falls back to the least-bad account and warns loudly on stderr —\nthat session bills pay-as-you-go credits if extra usage is enabled, or\nrate-limits until the window resets.\n\n`ccp select`\n\nprints the chosen config dir on stdout and nothing else. Set the\nplugin root too, so the session writes canonical plugin paths into the shared\n`~/.claude/plugins`\n\n(`ccp run`\n\nand `ccp env`\n\nboth do this for you):\n\n```\nCLAUDE_CODE_PLUGIN_CACHE_DIR=\"$HOME/.claude/plugins\" CLAUDE_CONFIG_DIR=$(ccp select) claude\n```\n\n`~/.claude`\n\nis **never moved** and never registered as a pool account. It\nstays the canonical config dir, so plain `claude`\n\nkeeps working exactly as\nbefore, and is the **shared base** every pooled account mirrors. The\npool never touches plain claude's credential or login identity. Every account,\nincluding your main subscription, joins with its own `claude /login`\n\n, so its\ntoken chain is fully independent of plain claude's.\n\nClaude Code namespaces its Keychain credential **per config dir**: the default\n`~/.claude`\n\nuses the item `Claude Code-credentials`\n\n; a custom\n`CLAUDE_CONFIG_DIR`\n\ngets a suffixed item `Claude Code-credentials-<hash>`\n\n.\ncc-pool gives each account a real, unique dir (`~/.cc-pool/accounts/acct-NN`\n\n)\nso each gets its own Keychain item, its own independent OAuth grant (its own\nrefresh-token chain), and runs on its own **subscription** — never API\nbilling. Each account dir is seeded with a copy of your `~/.claude.json`\n\nwith\nthe identity stripped (the account's own login writes its identity), so pooled\nsessions inherit your settings, MCP servers, and per-project tool approvals\ninstead of running first-run onboarding.\n\nEach account dir presents **all of ~/.claude** —\n\n`projects/`\n\n, `skills/`\n\n,\n`plans/`\n\n, `settings.json`\n\n, `history.jsonl`\n\n, the lot — with writes passing\nstraight back, so every session shares the same workspace and plan-mode plans\npersist across pooled sessions. Two providers:**symlink**(default, zero-dependency): symlinks each top-level entry of`~/.claude`\n\ninto the account dir. New top-level entries are picked up automatically at launch, by the daemon, and by`ccp doctor --fix`\n\n.**fuse**(optional, live mirror): a passthrough mirror mounted via[fuse-t](https://github.com/macos-fuse-t/fuse-t)— kext-less, mounted as you, no root — and hosted by a detached cc-pool**mount-holder** process, so daemon restarts and upgrades never disturb live sessions' mounts. Requires a`-tags fuse`\n\nbuild (cgo) and a one-time*Network Volumes*privacy grant.\n\nA few entries stay per-account instead of shared: `daemon/`\n\nand `ide/`\n\n(Claude's PID-keyed supervisor and IDE lock/socket files, which would collide\nacross concurrent sessions), `backups/`\n\n(rotating backups of each account's\n`.claude.json`\n\n), the identity and credential files `.claude.json`\n\nand\n`.credentials.json`\n\n, `.last-update-result.json`\n\n(instance-local auto-update\nstate), and `remote-settings.json`\n\n(claude's cached per-subscription\nsettings).\n\nPer-account `.claude.json`\n\ndoesn't mean settings fork, though: its shareable\ntop-level keys — everything except identity, per-project state, and startup\ncounters — flow from `~/.claude.json`\n\ninto pooled sessions, so a setting you\nchange in vanilla `claude`\n\nreaches every account. One caveat: under the\ndefault symlink overlay the flow is one-way (merged in at launch, base wins),\nso changing a shareable setting *inside* a pooled session reverts at the next\nlaunch — manage shared settings in vanilla `claude`\n\n, or use the fuse overlay,\nwhose live merged view writes shareable changes back to `~/.claude.json`\n\n(two-way).\n\nThe baseline — exact when windows are far from a reset — is:\n\n```\nscore = 0.70·(100−util_5h) + 0.25·(100−util_7d)\n      − 2·active_sessions − 100·rate_limited − 20·stale_or_refresh_failed\n```\n\nThree terms keep the ranking honest near the edges: an **imminent reset**\nearns credit in proportion to how soon the window resets (a 90%-used window\nresetting in 10 minutes ranks *up*, not down); a **low-headroom barrier**\nstops a nearly-exhausted 7-day window from being masked by 5-hour headroom;\nand a **burn-rate** term downranks an account being actively drained.\n`select`\n\npicks argmax. Usage comes from Claude's own `/api/oauth/usage`\n\nendpoint.\n\n`brew services start cc-pool`\n\n(Homebrew installs) or `ccp service install`\n\n(source builds) runs a **user LaunchAgent** — a root daemon couldn't read your\nlogin Keychain. It polls usage every ~3 min with exponential backoff,\nrefreshes **idle** accounts' tokens before they expire (a checked-out session\nowns its own refresh; the daemon adopts whatever token it rotated to on\ncheck-in), caches scores, and — with the fuse overlay — supervises the\ndetached mount holder, which owns the mounts (so daemon restarts and upgrades\nnever disturb them). `ccp add`\n\nand `ccp init`\n\nstart it automatically; if it\nisn't running, `ccp select`\n\nauto-spawns it or samples live.\n\nNo secrets are ever stored in cc-pool's database — the macOS Keychain is the only secret store.\n\nThese two tables cover the full user-facing surface; `ccp help <command>`\n\nprints the same per command.\n\n| Command | What it does |\n|---|---|\n`ccp` |\nOn a terminal — empty pool: guided onboarding; populated pool: status. With flags: shorthand for `ccp run` |\n`ccp add` |\nPool a subscription via its own `claude /login` (auto-inits the pool, starts the daemon) |\n`ccp run [claude args…]` |\nSelect the emptiest account and exec `claude` , forwarding every arg |\n`ccp status` |\nPer-account usage, score, and sessions — TUI on a terminal, plain table when piped |\n`ccp select` |\nPrint the chosen account's config dir on stdout — the composable hot path |\n`ccp env` |\nPrint shell `export` lines to launch an account by hand |\n`ccp list` |\nStatic account list: ids, paths, Keychain items |\n`ccp doctor` |\nCheck accounts' Keychain items and overlays; repair drift |\n`ccp remove <id>` |\nRemove an account from the pool |\n`ccp rename <id> <name>` |\nRename an account; `--auto` derives names from account emails |\n`ccp init` |\nSet up the pool and start the daemon (optional — `ccp add` does this) |\n`ccp service install|uninstall|status` |\nManage the daemon and mount holder (delegates to `brew services` on Homebrew installs) |\n`ccp widget` |\nInstall the Notification Center status widget (Homebrew cask) and show how to enable it |\n\nFlags, by command:\n\n| Command | Flag | Effect |\n|---|---|---|\n`add` |\n`--label <name>` |\nName for the first account |\n`add` |\n`--count <n>` |\nAdd exactly N accounts, no continue prompt |\n`add` |\n`--yes` , `-y` |\nAdd one account and log in right away |\n`add` |\n`--run-login` |\nLog in immediately instead of asking how |\n`add` |\n`--no-alias` |\nDon't add a `claude` shell alias |\n`run` |\n`CCP_ACCOUNT=<id>` (env) |\nForce a specific account instead of auto-selecting |\n`select` |\n`--wait` |\nWait for an account with headroom instead of failing or using an exhausted one |\n`select` |\n`--account <id>` |\nForce a specific account id |\n`select` |\n`--no-daemon` |\nDon't use the daemon; sample usage live |\n`select` |\n`--fresh <dur>` |\nReuse cached usage newer than this (live mode) |\n`status` |\n`--plain` |\nPrint the plain table instead of the interactive TUI |\n`status` |\n`--watch` , `-w` |\nRefresh continuously (plain mode) |\n`status` |\n`--live` |\nForce live sampling even if the daemon is running |\n`env` |\n`--account <id>` |\nAccount id (defaults to the best account) |\n`doctor` |\n`--fix` |\nAttempt to repair detected drift |\n`remove` |\n`--keep-credential` |\nKeep the account's Keychain item |\n`rename` |\n`--auto` |\nDerive labels from account emails (skips custom labels) |\n`rename` |\n`--force` |\nWith `--auto` , overwrite custom labels too |\n`init` |\n`--no-service` |\nDon't start the daemon now; `ccp add` starts it |\n`service uninstall` |\n`--force` |\nSkip the live-session gate (sessions on unmounted dirs will break) |\n`service uninstall` |\n`--purge` |\nAlso remove all pool accounts and state; never touches `~/.claude` |\n\n```\nccp service uninstall            # stop the daemon + mount holder, unmount fuse overlays\n                                 # (refuses under live sessions; --force overrides)\nccp service uninstall --purge    # ...and remove all pool accounts/dirs/state\nbrew uninstall cc-pool\n```\n\n`~/.claude`\n\nand its credential are never touched.\n\nBuild with `CGO_ENABLED=0 go build ./cmd/cc-pool`\n\n; `go test ./...`\n\npasses with\nno network, Keychain, or daemon. The manual end-to-end test matrix lives in\n[docs/VERIFICATION.md](/yasyf/cc-pool/blob/main/docs/VERIFICATION.md), release history in\n[CHANGELOG.md](/yasyf/cc-pool/blob/main/CHANGELOG.md), and conventions in [AGENTS.md](/yasyf/cc-pool/blob/main/AGENTS.md).\n\nPolyForm-Noncommercial-1.0.0 © Yasyf Mohamedali — free for noncommercial use.\nSee [LICENSE](/yasyf/cc-pool/blob/main/LICENSE) or the [license text\nonline](https://polyformproject.org/licenses/noncommercial/1.0.0).", "url": "https://wpnews.pro/news/show-hn-predictive-load-balancing-for-claude-accounts", "canonical_source": "https://github.com/yasyf/cc-pool", "published_at": "2026-06-13 10:37:34+00:00", "updated_at": "2026-06-13 10:49:35.395474+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools", "large-language-models"], "entities": ["Yasyf", "Claude", "cc-pool", "macOS", "Homebrew", "Claude Code"], "alternates": {"html": "https://wpnews.pro/news/show-hn-predictive-load-balancing-for-claude-accounts", "markdown": "https://wpnews.pro/news/show-hn-predictive-load-balancing-for-claude-accounts.md", "text": "https://wpnews.pro/news/show-hn-predictive-load-balancing-for-claude-accounts.txt", "jsonld": "https://wpnews.pro/news/show-hn-predictive-load-balancing-for-claude-accounts.jsonld"}}