https://github.com/in-jun/cc-relay-proxy
Claude Code account rotation proxy
https://github.com/in-jun/cc-relay-proxy
Last synced: 2 months ago
JSON representation
Claude Code account rotation proxy
- Host: GitHub
- URL: https://github.com/in-jun/cc-relay-proxy
- Owner: in-jun
- Created: 2026-03-21T18:03:25.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-03-22T16:10:02.000Z (3 months ago)
- Last Synced: 2026-03-22T19:43:21.099Z (3 months ago)
- Language: Go
- Size: 171 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# cc-relay-proxy
A local proxy that sits between Claude Code and the Anthropic API, transparently rotating between multiple accounts to avoid rate limits. Claude Code never sees a 429 — the proxy absorbs it and retries with a different account.
```
Claude Code → cc-relay-proxy :9999 → api.anthropic.com
├─ Route to best available account
├─ On 429 → switch account and retry
└─ GET /status
```
## Installation
### Docker (recommended)
```bash
mkdir -p config logs
# put your accounts.json inside config/
docker run -d \
-p 9999:9999 \
-v $(pwd)/config:/app/config \
-v $(pwd)/logs:/app/logs \
--name cc-relay-proxy \
injundev/cc-relay-proxy
```
### Build from source
```bash
git clone https://github.com/in-jun/cc-relay-proxy
cd cc-relay-proxy
go build -o cc-relay-proxy ./cmd/cc-relay-proxy
```
Requires Go 1.21+. No external dependencies.
## Setup
### 1. Create config/accounts.json
```bash
mkdir -p config
```
```json
[
{"name": "acct1", "refreshToken": "rt_..."},
{"name": "acct2", "refreshToken": "rt_..."}
]
```
Get your refresh token from the local Claude Code credentials:
```bash
cat ~/.claude/.credentials.json | python3 -c "
import json, sys
d = json.load(sys.stdin)
print(d['claudeAiOauth']['refreshToken'])
"
```
### 2. Connect Claude Code
Add to `~/.claude/settings.json`:
```json
{
"env": {
"ANTHROPIC_BASE_URL": "http://localhost:9999"
}
}
```
Or use an alias to keep the original `claude` command available:
```bash
alias claudep='ANTHROPIC_BASE_URL=http://localhost:9999 claude'
```
## Configuration
| Variable | Default | Description |
|---|---|---|
| `CC_ACCOUNTS_FILE` | `config/accounts.json` | Path to accounts file |
| `CC_PROXY_PORT` | `9999` | Port to listen on |
| `CC_PROXY_BIND` | `127.0.0.1` | Bind address (`0.0.0.0` to expose on LAN) |
| `CC_LOG_PATH` | `logs/proxy.log` | JSONL log file |
| `CC_LOG_LEVEL` | — | Set to `debug` to log auth headers |
## How account selection works
Each account gets a water score (lower = more available):
```
water = max(5h_util × time_left / 300min,
7d_util × time_left / 168hr)
```
Time-decay is the key insight: an account at 90% 5h utilization that resets in 10 minutes scores near 0 and floats to the top of the pool — the proxy will start using it the moment quota resets. An account is only hard-blocked when the API explicitly returns `rejected`; high utilization alone just raises the score.
With multiple accounts, the proxy switches proactively if a better option scores at least 10% lower than the current one.
## Status endpoint
```bash
curl -s http://localhost:9999/status | python3 -m json.tool
```
```json
{
"active": "acct1",
"uptime": "2h34m",
"accounts": [
{
"name": "acct1",
"isActive": true,
"status": "allowed",
"water": 0.133,
"fiveHour": { "utilization": 0.0, "percent": "0%", "resetsInMins": 254 },
"sevenDay": { "utilization": 0.67, "percent": "67%", "resetsInHours": 33 },
"tokenExpiresIn": "7h40m",
"lastSeen": "just now"
}
]
}
```
## Logs
Every significant event is written as a JSONL line to `logs/proxy.log`:
```
request — account, latency, water score per request
account_switched — switch reason and water scores before/after
429_received — action taken: switch / hold / forward
rate_limit_update — utilization parsed from API response headers
token_refreshed — OAuth token renewal
```