Claude Proxy
NeuroLink includes a Claude-API-compatible proxy server that sits between Claude Code and Anthropic. It pools multiple Claude accounts, handles rate-limit failover automatically, refreshes OAuth tokens on demand before they expire, and falls back to other providers when all Claude accounts are exhausted.
Overview
Why use the proxy?
Claude Code supports only one Anthropic account at a time. If you hit a rate limit, you wait. If your token expires mid-session, you re-authenticate manually. The NeuroLink proxy solves these problems:
- Multi-account pooling -- Combine multiple Claude Pro/Max subscriptions for higher aggregate throughput.
- Automatic token refresh -- OAuth tokens are refreshed before they expire (pre-request check + 401 retry).
- Rate-limit failover -- When one account hits a 429, the proxy immediately tries the next account with exponential backoff.
- Multi-provider fallback -- When all Claude accounts are exhausted, requests are routed to alternative providers (Gemini, OpenAI, etc.) through NeuroLink's provider layer.
- Transparent to Claude Code -- Set
ANTHROPIC_BASE_URLand Claude Code works normally. The proxy auto-configures this on start.
How it works at a glance
Claude Code
|
| POST /v1/messages
v
NeuroLink Proxy (localhost:55669)
|
|-- Passthrough mode (Claude -> Claude): raw body forwarding
|-- Translation mode (Claude -> Other): through neurolink.generate()/stream()
v
Anthropic API / Google AI / OpenAI / ...
Quick Start
If you do not already have the CLI installed, install it first:
pnpm add -g @juspay/neurolink
# or
npm install -g @juspay/neurolink
Then continue with the proxy setup steps below.
One-command setup
neurolink proxy setup
This command:
- Checks for existing authenticated accounts
- Runs OAuth login if no valid accounts exist
- Installs the proxy as a launchd service (macOS) that auto-restarts on crash or reboot
- Auto-configures Claude Code to use the proxy
Use --no-service to skip service installation and start the proxy in the foreground instead:
neurolink proxy setup --no-service
Manual setup
# Step 1: Authenticate with Anthropic via OAuth
neurolink auth login anthropic --method oauth
# Step 2: (Optional) Add more accounts for pooling
neurolink auth login anthropic --method oauth --add --label work
neurolink auth login anthropic --method oauth --add --label personal
# Step 3: (Optional) Start the local OpenObserve stack and import the dashboard
# (auto-writes OTEL_EXPORTER_OTLP_ENDPOINT to ~/.neurolink/.env)
neurolink proxy telemetry setup
# Step 4: Start the proxy
neurolink proxy start
# Step 5: Restart Claude Code to pick up the new ANTHROPIC_BASE_URL
For deployments that require an outbound HTTP proxy, set HTTPS_PROXY and/or
HTTP_PROXY in the environment of the neurolink proxy process before starting
it. ALL_PROXY is used when no scheme-specific proxy is set, and NO_PROXY
bypasses the proxy for matching hosts. These settings cover Anthropic, Codex,
and Vertex passthrough forwards, OAuth requests, and quota polling. When a
configured proxy fails, these requests fail instead of retrying directly.
Restart the serving worker
neurolink proxy restart --check
neurolink proxy restart
restart starts a replacement worker while the existing worker continues serving.
The supervisor switches new connections only after the replacement acknowledges
readiness and activation, then lets requests on the previous worker finish.
You do not need to run the check separately: restart includes it.
The command verifies admission, worker identity/version, socket-transfer counters and preservation of request-log disk/OTel settings. It does not generate a model request. A failed candidate has a 120-second readiness limit; the serving worker is retained. Concurrent restarts, pending updates and additional restarts while an older worker is still draining are refused. A disconnected terminal does not cancel the supervisor-owned operation or close admission.
Use --format json for scripts. Exit zero means the check or activation was
verified. activated_unverified means the replacement activated but a subsequent
check failed; inspect neurolink proxy status before another operation. A lost
control connection leaves the outcome unknown, rather than triggering a forced
service restart.
The JSON contract is CliProxyRestartOutput: an authenticated supervisor result
or { "ok": false, "phase": "unverified", "message": "..." } when the CLI cannot
receive or authenticate that result. The latter exits nonzero and deliberately
omits supervisor/worker fields that could not be verified. It does not mean the
replacement failed or was rolled back. Control-server errors after binding are
reported through the supervisor logger without stopping the serving listener.
This requires a running supervisor that advertises restart-control support. Older supervisors must first receive a separately planned service activation; installing a newer CLI alone does not add the capability to an existing process. The command refuses an unsupported supervisor without signalling it.
The listener and supervisor remain running, so this command does not apply changed
launchd stdout/stderr destinations or replace supervisor/updater code. Those need
a service activation with its own interruption budget. proxy install is service
setup, not a routine restart command. The restart command does not rewrite the
launcher, environment, routing configuration or update history. Local OTel
initialization is checked; collector/backend delivery still needs telemetry
verification.
How It Works
Request Flow
Every request from Claude Code flows through the proxy in one of two modes:
Passthrough mode (Claude to Claude): The request body is forwarded directly to api.anthropic.com with only the authentication headers modified. This preserves multi-turn conversation history, thinking content, cache control, and tool definitions exactly as Claude Code sent them. No lossy conversion through an intermediate format.
Translation mode (Claude to other provider): When model routing directs a request to a non-Anthropic provider, the proxy parses the Claude Messages API request into NeuroLink's internal format, calls neurolink.generate() or neurolink.stream(), and serializes the result back into Claude Messages API format (including SSE streaming events). For streaming, the proxy emits SSE keep-alive comments (: keep-alive) every 15 seconds during idle periods to prevent connection timeouts.
Trace And Session Context
If the caller sends W3C trace headers (traceparent, tracestate) or NeuroLink session headers (x-neurolink-session-id, x-neurolink-user-id, x-neurolink-conversation-id), the proxy links its spans to the caller trace and preserves that session/user/conversation context in proxy traces and logs.
Token Management
The proxy uses three coordinated token refresh paths:
- Background check -- Every 30 seconds, one non-overlapping maintenance cycle checks allowed, enabled accounts.
- Pre-request check -- A request refreshes an OAuth token when it is within 5 minutes of expiry.
- 401 retry -- An unexpected Anthropic 401 triggers refresh and bounded retry before account rotation.
Refresh calls sharing the same rotating refresh token are serialized and reuse the winning result. Credential rejection responses (400, 401, 403, or 404) disable the account until explicit login; network errors, refresh-endpoint 429s, and 5xx responses apply a bounded 30-second to 5-minute auth cooldown instead. Automatic token saves preserve an operator-disabled account's metadata.
Refreshed TokenStore and legacy credentials are persisted with 0o600 permissions using serialized atomic snapshot writes.
Multi-Account Routing
When multiple accounts are available, the proxy uses fill-first routing:
- Use the first non-cooling account for every request.
- On a 429, classify the authoritative quota window, persist its cooldown, and try the next account.
- Continue until a request succeeds or all accounts are exhausted.
- If all accounts are exhausted, walk the fallback chain (alternative providers).
- If all fallbacks fail, return a 429 with a
Retry-Afterheader indicating the earliest account recovery time.
If every account has a known future cooldown, the proxy does not call any of them again. Cooldowns survive restarts in ~/.neurolink/account-cooldowns.json.
Account sources are checked in priority order:
- TokenStore compound keys (e.g.,
anthropic:work,anthropic:personal) -- fromneurolink auth login --label - Legacy credentials file (
~/.neurolink/anthropic-credentials.json) -- only if no TokenStore accounts exist - Environment variable (
ANTHROPIC_API_KEY) -- only if no other accounts exist
Designating a primary (home) account
By default the "first" account is the first key in token-store insertion order. To override this without re-OAuthing or editing the obfuscated token store (~/.neurolink/tokens.json is XOR-obfuscated, not encrypted — treat the file as a secret), set routing.primary-account in the proxy config. The proxy resolves the email to a stable token-store key per request, so the choice survives account additions/removals and only takes effect when that account is currently authenticated:
neurolink auth set-primary alice@example.com
neurolink proxy status # Config: generation N
curl http://127.0.0.1:55669/status # stats.primaryAccount.label = alice@example.com
After a 429 cools off, traffic returns to the configured primary (not literal index 0). When the configured account is missing or disabled, the proxy logs a warning while loading the configuration and falls back to insertion-order index 0. See the config reference for the full CLI surface (set-primary / get-primary / clear-primary).
Restricting eligible accounts
primary-account controls ordering; it is not a security or isolation boundary. To ensure the proxy can use only an explicit set of Anthropic credentials, configure routing.account-allowlist:
routing:
primary-account: primary@example.com
account-allowlist:
- primary@example.com
Entries accept an email/label or a full anthropic:<email-or-label> key and are matched case-insensitively. When the field is present, unlisted TokenStore accounts are excluded before token loading or refresh. The legacy credential and ANTHROPIC_API_KEY fallback are also denied unless explicitly listed as legacy-default or env, and neither hidden fallback is considered while any Anthropic TokenStore entry exists. An empty list denies all Anthropic credentials; an absent field preserves unrestricted account discovery.
Fallback Chain
When all Claude accounts are rate-limited, the proxy walks the fallback chain defined in the config file. Each fallback entry specifies a provider and model:
routing:
fallback-chain:
- provider: google-ai
model: gemini-3-flash-preview
- provider: openai
model: gpt-4o
Fallback requests go through NeuroLink's stream() pipeline (translation mode), which handles the format conversion to and from the target provider's API. Tools, thinking configuration, and conversation history from the original request are passed through to the fallback provider.
Configuration
Proxy config file
The proxy loads configuration from ~/.neurolink/proxy-config.yaml by default (override with --config). The file supports YAML or JSON format with environment variable interpolation. The running proxy watches this file and its resolved proxy env file. Valid routing edits are published atomically for new requests; in-flight requests keep their original generation. Invalid or deleted observed files are rejected and the last-known-good generation remains active.
# ~/.neurolink/proxy-config.yaml
version: 1
# Account definitions (alternative to neurolink auth login)
accounts:
anthropic:
- name: primary
apiKey: ${ANTHROPIC_API_KEY_PRIMARY}
- name: secondary
apiKey: ${ANTHROPIC_API_KEY_SECONDARY}
weight: 2
rateLimit: 100
# Routing configuration
routing:
strategy: fill-first # or round-robin
quota-routing: true
session-soft-limit: 0.97
session-reset-tolerance-ms: 900000
primary-account: primary@example.com
account-allowlist:
- primary@example.com
# Model mappings: remap incoming model names to different providers
model-mappings:
- from: claude-sonnet-4-20250514
to: gemini-3-pro-preview
provider: google-ai
# Fallback chain: try these when all Claude accounts are exhausted
fallback-chain:
- provider: google-ai
model: gemini-3-flash-preview
- provider: openai
model: gpt-4o
# Models that always go to Anthropic (skip routing logic)
passthrough-models:
- claude-opus-4-20250514
- claude-sonnet-4-5-20250929
# Cloaking configuration (request transformation for OAuth)
cloaking:
mode: auto # "auto" | "always" | "never"
plugins: {}
When routing is enabled, any requested model that starts with gemini- is treated as a Vertex target by default unless an explicit model-mappings rule overrides it.
Environment variable interpolation
String values in the config file support ${VAR_NAME} and ${VAR_NAME:-default} syntax:
accounts:
anthropic:
- name: primary
apiKey: ${ANTHROPIC_KEY_1}
- name: fallback
apiKey: ${ANTHROPIC_KEY_2:-sk-ant-fallback-key}
Account configuration options
| Field | Type | Default | Description |
|---|---|---|---|
name | string | unnamed | Human-readable label for the account |
apiKey | string | -- | API key or token (supports ${ENV_VAR}) |
baseUrl | string | -- | Override the provider endpoint URL |
orgId | string | -- | Organization ID (e.g., for OpenAI orgs) |
weight | number | 1 | Weight for weighted round-robin selection |
enabled | boolean | true | Whether this account is active |
rateLimit | number | -- | Max requests per minute for this account |
metadata | object | -- | Arbitrary metadata attached to the account |
Server options
| Option | Default | Description |
|---|---|---|
port | 55669 | Port to listen on |
host | 127.0.0.1 | Host to bind to |
config | ~/.neurolink/proxy-config.yaml | Path to config file |
Inbound authentication
Every request the proxy serves spends one of the pooled subscriptions, so the proxy decides at start-up whether anything authenticates its callers:
- Loopback (
127.0.0.1,localhost,::1) binds always start. The only callers are processes on this machine. - Any other
--hostis refused unless one of these is in place:- an inbound token —
--inbound-token <secret>orNEUROLINK_PROXY_INBOUND_TOKEN. Every caller must then send it asx-neurolink-inbound-token: <secret>orAuthorization: Bearer <secret>(the proxy replacesAuthorizationupstream, so an Anthropic SDK client can simply use the token as itsANTHROPIC_AUTH_TOKEN). A request carrying a share token is judged by the share gate instead./healthstays open;/peer/*authenticates itself;/internal/update-controlhas its own token. - the share gate —
NEUROLINK_PROXY_REQUIRE_GRANT=1, which refuses any request without a valid share token (including the operator's own client). --allow-unauthenticated(orNEUROLINK_PROXY_ALLOW_UNAUTHENTICATED=1), which records that an authenticating reverse proxy sits in front. The proxy starts and logs a warning.
- an inbound token —
GET /limits, GET /accounts and GET /status are operator surfaces: they
name every account (an email for an OAuth account) with its quota and spend.
They are refused to borrowers and, when an inbound token is configured, to any
caller without it.
Other inbound-trust guarantees, regardless of bind address:
- Only protocol headers travel upstream (
content-type,accept*,anthropic-version,anthropic-beta,user-agent,x-app,x-stainless-*,x-claude-code-session-id, …).x-neurolink-*,traceparent/tracestate/baggage,cf-*,x-forwarded-*,x-real-ip,via,forwarded, cookies and hop-by-hop headers never reach Anthropic. - A client may only add
anthropic-betavalues the proxy would send itself, unless it is Claude Code (bothx-claude-code-session-idand aclaude-cli/user-agent). A rejected client-supplied beta never re-ranks the pool. - One request is tried against at most
NEUROLINK_PROXY_MAX_ACCOUNTS_PER_REQUESTaccounts (default 3;0removes the cap). - The per-account header snapshot (
~/.neurolink/header-snapshots/) is captured only from Claude Code itself, never from a borrowed request, and holds only the identity tuple (device id, session id, normalised billing header). The agentsystemblock is compiled in and never taken from a request; a snapshot written by an earlier release that carries one is rewritten without it on first load.
CLI Commands
neurolink proxy setup
One-command onboarding: checks for existing accounts, runs OAuth login if needed, installs the proxy as a persistent service, and configures Claude Code.
neurolink proxy setup # Full setup: login + install as launchd service (macOS)
neurolink proxy setup --no-service # Login + start foreground (no auto-restart)
neurolink proxy setup -p 9000 # Setup on custom port
neurolink proxy install
Install the proxy as a persistent macOS launchd service. The service auto-restarts on crash (5-second throttle interval) and starts on login.
neurolink proxy install # Install with defaults (port 55669)
neurolink proxy install --port 9000 # Install on custom port
neurolink proxy install --host 0.0.0.0 --inbound-token "$(openssl rand -hex 32)" # All interfaces, callers must present the token
A non-loopback --host without --inbound-token, NEUROLINK_PROXY_REQUIRE_GRANT=1
or --allow-unauthenticated is refused — see Inbound authentication.
The token is persisted into the service's environment (the plist is 0600).
Options:
| Flag | Alias | Default | Description |
|---|---|---|---|
--port | -p | 55669 | Port to listen on |
--host | -H | 127.0.0.1 | Host to bind to |
neurolink proxy uninstall
Remove the launchd service. Stops the proxy if it is running and deletes the launchd plist.
neurolink proxy uninstall
neurolink proxy start
Start the proxy server.
neurolink proxy start # Default: port 55669, fill-first
neurolink proxy start -p 8080 -s fill-first # Custom port and strategy
neurolink proxy start --config ./my-proxy.yaml # Custom config file
neurolink proxy start --debug # Enable debug logging
neurolink proxy start --quiet # Suppress non-essential output
neurolink proxy start --passthrough # Transparent forwarding (no retry/rotation)
neurolink proxy start --env-file ./proxy.env # Load provider keys from dedicated file
Options:
| Flag | Alias | Default | Description |
|---|---|---|---|
--port | -p | 55669 | Port to listen on |
--host | -H | 127.0.0.1 | Host to bind to. Non-loopback needs --inbound-token, NEUROLINK_PROXY_REQUIRE_GRANT=1 or --allow-unauthenticated |
--inbound-token | Bearer every caller must present (x-neurolink-inbound-token or Authorization: Bearer); also NEUROLINK_PROXY_INBOUND_TOKEN | ||
--allow-unauthenticated | false | Bind a non-loopback host with no inbound authentication (an authenticating reverse proxy must sit in front) | |
--strategy | -s | fill-first | Account selection strategy (fill-first or round-robin) |
--health-interval | 30 | Health check interval (seconds) | |
--config | -c | ~/.neurolink/proxy-config.yaml | Config file path |
--quiet | -q | false | Suppress output |
--debug | -d | false | Enable debug output |
--passthrough | false | Transparent forwarding (no retry, rotation, or polyfill) | |
--env-file | Path to .env file for provider API keys |
Strategy choices: round-robin, fill-first
neurolink proxy status
Show proxy status, including PID, uptime, strategy, fallback chain, and per-account usage statistics fetched from the live /status endpoint. Status output now distinguishes total upstream attempts from completed requests, so retry-heavy incidents are easier to spot.
neurolink proxy status # Human-readable text output
neurolink proxy status --format json # Machine-readable JSON
neurolink proxy telemetry <action>
Manage the local OpenObserve stack and the maintained proxy dashboard from the CLI.
neurolink proxy telemetry setup # Start OpenObserve + OTEL collector and import dashboard
neurolink proxy telemetry start # Start the local telemetry stack only
neurolink proxy telemetry stop # Stop the local telemetry stack
neurolink proxy telemetry status # Show local stack health
neurolink proxy telemetry logs # Follow OpenObserve + collector logs
neurolink proxy telemetry import-dashboard # Re-import the dashboard without restarting containers
These commands use the repo-owned assets under scripts/observability/ and the dashboard JSON at docs/assets/dashboards/neurolink-proxy-observability-dashboard.json.
neurolink proxy analyze
Read the proxy's own request and attempt logs and report what actually happened: per-account success and failure counts, retry-recovered requests, terminal error categories, and routing decisions.
neurolink proxy analyze # summarise the retained window
neurolink proxy analyze --logs-dir <path> # a specific log directory
Reported counts are bounded by log retention. When the request and attempt windows are not comparable, the command says so rather than printing a recovered-after-retry figure it cannot stand behind.
neurolink proxy replay <export|compare>
Reconstruct a captured request for debugging, or send it directly upstream to compare proxied and direct behaviour.
neurolink proxy replay export --request-id <id> --output bundle.json
neurolink proxy replay compare --bundle bundle.json --execute
compare reaches a provider, so it requires the explicit --execute flag.
Captured bodies are redacted; supply --body-file when a full body is needed,
and --header-env to inject a credential from an environment variable rather
than a literal.
neurolink proxy share <action>
Lend spare pool capacity to a peer, with the terms enforced on every request. See Proxy Peer Sharing for the full guide.
neurolink proxy share url https://proxy.example.com # record this node's address
neurolink proxy share url # show it
neurolink proxy share url get # bare value, for scripts
neurolink proxy share url --clear # forget it
neurolink proxy share create --peer <name> --preset spare
neurolink proxy share list | status [--peer <name>]
neurolink proxy share pause --peer <name> # stops at their next request
neurolink proxy share resume --peer <name>
neurolink proxy share set --peer <name> --reserve 40 --max-slice 5h=15
neurolink proxy share topup --peer <name> --coins 200
neurolink proxy share rotate --peer <name> # new token, same controls
neurolink proxy share revoke --peer <name>
neurolink proxy share delete --peer <name>
neurolink proxy share level --peer <name> --to complete
neurolink proxy share provision --peer <name> --from-account <account-label>
neurolink proxy share provision --peer <name> --code <code> # finish it
neurolink proxy share receipts --peer <name> # what you charged them
neurolink proxy share note --coins 200 --ttl 30d # mint a transferable note
neurolink proxy share notes # notes minted, and spent
share provision is a split-PKCE flow: the borrower runs
neurolink proxy peer request first and keeps the verifier, you authorize in
your browser and relay a single-use code. You never hold a token for the
credential you mint. See
Proxy peer sharing.
Controls, all applied together:
| Flag | Meaning |
|---|---|
--reserve 30 | Keep 30% headroom on each account for yourself |
--max-slice 20 | Borrower may take at most 20% of the pool, however spread |
--max-slice-per-account 20 | Apply that ceiling to each account independently instead |
--spillover 12h\<60@25 | Lend only near a reset, when little of the window was used |
--models sonnet haiku | Restrict which model tiers the share covers |
--accounts <label> | Restrict which of your accounts are lendable |
--rate 20/min --concurrency 2 | Request and in-flight ceilings |
--schedule 21-9 | Hours the share is open (wraps midnight) |
--expires 7d | Grant lifetime |
--ledger coins --coins 500 --refill 100/week | Meter it instead of leaving it open |
Presets fill these in: spare (reserve + pool slice), spillover, metered,
open. Any explicit flag overrides the preset.
neurolink proxy peer <action>
Borrow capacity from someone else's pool. Peers are consulted only after every local account is spent, ahead of the provider fallback chain.
neurolink proxy peer add --name <lender> --link "neurolink://share/...#<token>"
neurolink proxy peer add --name <lender> --url https://proxy.example.com --token <token>
neurolink proxy peer request --name <lender> # ask to be provisioned
neurolink proxy peer request --name <lender> --claim # collect and install
neurolink proxy peer list | status [--json]
neurolink proxy peer test --name <lender> # free reachability probe
neurolink proxy peer sync [--name <lender>] # force a check-in
neurolink proxy peer receipts --name <lender> # check what you were charged
neurolink proxy peer net --name <lender> # net reciprocal use
neurolink proxy peer redeem --name <lender> --coin-note <note>
neurolink proxy peer set --name <lender> --priority 5
neurolink proxy peer pause | resume | remove --name <lender>
neurolink proxy expose
Publish this node through a cloudflared tunnel, for operators without an
address of their own. With no --port it picks the gate-only share
listener — the port that requires a grant on every request — and refuses to
open a tunnel to anything that serves untokened requests.
neurolink proxy expose # the share listener
neurolink proxy expose --port 3000 # or a port you name
neurolink proxy expose --named <tunnel-name> # stable URL across restarts
The share listener starts on its own once at least one grant is active, on
--share-port (default: proxy port + 1). Your own client keeps using the main
port untokened.
If you already front the proxy with your own domain, skip this and record the
address with neurolink proxy share url <url> instead.
neurolink auth login anthropic
Authenticate with Anthropic. Supports multi-account pooling via --add --label.
# Interactive (prompts for method)
neurolink auth login anthropic
# OAuth (for Claude Pro/Max subscription)
neurolink auth login anthropic --method oauth
# API key
neurolink auth login anthropic --method api-key
# Create API key via OAuth (Claude Pro/Max)
neurolink auth login anthropic --method create-api-key
# Add a second account with a label
neurolink auth login anthropic --method oauth --add --label work
neurolink auth login anthropic --method oauth --add --label personal
# Non-interactive mode (requires environment variables)
neurolink auth login anthropic --method api-key --non-interactive
Options:
| Flag | Alias | Default | Description |
|---|---|---|---|
--method | -m | -- | Auth method: api-key, oauth, create-api-key |
--add | false | Add as additional account to the pool (instead of replacing) | |
--label | -- | Human-readable label for this account (used with --add) | |
--non-interactive | false | Skip interactive prompts (requires environment variables) | |
--format | text | Output format: text or json | |
--debug | false | Enable debug output |
neurolink auth list
List all authenticated accounts with status, including the account email address (resolved via OAuth token exchange), token expiry, and per-account quota utilization (5-hour and 7-day windows).
neurolink auth list # Text output
neurolink auth list --format json # JSON output
neurolink auth list --debug # Include debug details
neurolink auth status
Show authentication status for a specific provider (or all providers if omitted).
neurolink auth status # Show all providers
neurolink auth status anthropic # Show Anthropic only
neurolink auth status --format json # JSON output
neurolink auth refresh
Manually refresh OAuth tokens.
neurolink auth refresh anthropic
neurolink auth cleanup
Remove accounts from the token store whose credentials no longer work.
neurolink auth cleanup # Interactive: prompts before removing
neurolink auth cleanup --force # Remove without prompting
Only accounts the proxy gave up on are deleted — expired entries with no refresh
token, and accounts disabled for missing_refresh_token, refresh_invalid or
refresh_failed. An account you disabled yourself, or one blocked by an
organization policy, still holds a valid login, so cleanup keeps it and tells you
to use auth enable or auth remove instead.
neurolink auth enable / neurolink auth disable
Take an account out of the proxy pool, or put it back. Disabling keeps the credentials; the proxy re-reads the token store on every request, so it takes effect on the next one without a restart.
neurolink auth disable anthropic:work --reason "org disabled oauth"
neurolink auth enable anthropic:work
The proxy also disables an account automatically when Anthropic refuses it on an
organization entitlement policy (403 permission_error), after rotating the
request to a healthy account. Re-enable it once an admin restores access.
neurolink auth cooldown <action> [account]
Inspect or release the per-account cooldowns the proxy persists after rate limits
and auth failures. action is list or clear.
neurolink auth cooldown list
neurolink auth cooldown clear anthropic:work
neurolink auth cooldown clear --all
A running proxy caches cooldowns for its process lifetime, so restart it for a clear to take effect on an instance that is already serving.
neurolink auth overage [action]
Show or set whether the pool may keep serving on paid extra usage once an
account's subscription window is spent. Writes routing.use-overage to the proxy
config, which a running proxy picks up automatically. action is status
(the default), auto, always or never.
neurolink auth overage # current policy and per-account status
neurolink auth overage auto|always|never
neurolink auth overage never # stop at the subscription limit
Only never overrides the provider. Nothing here can enable extra usage that
Anthropic reports as disabled — status names the reason when it is, for example
org_level_disabled.
neurolink auth set-primary / get-primary / clear-primary
Pin routing to a preferred account, read the current pin, or remove it. The primary is a preference, not a guarantee: a saturated or cooling primary is still passed over.
neurolink auth set-primary <account-label>
neurolink auth get-primary
neurolink auth clear-primary
neurolink auth health
Report per-account credential health — token validity, expiry, disabled state and the reason for it.
neurolink auth health
neurolink auth health --format json
neurolink auth logout <provider> / neurolink auth remove <provider>
logout clears stored tokens for a provider but keeps the account entry.
remove deletes the entry entirely.
neurolink auth logout anthropic
neurolink auth remove anthropic
neurolink auth validate <token>
Check a token against the provider without storing it — useful when diagnosing whether a credential or the routing around it is at fault.
neurolink auth validate <token>
neurolink auth providers
List the providers the auth subsystem supports and which of them have stored credentials.
neurolink auth providers
Multi-Account Setup
Adding multiple accounts
Each neurolink auth login --add --label <name> creates a separate account entry in the TokenStore (~/.neurolink/tokens.json):
# Account 1: personal Claude Max
neurolink auth login anthropic --method oauth --add --label personal
# Account 2: work Claude Max
neurolink auth login anthropic --method oauth --add --label work
# Account 3: API key for fallback
neurolink auth login anthropic --method api-key --add --label api
How accounts are selected
The proxy discovers accounts in this order:
- Compound keys from TokenStore (e.g.,
anthropic:personal,anthropic:work) - Legacy credentials file (if no compound keys exist)
ANTHROPIC_API_KEYenvironment variable (if no other accounts exist)
When routing.account-allowlist is configured, this discovery happens only within the allowed set. A disabled or unavailable TokenStore account does not cause the proxy to activate a legacy file or environment key while TokenStore entries still exist.
Within the account pool, the proxy uses fill-first routing: it always tries the first non-cooling account and only switches on failure. This avoids unnecessary identity switches that could confuse Claude Code's session state.
Model-scoped weekly limits
Some plans cap a specific model separately from the overall weekly window — a
Fable-only weekly allowance, for instance. Anthropic reports that cap as its own
header family (anthropic-ratelimit-unified-7d_oi-*), sent only on responses
for the model it applies to, so the proxy learns about it from live traffic
rather than needing a refresh.
Routing treats a spent model-scoped cap as per-model, never per-account:
- An account whose cap for the requested model is spent is skipped for that model only. It stays fully available for every other model, and no cooldown is set — cooling is account-wide and would wrongly withhold a healthy account.
- Among accounts that do have headroom, the one closest to spending its allowance is preferred, so the pool finishes an allowance rather than spreading across all of them. This rung only applies when both candidates report a window for the model.
- If every account has spent the cap, the request is not attempted. The
client gets a
429naming the model, the real reset time, and that other models remain available — switch model, or add an account with headroom. - A scoped window older than the quota freshness budget is ignored, so stale or mis-parsed data can never take the pool down. Routing falls back to attempting the request.
neurolink auth list shows any scoped window under its account, and
GET /limits returns the full windows[] array.
Cooldown and backoff
When an account encounters an error, it enters a cooldown period based on the error type:
| Failure | Cooldown | Behavior |
|---|---|---|
| Authoritative unified, 5-hour, or 7-day rejection | Upstream reset or Retry-After, capped per reason | Persist cooldown and rotate immediately |
| Transient burst 429, as a status or as the stream's first event | Upstream delay, capped at 15 minutes | At most 2 same-account retries, then rotate |
Refresh credential rejection (400/401/403/404) | Disabled until explicit login | Rotate without retrying an invalid refresh token |
Refresh network, 429, or 5xx | 30 seconds to 5 minutes | Persist auth cooldown and rotate |
Upstream 5xx or network error | Bounded same-account retries | Rotate after retry budget |
Cooldown updates are extend-only: a late concurrent response cannot shorten a longer known reset window.
The one exception is a transient burst. When a same-account retry is served, the burst is over, so its cooldown is lifted and the session's next request stays on that account instead of moving away and losing its prompt cache. An exhaustion cooldown recorded in the meantime is left in place.
Each cooldown is also capped by what its reason can mean — a session cooldown
describes a 5-hour window, so it can never run for days no matter what reset the
upstream reports. The cap is applied when the cooldown is written and again when
it is read back from disk, so an entry written by an older build heals itself on
load and logs that it did:
[proxy] cooldown clamp: anthropic:work session entry healed from 206.0h to 5.3h — the stored wait exceeded what "session" can mean
Use neurolink auth cooldown list to see what is currently parked, and
neurolink auth cooldown clear <account> to release one.
Error Handling
The proxy classifies upstream errors and applies different strategies:
429 Rate Limit
- Treat top-level
anthropic-ratelimit-unified-status: rejectedas authoritative, even if sub-windows still sayallowed. - Prefer the rejected 5-hour or 7-day reset and otherwise use
Retry-After. - Persist the cooldown and rotate immediately for authoritative exhaustion.
- Return the earliest recovery timestamp without another upstream request when all accounts are cooling.
401/402/403 Authentication Errors
- OAuth accounts with refresh token: Serialize refresh, persist the new rotating token, and retry. A rejected refresh credential disables the account until re-authentication; transient refresh infrastructure errors cool and rotate.
- OAuth accounts without refresh token: Disable until re-authentication and rotate.
- API key accounts: Rotate after authentication failure.
400/422 Request Shape Error
- Detected via HTTP 422 status or
invalid_request_errorerror type in the response body. - No retry or failover. These are client-side errors (malformed request, invalid parameters).
- Return the error body directly to Claude Code.
400 Refused Image Reference
- A translated request (OpenAI, Gemini, or Claude routed to another provider) named an image the proxy will not resolve: a local path, an
http://URL, another scheme, anhttps://URL that could not be fetched safely, an empty URL, or (Gemini) raw base64 whosemimeTypeis not animage/<subtype>type; or it carried image bytes that are not a recognised image: text, a path, a page, SVG, an empty (or absent) payload, or one too short to hold a complete recognised signature. - Answered locally before any provider is contacted, with code
invalid_image_reference,invalid_image_data,image_url_unfetchableorimage_input_too_large. No retry and no fallback. - See Image references on translated requests.
404 Not Found
- Typically means the model is not available for this account.
- No cooldown applied.
- Return the error body immediately to the client (no failover to next account).
5xx / Transient Server Error
- Transient errors (408, 500, 502, 503, 504, and Cloudflare 520-526/529).
- Also matches
400responses withapi_errororoverloaded_errortypes that wrap transient HTML content (e.g., Cloudflare error pages). - Apply bounded same-account retries, then rotate to the next account.
All Accounts Exhausted
When every account is in a cooling state:
- Walk the fallback chain (if configured).
- Each fallback uses NeuroLink's
stream()pipeline with the specified provider/model. - If all fallbacks also fail, return a 429 with
Retry-Afterset to the earliest account recovery time.
Bootstrap Retry (Streaming)
For streaming requests, the proxy reads the first chunk from the upstream response before forwarding it to the client. If the first chunk is empty (indicating a failed stream), the proxy retries with the next account. This prevents Claude Code from receiving an empty SSE stream.