Route Hermes API requests through your Claude Max/Pro subscription instead of Extra Usage billing.
Tested models: Claude Opus 5.5 (claude-opus-5-5), Claude Fable 5, Opus 4.8, Opus 4.7, Opus 4.6, Sonnet 4.6, Haiku 5.5 (claude-haiku-5-5), Gemini 2.5 Pro/Flash
An opt-in management console, caller API keys, isolated Anthropic/Gemini account pools, cloud quota reporting and concurrency admission are available on the development branch. See managed mode setup and API. Existing configuration keeps the legacy listener; enabling management requires registering accounts and provisioning caller keys first.
Sits between Hermes and the Anthropic/Gemini APIs. On the Anthropic path it:
- Adds the existing billing metadata and forwards with the configured Claude OAuth token.
- Preserves Hermes system blocks and cache markers. Compatibility stubs are added only when the caller already supplies tools.
- Forwards response bytes unchanged, including SSE, signed thinking and tool arguments.
- Cancels the upstream connection when the client disconnects and handles backpressure/timeouts.
- Refreshes OAuth proactively and retries one rejected token once, with atomic credential writes.
- Leaves reasoning settings to Hermes; removes clear-thinking edits when thinking is inactive.
Acceptance and subscription allowance remain account/model dependent. Validate with Extra Usage disabled if that is your account policy; an HTTP 200 alone does not prove a usable response.
Zero npm runtime dependencies. Linux only. Node.js 22.13+ (managed mode uses built-in SQLite).
The Gemini routes are unchanged by the Anthropic 2.2.0 fixes.
The proxy forwards Anthropic model IDs unchanged; it has no static model catalog or allowlist. Configure model choices in your client. For Haiku, use claude-haiku-5-5 in place of claude-haiku-4-5 or claude-haiku-4-5-20251001. The proxy does not silently rewrite older model IDs or block them.
Haiku 5.5 was verified on October 7, 2026 with completed non-streaming and SSE responses through the deployed proxy, without a billing-header change. This replaces Haiku 4.5 in the current tested-model list; dated audit records retain their original model names.
Version 2.2.1 updates the fixed Claude Code billing version to 2.1.280, the minimum accepted for claude-opus-5-5. Updating the installed Claude CLI alone does not update this proxy header. The model ID is forwarded unchanged; no model allowlist change is needed.
Live checks passed for a basic answer, SSE streaming, and a two-turn temperature lookup with tool arguments/results. The existing synthetic secrets.env round-trip probe returned stop_reason: refusal on Opus 5.5; that probe is not counted as passing. These checks do not establish compatibility with every Hermes conversation or parameter combination.
- Node.js 18+
- Claude Max or Pro subscription
- Claude Code CLI installed and authenticated
- Hermes running on the same machine
npm install -g @anthropic-ai/claude-code
claude auth loginVerify:
claude auth status
# Should show: loggedIn: true, subscriptionType: max (or pro)# 1. Clone
git clone https://github.com/croll83/openclaw-billing-proxy hermes-billing-proxy
cd hermes-billing-proxy
# 2. Run setup (auto-detects credentials and Hermes)
node setup.js
# 3. Start the proxy
node index.js
# 4. Point Hermes ANTHROPIC_BASE_URL to the proxy addressThe config.json file (generated by setup or created manually):
{
"port": 18801,
"credentialsPath": "~/.claude/.credentials.json",
"stripSystemConfig": false,
"injectCCStubs": true,
"replacements": [],
"reverseMap": [],
"anthropicTimeoutMs": 3600000
}Upgrading an existing installation: explicitly set these values in your current configuration. Existing true flags and replacement lists override the new defaults.
stripSystemConfig: true is a legacy compatibility option: it moves large system blocks to messages but now retains their cache metadata. injectCCStubs: true retains compatibility stubs on tool-bearing requests without duplicating caller tool names. Requests without tools never receive stubs. Relocation is off by default; compatibility stubs remain on because removing them caused a live regression on short tool-bearing requests. Keep the same tool inventory throughout a conversation to preserve cache reuse.
Legacy outbound replacements remain opt-in. They can change identifiers and paths and are discouraged. Anthropic response reverse mapping is no longer performed; reverseMap is retained only for old configuration compatibility. anthropicTimeoutMs is the upstream socket idle timeout, not the total generation duration. It resets on socket activity, so responses can last longer than the configured interval while data keeps arriving (including SSE pings). The default is 3600000 ms (60 minutes); there is no fixed 540-second total deadline in the Anthropic handler.
Existing installations with an explicit anthropicTimeoutMs keep that value; set it to 3600000 to use the 60-minute idle timeout.
Without streaming, the upstream may generate the entire answer before sending any response bytes. That silence can trigger the idle timeout even while the model is working. Prefer stream: true for long generations, or explicitly increase anthropicTimeoutMs if a non-streaming client must wait longer. The proxy does not buffer the complete upstream response or convert between JSON and SSE.
Before response headers are sent, an idle timeout returns HTTP 504 with error.code: "upstream_idle_timeout", timeout_ms, elapsed_ms, phase and streaming, plus a readable message. Elapsed time includes authentication and any retry. After headers are sent, the proxy logs the same diagnosis and terminates the response: it cannot change the HTTP status or append JSON to an existing stream. Dashboards should treat truncated streams as failures and consult the proxy logs for details.
cat > ~/.config/systemd/user/hermes-billing-proxy.service << EOF
[Unit]
Description=Hermes Billing Proxy
After=network-online.target
Wants=network-online.target
[Service]
Environment="DEBUG_DUMP=0"
Type=simple
ExecStart=$(which node) $(pwd)/index.js
WorkingDirectory=$(pwd)
Restart=always
RestartSec=5
TimeoutStopSec=10
[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload
systemctl --user enable hermes-billing-proxy
systemctl --user start hermes-billing-proxyThe proxy auto-refreshes the OAuth token:
- Proactive: refreshes 5 minutes before expiry on the next request
- Reactive: on 401 from upstream, refreshes and retries once transparently
- Token is persisted to
~/.claude/.credentials.jsonafter refresh
No manual intervention needed. The token refresh uses platform.claude.com/v1/oauth/token.
curl http://127.0.0.1:18801/healthnode troubleshoot.js"Could not find credentials file"
- Run
claude auth loginto authenticate - Check
~/.claude/.credentials.jsonexists and is non-empty
Proxy returns 400 "extra usage"
- Check the upstream error and account/model entitlement.
- Roll back the last candidate change and retest; do not treat every error as a keyword issue.
429 Rate Limit
- Normal if you have active Claude Code sessions sharing the rate bucket
- Wait and retry
Token Expired / 401
- Should auto-recover via built-in refresh
- If persistent, check
journalctl --user -u hermes-billing-proxyfor[AUTH]lines - Manual fallback:
claude -p "ping" --max-turns 1 --no-session-persistence
Offline regression tests (no model calls or real credentials):
npm testExplicit live probes consume subscription allowance. Run on the proxy host:
python3 scripts/probe-anthropic.py --url http://127.0.0.1:18802 --case stream
python3 scripts/probe-anthropic.py --url http://127.0.0.1:18802 --case roundtripThe client checks completed answers and tool arguments, not just HTTP status. Optional on-host system/tool fixtures let cache and signed-replay probes use representative Hermes context without replaying private conversations. Receipts include status, usage and request IDs; remove request IDs and inspect error details before sharing them. The published receipts are anonymized. See the rollout record for the tested stages, limitations and rollback.
Keep DEBUG_DUMP=0 for normal operation. If explicitly enabled, transport dump auth headers are redacted; raw prompt bodies still contain private context. Old dump files are not rewritten by this release.
MIT