| name | cc-setup |
| description | Configure & launch cc (claude-code-split-billing): keep subscription Remote Control working PERSISTENTLY while inference is billed to a gateway, isolated from plain claude. One-shot idempotent setup/repair via a per-process MITM proxy. |
| allowed-tools | Read, Glob, Grep, Bash, Edit, Write |
cc-setup
Governing principle: cc keeps Claude Code's Remote Control (needs claude.ai OAuth)
working persistently AND routes inference to a custom gateway, without touching
plain claude. It runs Claude in normal (non-socket) mode and intercepts its HTTPS via
a local MITM proxy reached through HTTPS_PROXY — set only in cc's env. In non-socket mode
Remote Control authenticates with your refreshable file credentials (like plain claude), so
it stays connected instead of dropping every few hours.
When to use
- Setting up
cc on a new machine, or repairing/verifying an existing install.
- Remote Control keeps dropping under
cc (that means it's still on the retired unix-socket
route; this config replaces it).
Not for: plain Claude Code usage, or non-gateway billing.
Why this design (the RC-persistence fix)
Remote Control is enabled only when the base-URL host is api.anthropic.com (or a unix socket
is set). The old socket route satisfied the check but tied RC's bearer to
CLAUDE_CODE_OAUTH_TOKEN, which Claude Code deletes on its first token refresh → RC dropped
every few hours. The fix: don't use socket mode. Set ANTHROPIC_BASE_URL=https://api.anthropic.com
(literal host passes the check) and intercept the traffic with a per-process MITM proxy via
HTTPS_PROXY + NODE_EXTRA_CA_CERTS (both cc-env-only). Non-socket mode uses refreshable file
creds → RC is persistent. Isolation holds because none of those vars exist for plain claude.
One-shot setup / repair (idempotent)
One command: cc. Configure/verify with cc setup — safe to re-run:
cc setup
(On a brand-new machine where cc isn't on PATH, run the script directly once: Windows
powershell -ExecutionPolicy Bypass -File scripts\setup-cc.ps1, macOS/Linux
bash scripts/setup-cc.sh — it also installs the cc launcher onto PATH.)
cc setup does, idempotently: verify Node ≥ 18; ensure .env has GATEWAY_HOST; mint the
local CA + api.anthropic.com leaf (gen-certs.sh, needs openssl); run setup-config
(inherit mode + remoteControlAtStartup + a statusLine badge); install the cc launcher (+ a git-bash shim);
then smoke-test (cc -p) and confirm inference routed to the gateway (proxy.log → INFER RES 200).
The one thing it can't do for you: fill gateway secrets in .env (GATEWAY_HOST,
GATEWAY_HEADERS, GATEWAY_MODEL_MAP — include a fable entry; Claude Code defaults to Fable).
Manual reference (what the script automates)
- Node ≥ 18 (proxy runtime). No Bun.
- Kit: clone
https://github.com/DaizeDong/claude-code-split-billing if absent.
.env: cp .env.example .env; set gateway host/headers/model-map.
- Certs:
bash scripts/gen-certs.sh → certs/ + ca-bundle.pem (git-ignored).
- Config dir + RC:
scripts/setup-config.ps1 -Inherit (or .sh --inherit).
- PATH:
bin/ on PATH, or a ~/.local/bin/cc.cmd (Win) / cc (unix) shim.
- Launch:
cc (drop-in for claude; RC on by default, now persistent).
Verify
cc -p "hi" returns a reply; proxy.log shows INFER /v1/messages -> <gateway> → INFER RES 200.
- On
claude.ai/code / mobile the session appears, is controllable, and stays connected.
- Plain
claude elsewhere works independently (no proxy, no cert → unaffected).
Gotchas / troubleshooting
- RC still drops → you're still in socket mode; ensure
ANTHROPIC_UNIX_SOCKET is NOT set (cc clears it).
missing ca-bundle.pem → bash scripts/gen-certs.sh (needs openssl / Git Bash).
400 Deployment … not found → add the model (esp. fable) to GATEWAY_MODEL_MAP.
- Version skew 400s (
Unexpected value(s) ... anthropic-beta, Extra inputs are not permitted,
Deployment ... not found) → Claude Code sent a beta/field/model the gateway hasn't adopted, and a
strict gateway 400s the whole request. src/compat.js absorbs it: reads what the gateway named,
checks we actually sent it, drops it, retries, remembers it in .gateway-compat.json, and notifies
(GATEWAY_COMPAT_NOTIFY_CMD). Only client opt-ins are ever dropped; model/messages/max_tokens/
system/tools/gateway auth are untouchable, and a semantic 400 is passed through verbatim.
Visibility (a downgrade must never stay silent): cc compat lists what is currently dropped,
cc prints a warning line at startup while anything is, and the status badge shows ⚠ compat:N.
cc compat reset [value] re-tests once the gateway catches up. Log line: INFER COMPAT.
INFER RES 530 / retries / timeouts → the gateway's Cloudflare tunnel is flaky (infra, not cc);
the proxy retries within GATEWAY_RETRY_MAX_MS. Don't guess how bad it is — cc status shows the
running proxy's health on one screen (request outcomes + fail %, retry totals by code, deepest retry
storm, timeouts/gateway-errors — shown even when zero, so absence is informative — latency p50/p95, and
the active compat drops). The badge also raises a red ⚠ timeout/⚠ failed (or yellow ↻ N) on recent
trouble. Data comes from .gateway-stats.json (src/metrics.js, per-process, resets on proxy restart).
- port squatted (
something else is listening on 127.0.0.1:8788) → another process holds the
proxy port; free it or set PROXY_PORT. cc no longer launches into a foreign listener: the proxy
answers a plain GET with an x-cc-proxy header, and ensure-proxy requires it.
cc not found in git-bash → the setup installs a ~/.local/bin/cc shim; ensure that dir is on PATH.
Files
bin/cc{,.cmd} launchers (cc setup/compat/status subcommands) · src/proxy.js (Node MITM CONNECT
proxy) · src/compat.js (heals client/gateway version skew) · src/metrics.js + src/status-cli.js
(operational health → cc status) · src/ensure-proxy.js (start it detached; verifies the listener is
really cc via its x-cc-proxy header) · scripts/gen-certs.sh (local CA) · scripts/setup-cc.* (this
setup) · scripts/setup-config.* (config dir + RC) · tests/ (npm test) · .env (gateway, gitignored) ·
.gateway-compat.json + .gateway-stats.json (gitignored) · certs/ + ca-bundle.pem (gitignored) ·
legacy/ (retired hosts-hijack + unix-socket routes).