- Add RESUME.md for cross-session continuity (open items, key state, constraints) - claude-max-bridge: implement /v1/responses (OpenAI Responses API) with previous_response_id chaining via server-side history injection into system prompt. Root cause of "session already in use": CLI leaves JSONL in un-resumable "dequeued" state after each --print run; fix avoids session reuse entirely. Also fixed: assistant content must be array-of-blocks not plain string (silent JS crash otherwise). - LiteLLM: add pass_through_endpoints for /v1/responses → claude-max-bridge - Storage, volumes, architecture docs reconciled (Vaultwarden → local zfs, Pocket-ID backup, WAL-G fix, apps/ decommission, Nextcloud CIFS→NFS) - Add ideas/, proxmox-memory-audit.md, llm-benchmark.md (new docs this session) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8.1 KiB
Design: Claude Max Subscription → LiteLLM Gateway Bridge
Status: Proposal — not implemented
Context: LiteLLM at https://ai.nuclide.systems currently has no Anthropic models. Claude Max subscription (claude.ai) provides high-rate access to Sonnet 4.5/4.6 and Opus 4.7 but is decoupled from Anthropic API billing. This doc explores bridging the two.
Problem
Claude Max and the Anthropic API are separate products with separate billing:
| Claude Max | Anthropic API | |
|---|---|---|
| Auth | OAuth / browser session | API key |
| Billing | Flat monthly subscription | Per-token |
| Rate limits | 5h rolling windows, model-specific | Tier-based RPM/TPM |
| Access | claude.ai web + Claude Code CLI | Any HTTP client |
The goal is to surface Max-subscription capacity through LiteLLM so that LobeHub, n8n, Coder workspaces, and other internal tools can call claude-sonnet-4-6 at zero marginal cost and fall back to paid providers only when Max limits are hit.
Approaches
A — Session Cookie Reverse-Engineering (not recommended)
Several community projects (e.g. claude-unofficial-api) scrape the claude.ai WebSocket/HTTP protocol and expose an OpenAI-compatible endpoint. LiteLLM would point at this as a custom openai/ provider.
Pros: Exposes the full web model lineup; streaming works.
Cons: Violates Anthropic ToS; breaks on any claude.ai front-end change; auth flow requires persisting browser cookies; no multimodal or tool-use parity guarantees.
Verdict: Avoid. Fragile and non-compliant.
B — Claude Code CLI Bridge (recommended)
Claude Code CLI (claude) is already installed on CT 104 and authenticated with the Max subscription via ~/.claude/. It ships a --print / --output-format stream-json mode designed for non-interactive use, and Anthropic explicitly supports programmatic use of the CLI.
A small claude-max-bridge service wraps this CLI as an OpenAI-compatible HTTP endpoint. LiteLLM registers it as a custom openai/ base URL. No ToS issues — this is the supported surface.
LobeHub / n8n / Coder / Claude Code
│
▼
LiteLLM Gateway (ai.nuclide.systems)
│ model: claude-sonnet-4-6 → openai/claude-sonnet-4-6
│ api_base: http://claude-max-bridge:8000
▼
claude-max-bridge (new container, CT 104 ai-internal)
│ subprocess: claude --model ... --print --output-format stream-json
▼
~/.claude/ (Max subscription session)
│
▼
Anthropic (claude.ai)
Pros:
- Uses the officially supported programmatic interface
- Auth is already set up; no cookie management
claudeCLI handles retries, token limits, context window management- Subprocess overhead is ~200–400 ms cold; warm invocations faster
Cons:
- One subprocess per request — cannot multiplex a single session (unlike streaming HTTP)
- CLI is tied to the single authenticated user; no multi-user isolation
- Max rate limits apply per-account, same pool as interactive use
- Claude Code SDK (TypeScript) is cleaner but adds Node dependency
C — Official API + Budget Cap (stop-gap)
Add Anthropic API key to LiteLLM with a hard budget cap (e.g. $20/month). Use it for Claude-specific features (tool use, long context) and let the existing free SAIA/Gemini fallback chain absorb general-purpose traffic.
Pros: Zero implementation work; full API feature parity.
Cons: Still costs money; no benefit from Max subscription.
Verdict: Valid fallback if B proves too complex, or as a complement for tool-heavy workloads that need the official API surface.
Recommended Architecture (Option B)
claude-max-bridge service
/opt/stacks/ai/claude-max-bridge/
Dockerfile
server.py # FastAPI, ~150 lines
docker-compose.yml (or entry in ai/docker-compose.yml)
Dockerfile — reuse the existing claude CLI install:
FROM python:3.12-slim
RUN pip install fastapi uvicorn
# Mount ~/.claude from host; claude binary from host PATH or copied in
COPY server.py /app/server.py
CMD ["uvicorn", "app.server:app", "--host", "0.0.0.0", "--port", "8000"]
server.py — OpenAI /v1/chat/completions shim:
# POST /v1/chat/completions
# Translates messages[] → claude --print --model ... --output-format stream-json
# Streams NDJSON lines back as SSE (text/event-stream)
# Maps finish_reason, usage tokens from CLI output headers
Key translation notes:
messages→ write to a temp file, pass via--input-file(avoids shell quoting issues)stream: true→ parsestream-jsonlines, emitdata: {...}SSE chunksstream: false→ buffer all chunks, return single response objectmax_tokens,temperature,system→ map to--max-tokens,--temperature,--system- Tool use: not supported in first iteration; return 501 for requests with
tools - Model name passthrough:
claude-sonnet-4-6→--model claude-sonnet-4-6
LiteLLM config additions
model_list:
- model_name: claude-sonnet-4-6
litellm_params:
model: openai/claude-sonnet-4-6
api_base: http://claude-max-bridge:8000
api_key: "dummy" # bridge ignores it; LiteLLM requires a value
stream_timeout: 120
timeout: 120
- model_name: claude-opus-4-7
litellm_params:
model: openai/claude-opus-4-7
api_base: http://claude-max-bridge:8000
api_key: "dummy"
stream_timeout: 180
timeout: 180
- model_name: claude-haiku-4-5
litellm_params:
model: openai/claude-haiku-4-5-20251001
api_base: http://claude-max-bridge:8000
api_key: "dummy"
stream_timeout: 60
timeout: 60
Add to router_settings.fallbacks — Max hits rate limit → fall to SAIA:
fallbacks:
- claude-sonnet-4-6: [qwen3.5-397b-a17b]
- claude-opus-4-7: [qwen3.5-397b-a17b, mistral-large-latest]
- claude-haiku-4-5: [qwen3.5-122b-a10b, cerebras-llama-3.1-8b]
Rate limit handling
The bridge should detect the CLI's rate-limit exit code / stderr message and return HTTP 429. LiteLLM's router will then trigger the fallback chain. No custom logic needed in LiteLLM itself.
Max limits as of 2026 (approximate, vary by plan tier):
- Sonnet 4.6: ~50 messages / 5-hour window at Pro; higher at Max
- Opus 4.7: ~10–20 messages / 5-hour window
- Haiku 4.5: effectively unlimited under Max
Volume estimate
Typical homelab workloads (LobeHub chat, n8n automations, Coder coding assist) generate maybe 200–500 completions/day. Max's 5-hour windows reset 4–5× daily, so the practical limit is not usually hit unless Opus is used heavily.
Risks
| Risk | Mitigation |
|---|---|
| CLI API changes between Claude Code releases | Pin the claude binary version; watch for breaking changes in release notes |
| Max rate limit shared with interactive use | Monitor via claude usage; bridge adds X-Max-Usage header from CLI output |
| Auth session expiry | Bridge returns 401 on auth failure; detect and alert via Gotify |
| Subprocess latency (cold start) | Keep a warm subprocess pool (1–2 persistent processes) using --interactive + message passing, or accept the 200–400 ms overhead |
| No tool use support | Fallback chain routes tool-use requests to API providers (Gemini, Mistral) |
Implementation plan
- Write
server.pyshim (~150 lines) + Dockerfile - Build image on CT 104, add to
ai/docker-compose.yml - Bind-mount
~/.clauderead-only into the container - Add model entries to
litellm-config/config.yaml - Test streaming via
curlagainst the bridge directly - Test end-to-end via LiteLLM → LobeHub
- Watch
docker logs claude-max-bridgefor rate-limit / auth errors for 48h
Open questions
- Does
claude --printsupport all message roles (system,user,assistantturns)? Needs verification with multi-turn conversation format. - Should the bridge be on CT 104 (access to
~/.claude) or CT 109 ops (remote docker socket to CT 104)? CT 104 is simpler for v1. - Is there an official Claude Code SDK for Python? (TypeScript SDK exists at
@anthropic-ai/claude-code; Python wrapper may be cleaner than shelling out.)