"How does it send to anthropic backends via subscription"
Lynkr currently uses API keys from .env, NOT OAuth tokens from incoming requests.
Claude Code (with Pro/Max OAuth token)
↓ Sends: Authorization: Bearer <oauth-token>
↓
Lynkr Proxy (localhost:8081)
↓ IGNORES incoming Authorization header
↓ Uses config.anthropic.apiKey from .env instead
↓ Routes based on tier (SIMPLE → Ollama, COMPLEX → Anthropic)
↓
Anthropic API
✓ Uses API key from .env (NOT subscription)
Result: You need an Anthropic API key in .env, can't use Claude Code Pro/Max subscription.
Claude Code (with Pro/Max OAuth token)
↓ Sends: Authorization: Bearer <oauth-token>
↓
Lynkr Proxy (localhost:8081)
↓ Preserves incoming Authorization header
↓ Routes based on tier
↓ If target = anthropic:* → Forward OAuth token AS-IS
↓
Anthropic API
✓ Validates OAuth token
✓ Charges to Pro/Max subscription
Result: Works with Claude Code subscription, no API key needed!
Lynkr doesn't check for incoming OAuth tokens yet. The code in src/clients/databricks.js always uses:
// Current code (uses .env API key)
const headers = {
"x-api-key": config.azureAnthropic.apiKey, // From .env
"anthropic-version": "2023-06-01",
};It should be:
// Proposed code (checks for OAuth first)
const authHeader = incomingHeaders?.authorization || incomingHeaders?.Authorization;
const headers = {
"x-api-key": authHeader ? undefined : config.azureAnthropic.apiKey,
"anthropic-version": "2023-06-01",
};
if (authHeader) {
headers["Authorization"] = authHeader; // Forward OAuth token
}Headroom's approach (what you asked about):
1. Headroom wraps the official Claude Code binary
2. Sets ANTHROPIC_BASE_URL=http://localhost:PORT
3. Claude Code sends OAuth token in Authorization header
4. Headroom proxy receives request WITH OAuth token
5. Headroom forwards entire request to Anthropic, INCLUDING Authorization header
6. Anthropic validates OAuth → charges subscription
Key: Headroom PRESERVES the Authorization header, doesn't replace it.
File: src/clients/databricks.js
Add function:
function getAuthHeader(incomingHeaders, providerConfig) {
// Priority:
// 1. OAuth token from incoming request (Claude Code subscription)
// 2. API key from .env (API-based usage)
const incomingAuth = incomingHeaders?.authorization || incomingHeaders?.Authorization;
if (incomingAuth && incomingAuth.startsWith('Bearer ')) {
// Has OAuth token - use it (subscription mode)
return { type: 'oauth', value: incomingAuth };
}
if (providerConfig.apiKey) {
// No OAuth - use configured API key
return { type: 'api-key', value: `Bearer ${providerConfig.apiKey}` };
}
return { type: 'none', value: null };
}Example for Anthropic:
// Before (always uses API key)
async function invokeAzureAnthropic(body) {
const headers = {
"x-api-key": config.azureAnthropic.apiKey,
"anthropic-version": "2023-06-01",
};
// ...
}
// After (checks for OAuth first)
async function invokeAzureAnthropic(body, incomingHeaders) {
const auth = getAuthHeader(incomingHeaders, config.azureAnthropic);
const headers = {
"anthropic-version": "2023-06-01",
};
if (auth.type === 'oauth') {
headers["Authorization"] = auth.value; // Forward OAuth
} else if (auth.type === 'api-key') {
headers["x-api-key"] = config.azureAnthropic.apiKey; // Use .env key
} else {
throw new Error("No authentication available for Anthropic");
}
// ...
}Current flow:
router.js → processMessage() → invokeProvider()
↓ (no headers passed)
databricks.js functions
Need:
router.js → processMessage(headers) → invokeProvider(headers)
↓ (headers passed)
databricks.js functions (headers)
Changes needed:
src/api/router.js: Already passesheaders: req.headerstoprocessMessage()src/orchestrator/index.js: Need to threadheadersto provider callssrc/clients/databricks.js: Update allinvoke*functions to acceptheaders
You can't use Claude Code subscription with Lynkr wrap yet. You need API keys.
# .env
TIER_SIMPLE=ollama:llama3.2 # Free local
TIER_COMPLEX=anthropic:claude-sonnet-4 # Needs ANTHROPIC_API_KEY
TIER_REASONING=anthropic:claude-opus-4 # Needs ANTHROPIC_API_KEY
ANTHROPIC_API_KEY=sk-ant-... # Required for anthropic tiers
OLLAMA_ENDPOINT=http://localhost:11434# .env
TIER_SIMPLE=ollama:llama3.2 # Free local
TIER_MEDIUM=ollama:qwen2.5 # Free local
TIER_COMPLEX=openai:gpt-4o # Cheap OpenAI ($)
TIER_REASONING=anthropic:claude-sonnet-4 # Anthropic API ($$$)
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
OLLAMA_ENDPOINT=http://localhost:11434# .env
TIER_SIMPLE=ollama:llama3.2
TIER_MEDIUM=ollama:qwen2.5
TIER_COMPLEX=ollama:deepseek-coder
TIER_REASONING=ollama:qwen2.5-coder:32b
OLLAMA_ENDPOINT=http://localhost:11434Limitation: No access to Claude/GPT-4 quality, but 100% free.
# 1. Login to Claude Code (gets OAuth token)
claude login
# 2. NO API keys in .env (test OAuth passthrough)
# .env
TIER_SIMPLE=ollama:llama3.2
TIER_COMPLEX=anthropic:claude-sonnet-4
# ANTHROPIC_API_KEY= ← COMMENTED OUT (forces OAuth)
# 3. Run wrap
lynkr wrap claude
# 4. Try a complex query
> Refactor this class ← Should route to COMPLEX (Anthropic via OAuth)Expected:
- Lynkr detects incoming OAuth token
- Forwards to Anthropic with OAuth header
- Anthropic validates → charges subscription
- No API key needed
Current behavior:
- Fails with "No Anthropic API key configured"
Users can:
- ✅ Use Claude Code Pro/Max subscription
- ✅ Get tier routing benefits (60-70% requests stay local)
- ✅ No separate API billing for Anthropic
- ✅ 3-5x more usage from same subscription limits
Example:
- 100 requests/day subscription limit
- 60% routed to free Ollama (don't count against limit)
- 40% hit Anthropic (count against limit)
- Net: 250 effective requests (2.5x multiplier)
Users must:
- ❌ Have separate Anthropic API key
- ❌ Pay for API usage separately
- ❌ Can't leverage Pro/Max subscription
Result: Tier routing still works, but requires API keys for all cloud providers.
Problem: Headers aren't threaded through the full call stack.
Current:
// router.js
const result = await processMessage({
headers: req.headers, // ✅ Passed here
// ...
});
// orchestrator/index.js
async function processMessage({ headers, ... }) {
// ...
await invokeProvider(body); // ❌ Headers not passed
}
// databricks.js
async function invokeAzureAnthropic(body) {
// ❌ No access to headers here
}Fix: Thread headers through all provider calls.
Different providers use different auth:
| Provider | Auth Method | Header |
|---|---|---|
| Anthropic (API) | API key | x-api-key: sk-ant-... |
| Anthropic (OAuth) | Bearer token | Authorization: Bearer <oauth> |
| OpenAI | API key | Authorization: Bearer sk-... |
| Azure OpenAI | API key or Bearer | api-key: or Authorization: |
| Bedrock | Bearer token | Authorization: Bearer ABSK... |
| Ollama | None | (no auth) |
Solution: Provider-specific auth detection.
What if OAuth is invalid?
// Proposed behavior
if (auth.type === 'oauth') {
// Try OAuth first
headers["Authorization"] = auth.value;
} else if (auth.type === 'api-key') {
// Fall back to API key
headers["x-api-key"] = config.apiKey;
} else {
// No auth available
if (provider === 'anthropic') {
throw new Error("Anthropic requires authentication");
}
}❌ OAuth passthrough not implemented
- Lynkr uses
.envAPI keys only - Can't leverage Claude Code Pro/Max subscription
- Wrap works, but requires separate API billing
Phase 1: Header threading (pass headers through call stack)
Phase 2: Auth detection (check for OAuth vs API key)
Phase 3: Provider updates (use OAuth when available)
Phase 4: Testing (verify subscription charges work)
Estimate: 2-4 hours of development
Want this feature? Open an issue:
Title: Support OAuth token passthrough for subscription-based routing
Description:
Enable Lynkr wrap to forward OAuth tokens from Claude Code to Anthropic,
allowing Pro/Max subscription users to benefit from tier routing without
separate API billing.
Benefits:
- 3-5x effective capacity from same subscription
- No separate API costs
- Works with existing Claude Code login
| Feature | Headroom | Lynkr (Current) | Lynkr (Planned) |
|---|---|---|---|
| OAuth passthrough | ✅ | ❌ | 🔄 Planned |
| API key support | ✅ | ✅ | ✅ |
| Mixed auth (OAuth + API) | ❌ | ❌ | ✅ (tier-specific) |
| Subscription billing | ✅ | ❌ | 🔄 Planned |
Your question: "How does it send to anthropic backends via subscription"
Answer:
- Headroom: Wraps Claude Code, preserves OAuth token, forwards to Anthropic → subscription billing works
- Lynkr (current): Uses
.envAPI keys, ignores OAuth → requires separate API billing - Lynkr (planned): Will detect OAuth, forward when available → subscription billing will work
Temporary solution: Use API keys in .env for Anthropic tiers until OAuth passthrough is implemented.
Implementation: Needs header threading + auth detection (~2-4 hours work).
TL;DR: Lynkr doesn't support subscription-based routing yet (it's on the roadmap). For now, use API keys in .env.