{"id":47916015,"url":"https://github.com/karpeleslab/teamclaude","last_synced_at":"2026-06-27T08:00:51.500Z","repository":{"id":346603553,"uuid":"1190713534","full_name":"KarpelesLab/teamclaude","owner":"KarpelesLab","description":"Multi-account Claude proxy with automatic quota-based rotation","archived":false,"fork":false,"pushed_at":"2026-06-22T02:44:23.000Z","size":745,"stargazers_count":56,"open_issues_count":1,"forks_count":19,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-22T04:09:49.986Z","etag":null,"topics":["anthropic","claude","claude-code","load-balancer","multi-account","nodejs","oauth","proxy"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KarpelesLab.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["MagicalTux"]}},"created_at":"2026-03-24T14:49:08.000Z","updated_at":"2026-06-22T02:44:24.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/KarpelesLab/teamclaude","commit_stats":null,"previous_names":["karpeleslab/teamclaude"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/KarpelesLab/teamclaude","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarpelesLab%2Fteamclaude","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarpelesLab%2Fteamclaude/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarpelesLab%2Fteamclaude/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarpelesLab%2Fteamclaude/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KarpelesLab","download_url":"https://codeload.github.com/KarpelesLab/teamclaude/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarpelesLab%2Fteamclaude/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34845749,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anthropic","claude","claude-code","load-balancer","multi-account","nodejs","oauth","proxy"],"created_at":"2026-04-04T05:37:21.129Z","updated_at":"2026-06-27T08:00:51.494Z","avatar_url":"https://github.com/KarpelesLab.png","language":"JavaScript","funding_links":["https://github.com/sponsors/MagicalTux"],"categories":[],"sub_categories":[],"readme":"# TeamClaude\n\n[![CI](https://github.com/KarpelesLab/teamclaude/actions/workflows/ci.yml/badge.svg)](https://github.com/KarpelesLab/teamclaude/actions/workflows/ci.yml)\n[![npm version](https://img.shields.io/npm/v/@karpeleslab/teamclaude.svg)](https://www.npmjs.com/package/@karpeleslab/teamclaude)\n[![node](https://img.shields.io/node/v/@karpeleslab/teamclaude.svg)](https://nodejs.org)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nMulti-account Claude proxy with automatic quota-based rotation for [Claude Code](https://claude.ai/claude-code).\n\nSits transparently between Claude Code and the Anthropic API, managing multiple Claude Max (or API key) accounts and automatically switching when one approaches its session or weekly quota limit.\n\n![TeamClaude TUI](screenshots/teamclaude.png)\n\n## Features\n\n- **Automatic account rotation** — switches to the next account when session (5h) or weekly (7d) quota reaches the configured threshold (default 98%)\n- **Auto-retry on 429** — waits the `retry-after` duration and retries the same account; switches to the next on persistent errors\n- **Interactive TUI** — real-time dashboard with color-coded quota bars, reset countdowns, activity log, and keyboard controls; a settings screen (`g`) edits the rotation threshold, quota-probe interval, and sx.org proxy live\n- **OAuth token management** — automatically refreshes tokens nearing expiry and persists them to config; client token refreshes pass through untouched\n- **Hot-reload accounts** — add or change accounts while the server is running; press **R** in the TUI, or run headless and CLI changes auto-reload via a local control endpoint\n- **Headless mode** — run the proxy without the TUI (`--headless`) for backgrounding/services\n- **Org-aware accounts** — one email can hold multiple accounts across different organizations (e.g. corp + personal); dedup is keyed on account + org, and names disambiguate as `email (Org)`\n- **Rotation priority** — pin a preferred account order with `teamclaude priority`\n- **Enable/disable accounts** — temporarily pause an account without removing it (`teamclaude disable`/`enable`, or `d` in the TUI); re-enabling also clears a stuck error state\n- **Quota persistence** — observed quota survives restarts (saved to a sibling state file), so rotation state isn't lost on restart; stale windows are discarded automatically\n- **Optional quota probe** — off by default; when enabled, periodically refreshes idle accounts' quota from the usage endpoint (no message spend), and surfaces the Sonnet weekly bucket\n- **MITM proxy mode (default)** — `teamclaude run` routes claude via an HTTPS forward proxy with a local CA so even hardcoded `api.anthropic.com` endpoints (e.g. the Claude Design MCP) get the real token injected; pass `--no-mitm` for base-URL routing only\n- **Optional sx.org proxy mode** — off by default; set an [sx.org](https://sx.org) API key in the TUI settings screen (`g`) and TeamClaude auto-provisions a residential proxy to change the egress IP and work around IP-based `429`s. Three modes (`m` to cycle): **always** (route all upstream traffic), **on 429 only** (stay direct, fail over to the proxy after a 429), or **off** (keep the key but don't use it). TLS stays end-to-end with Anthropic (the proxy only relays ciphertext)\n- **Request logging** — optional full request/response logging for debugging\n- **Zero dependencies** — uses only Node.js built-in modules\n\n## Quick Start\n\nRequires Node.js 18+.\n\n```bash\n# Install\nnpm install -g @karpeleslab/teamclaude\n\n# Add your first account (opens browser for OAuth)\nteamclaude login\n\n# Add a second account\nteamclaude login\n\n# Start the proxy\nteamclaude server\n\n# In another terminal, run Claude Code through the proxy\nteamclaude run\n```\n\nYou can also import existing Claude Code credentials instead of logging in:\n\n```bash\nclaude /login           # Log into an account in Claude Code\nteamclaude import       # Import its credentials\n```\n\n## Adding Accounts\n\n### OAuth Login (recommended)\n\nThe easiest way to add accounts — opens your browser for authentication:\n\n```bash\nteamclaude login\n```\n\nUses the same OAuth flow as Claude Code. Auto-detects the account email and subscription tier. Logging in with the same account again updates its credentials.\n\nYou can add accounts while the server is running — press **R** in the TUI to reload.\n\n### Import from Claude Code\n\nIf you already have Claude Code set up, you can import its credentials directly:\n\n```bash\nclaude /login           # Log into an account in Claude Code\nteamclaude import       # Import its credentials\n```\n\nRe-importing the same account updates its credentials. You can also import from a custom path:\n\n```bash\nteamclaude import --from /path/to/credentials.json\n```\n\n### API Key\n\nFor Anthropic API key accounts (billed via Console):\n\n```bash\nteamclaude login --api\n```\n\n## Usage\n\n### Start the proxy server\n\n```bash\nteamclaude server\n```\n\nWhen running from a TTY, shows an interactive TUI with:\n- Account table with session/weekly quota progress bars and reset countdowns\n- Real-time activity log with request tracking\n- Keyboard shortcuts (see below)\n\nFalls back to plain log output when not a TTY (e.g. running as a service). Pass `--headless` (or `--no-tui`) to force the plain-log mode even from a terminal — useful for backgrounding the proxy.\n\nWhen running headless, you can re-sync accounts from the config without a restart by POSTing to the local control endpoint (the equivalent of pressing **R** in the TUI):\n\n```bash\ncurl -X POST http://localhost:3456/teamclaude/reload\n```\n\nYou usually don't need to call it directly: `teamclaude login`, `import`, `enable`, `disable`, and `priority` automatically notify a running server to reload. (New accounts and credential/priority/enable-disable changes are picked up live; account *removals* still require a restart.)\n\n#### TUI Keyboard Shortcuts\n\n| Key | Action |\n|-----|--------|\n| `s` | Switch active account |\n| `a` | Add account (import or API key) |\n| `r` | Remove an account |\n| `d` | Enable/disable an account |\n| `R` | Reload accounts from config |\n| `q` | Quit |\n\nIn selection mode, use `j`/`k` or arrow keys to navigate, `Enter` to confirm, `Esc` to cancel.\n\n### Run Claude Code through the proxy\n\n```bash\nteamclaude run\n```\n\n`run` probes the proxy first: if it's up, Claude Code is routed through it; if it's **not** running, `claude` is launched directly so nothing breaks.\n\nSince **1.1.0**, `run` defaults to [MITM forward-proxy mode](#mitm-proxy-mode-default) so even hardcoded `api.anthropic.com` endpoints (e.g. the Claude Design MCP) are intercepted. To keep the previous base-URL-only behavior, pass `--no-mitm`:\n\n```bash\nteamclaude run --no-mitm\n```\n\nOr manually set the environment:\n\n```bash\neval $(teamclaude env)\nclaude\n```\n\n### Routing plain `claude` automatically (alias)\n\nSo you don't have to type `teamclaude run` every time, add a shell alias that makes plain `claude` go through the proxy (and fall back to direct when it's down):\n\n```bash\nteamclaude alias              # print the alias for your shell\nteamclaude alias --install    # or write it to your shell rc (--uninstall to remove)\n```\n\nThis is an interactive-shell alias — it affects `claude` typed at a prompt, not `claude` spawned by editors or scripts. It's a thin passthrough to `teamclaude run`, which holds the proxy-up/down logic.\n\n### Other commands\n\n```bash\nteamclaude accounts          # List accounts with subscription tier and token status\nteamclaude accounts -v       # Also show token expiry times\nteamclaude status            # Show live proxy status (requires running server)\nteamclaude remove \u003cname\u003e     # Remove an account (by name or email)\nteamclaude disable \u003cname\u003e    # Temporarily exclude an account from rotation\nteamclaude enable \u003cname\u003e     # Re-enable it (also clears a stuck error state)\nteamclaude priority \u003cname\u003e 1 # Set rotation priority (lower = preferred)\nteamclaude probe 300         # Enable background quota refresh (off by default)\nteamclaude alias             # Print/install a `claude` alias that routes via the proxy\nteamclaude api \u003cpath\u003e        # Call an API endpoint with account credentials\nteamclaude help              # Show all commands\n```\n\nWhen the same email belongs to multiple organizations, accounts are named\n`email (Org)` to keep them distinct. Pass `--org \u003cname|uuid\u003e` to disambiguate a\nbare email, e.g. `teamclaude remove user@example.com --org Acme`. Use\n`teamclaude priority \u003cname\u003e --first` / `--last` to move an account to the front\nor back of the rotation order.\n\n### Request logging\n\nLog full request/response details to a directory (one file per request):\n\n```bash\nteamclaude server --log-to /tmp/requests\n```\n\n## Configuration\n\nConfig is stored at `~/.config/teamclaude.json` (or `$XDG_CONFIG_HOME/teamclaude.json`). A random proxy API key is generated on first use.\n\nVolatile runtime state (observed quota) is written separately to `teamclaude.state.json` alongside the config, so the config file stays clean and hand-editable. The state file is safe to delete — quota is simply re-learned from traffic.\n\nOverride the config path with `TEAMCLAUDE_CONFIG`:\n\n```bash\nTEAMCLAUDE_CONFIG=./my-config.json teamclaude server\n```\n\n### Config format\n\n```json\n{\n  \"proxy\": {\n    \"port\": 3456,\n    \"apiKey\": \"tc-auto-generated-key\"\n  },\n  \"upstream\": \"https://api.anthropic.com\",\n  \"switchThreshold\": 0.98,\n  \"sx\": { \"apiKey\": \"your-sx-org-api-key\", \"mode\": \"always\" },\n  \"accounts\": [\n    {\n      \"name\": \"user@example.com (Acme)\",\n      \"type\": \"oauth\",\n      \"accountUuid\": \"...\",\n      \"orgUuid\": \"...\",\n      \"orgName\": \"Acme\",\n      \"priority\": 0,\n      \"accessToken\": \"sk-ant-oat01-...\",\n      \"refreshToken\": \"sk-ant-ort01-...\",\n      \"expiresAt\": 1774384968427\n    }\n  ]\n}\n```\n\n| Field | Description |\n|-------|-------------|\n| `proxy.port` | Local port the proxy listens on |\n| `proxy.apiKey` | API key clients use to authenticate with the proxy |\n| `upstream` | Upstream API base URL |\n| `switchThreshold` | Quota utilization (0–1) at which to switch accounts (TUI: `g` → `t`) |\n| `quotaProbeSeconds` | Background quota-probe interval in seconds (`0` = off, the default; CLI `probe` or TUI `g` → `p`) |\n| `sx.apiKey` | [sx.org](https://sx.org) API key. When set, TeamClaude auto-provisions a residential proxy (egress-IP 429 workaround). Absent/empty = off |\n| `sx.mode` | `always` (route all upstream traffic), `429` (direct, fail over to the proxy after a 429), or `off` (keep the key but don't use it). Defaults to `always` when a key is set |\n| `accounts[].accountUuid` | Anthropic account (person) id; set automatically from the OAuth profile |\n| `accounts[].orgUuid` / `orgName` | Organization the account is scoped to — lets one email hold multiple org accounts |\n| `accounts[].priority` | Rotation preference, lower = preferred (default 0) |\n| `accounts[].disabled` | If `true`, the account is excluded from rotation until re-enabled |\n\n### Quota probe (optional, off by default)\n\nBy default TeamClaude is **passive** — it learns each account's quota only from the responses that flow through it, so an account that hasn't been used yet shows unknown quota until it's first rotated to.\n\nIf you'd rather keep idle accounts' quota fresh, enable the background probe:\n\n```bash\nteamclaude probe 300    # refresh every 300s\nteamclaude probe off    # back to passive (default)\nteamclaude probe        # show current setting\n```\n\nYou can also set the interval live from the TUI settings screen (`g` → `p`), alongside the rotation threshold (`t`).\n\nIt reads each OAuth account's utilization from Anthropic's usage endpoint (`/api/oauth/usage`), which reports quota **without consuming any message quota**. Minimum interval is 30s. Changing it takes effect on a running server immediately (no restart). When enabled, it also surfaces the **Sonnet 7-day** bucket as an extra bar in the TUI / `status` (when your plan exposes it).\n\n### MITM proxy mode (default)\n\nThe plain reverse-proxy only intercepts what `ANTHROPIC_BASE_URL` covers. Some Claude Code features (e.g. the **Claude Design MCP**) use a **hardcoded** `https://api.anthropic.com` URL that ignores that variable, so they bypass the proxy. MITM proxy mode captures those too, which is why it's the default for `teamclaude run` (and the shell alias):\n\n```bash\nteamclaude run -- \u003cclaude args...\u003e\n```\n\nTo opt out and route via `ANTHROPIC_BASE_URL` only, pass `--no-mitm`:\n\n```bash\nteamclaude run --no-mitm -- \u003cclaude args...\u003e\n```\n\nThat launches claude pointed at teamclaude as an **HTTPS forward proxy** (`HTTPS_PROXY`) and trusts a locally-generated CA (`NODE_EXTRA_CA_CERTS`). For an intercepted host, teamclaude **dials the real upstream first, mirrors its negotiated ALPN** (HTTP/2 or HTTP/1.1), then terminates TLS toward claude with the same protocol and relays the traffic **as transparently as possible** — rewriting only what it must:\n\n- the **`authorization`** header → the active account's real token (dropping any client `x-api-key`);\n- the **`account_uuid`** inside `metadata.user_id` → the active account's UUID (so the body agrees with the injected token);\n- and it reads `anthropic-ratelimit-*` from responses for quota.\n\nEverything else is copied byte-for-byte (HTTP/2 is handled with a built-in HPACK codec so the only header changed is the auth one). Any host other than the upstream is blind-tunnelled. The server accepts *both* base-URL and proxy clients at once, so instances launched with and without `--no-mitm` can share one server.\n\nTrust model:\n- The CA is generated locally, stored in the config dir, and trusted **only** by the claude process you launch via `teamclaude run` (through `NODE_EXTRA_CA_CERTS`) — it is **never** added to your system trust store. The leaf private key is `0600`; the CA private key is never written to disk.\n- teamclaude still verifies the **real** Anthropic certificate on the upstream leg.\n\nVerify the proxy + CA without any credentials — the proxy always answers a built-in test host:\n\n```bash\n# (with the server running and certs generated, e.g. after one `teamclaude run`)\ncurl --proxy http://localhost:3456 --cacert ~/.config/teamclaude-ca.pem https://www.example.org/\n# → {\"teamclaude\":\"mitm-proxy-ok\",\"host\":\"www.example.org\",...}\n```\n\n### sx.org proxy mode (optional, off by default)\n\nSome transient `429`s key on the proxy's **outbound IP**, not the account — so rotating accounts doesn't help. To work around them, TeamClaude can route upstream requests through a residential proxy from [sx.org](https://sx.org), giving a different egress IP.\n\nOpen the TUI and press **`g`** for the settings screen, then **`k`** to paste your sx.org API key (stored in `config.sx.apiKey`). TeamClaude reuses an existing active proxy port on your sx.org account, or auto-creates a residential US one, and dials the upstream through it via HTTP `CONNECT` on **both** the reverse-proxy and `--mitm` paths.\n\nPress **`m`** to cycle the **mode**:\n\n| Mode | Behavior |\n|------|----------|\n| **always** | Tunnel **every** upstream request through sx.org. |\n| **on 429 only** | Connect directly; on a `429` (which is IP-based), immediately retry that request through sx.org's fresh egress IP — no wait. On the `--mitm` path, a recent `429` routes new tunnels through sx.org for a short window. |\n| **off** | Never use sx.org, but **keep the API key** so you can re-enable it instantly. |\n\nTLS is established **end-to-end with `api.anthropic.com` over the tunnel**, so the sx.org proxy only ever relays ciphertext and the real Anthropic certificate is still verified. Mode and key changes apply live (no restart). Press **`x`** to forget the key entirely.\n\n\u003e **Cost:** in **always** mode *all* Claude traffic flows through the residential proxy, which sx.org meters by bandwidth — expect real per-GB cost. **on 429 only** uses the proxy just when you're actually being throttled, so it's the cheaper way to ride out rate limits.\n\n## How It Works\n\n1. Claude Code connects to the local proxy instead of `api.anthropic.com`\n2. The proxy selects the active account and forwards requests with that account's credentials\n3. OAuth tokens expiring within 5 minutes are automatically refreshed and persisted to config\n4. Rate limit headers from the API (`anthropic-ratelimit-unified-*`) track session (5h) and weekly (7d) quota utilization\n5. When usage reaches the threshold, the proxy switches to the next available account via round-robin\n6. On 429 responses, the proxy waits the `retry-after` duration and retries; on persistent errors, it switches accounts\n7. Transient network errors (connection reset, timeout) drop the connection so the client can retry\n8. If all accounts are exhausted, returns 429 with the soonest reset time\n9. Client token refresh requests (`/v1/oauth/token`) are relayed to upstream untouched — the proxy and client manage their own token lifecycles independently\n\n## Security\n\nThe only canonical sources for TeamClaude are this repository\n(https://github.com/KarpelesLab/teamclaude) and the\n[`@karpeleslab/teamclaude`](https://www.npmjs.com/package/@karpeleslab/teamclaude)\nnpm package. TeamClaude is **never** distributed as a downloadable binary\narchive — be wary of soft-forks that bundle a `.zip` and tell you to extract and\nrun it. See [SECURITY.md](SECURITY.md) for details and how to report issues.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarpeleslab%2Fteamclaude","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarpeleslab%2Fteamclaude","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarpeleslab%2Fteamclaude/lists"}