Bifurcating Claude API Access: Subscription UI + Cheap EC2 Farm-Out for Cost Control
Managing Claude API costs at scale requires hard choices. When monthly spend approaches $1500 and you need to cut it by 90%, the engineering solution isn't to just use cheaper models everywhere — it's to route the right work to the right Claude. This post documents how we separated interactive subscription-based Claude access from programmatic API-key-based farm-out to a cost-optimized EC2 instance.
The Problem: API Key Conflicts in Shell Environment
The core issue was credential collision. With ANTHROPIC_API_KEY exported globally in ~/.zshrc, the claude CLI would always prefer the API key over the claude.ai subscription token stored in ~/.claude/settings.json. This meant:
- Interactive commands burned expensive API credits instead of using the subscription plan.
- No way to gate "cheap Claude on EC2" separately without manual credential swaps.
- Programmatic scripts couldn't reliably farm out work without explicit routing logic.
The initial instinct — setting forceLoginMethod in ~/.claude/settings.json — doesn't work. That key is enterprise-only and silently ignored in user configs.
Technical Approach: Shell Environment Scoping, Not Settings.json
The fix leverages shell environment precedence, not configuration files:
- Interactive use: Remove
ANTHROPIC_API_KEYfrom the global shell environment (specifically~/.zshrc). The claude CLI will then fall back to the keychain-stored OAuth token under theClaude Code-credentialsentry (accountcb), which automatically gates subscription billing. - Programmatic/farm-out use: Keep the API key in
repos.envand source it only where needed, following your existing convention.
This is a pure shell-environment strategy. The keychain OAuth token is untouched and always available once the API key is removed from the interactive shell context.
EC2 Farm-Out Infrastructure (Verified)
Before implementing the routing, we verified the target farm-out environment:
Host: ubuntu@34.239.233.28 (AWS Lightsail, us-west-2)
Internal IP: ip-172-26-6-34
SSH Key: ~/.ssh/LightsailDefaultKey-us-west-2.pem
Claude Binary: /usr/bin/claude (present and functional)
Service: jada-agent.service (systemd, active)
Auth Method: API key/model injected per-invocation by the daemon
The key insight: the EC2 Claude instance doesn't store API credentials in its login shell. Instead, the jada-agent.service daemon accepts credentials per-invocation. This means farm-out wrappers must pass API key and model selection themselves, rather than relying on inherited shell state.
SSH Configuration for Farm-Out Wrapper
The connection uses BatchMode to ensure passwordless operation:
# In ~/.ssh/config (or injected via wrapper)
Host ec2-cheapclaude
HostName 34.239.233.28
User ubuntu
IdentityFile ~/.ssh/LightsailDefaultKey-us-west-2.pem
BatchMode yes
StrictHostKeyChecking accept-new
SSH key probing confirmed the connection works without interactive authentication. This is critical for unattended farm-out invocations from CI/CD or background scripts.
Credential Storage Reorganization
Before:
# ~/.zshrc
export ANTHROPIC_API_KEY="sk-ant-..." # Global, always active
After:
# ~/.zshrc
# ANTHROPIC_API_KEY removed from interactive environment
# In repos.env (sourced by specific scripts only)
export ANTHROPIC_API_KEY="sk-ant-..."
export ANTHROPIC_MODEL="claude-3-5-haiku-20241022" # Cheap default
Scripts that need the API key explicitly source repos.env:
#!/bin/bash
source ~/path/to/repos.env
# Now ANTHROPIC_API_KEY and ANTHROPIC_MODEL are in scope
python my_script.py
Farm-Out Wrapper Pattern
For work that should run on cheap Claude, the pattern is:
#!/bin/bash
# farm-out-claude: wrap a task for EC2 execution
source ~/repos.env
TASK_PAYLOAD="$@"
CHEAP_MODEL="claude-3-5-haiku-20241022"
ssh ec2-cheapclaude \
"ANTHROPIC_API_KEY='$ANTHROPIC_API_KEY' \
ANTHROPIC_MODEL='$CHEAP_MODEL' \
claude $TASK_PAYLOAD"
This invocation:
- Preserves interactive
claudecalls for subscription use (no API key in shell). - Routes atomized, low-risk subtasks to Haiku (1/10th the cost per token).
- Keeps EC2 credentials scoped to the wrapper, not leaked into global env.
Why This Architecture
Cost Control: Haiku on EC2 handles routine parsing, summarization, and structured output. Subscription Claude (Opus or Sonnet via claude.ai) is reserved for interactive, complex reasoning where the marginal cost is justified.
Credential Hygiene: API keys never sit in interactive shell history. They're sourced only where needed, reducing attack surface for shell injection or history leaks.
Billing Clarity: Subscription tokens and API keys are now spatially separated. You can audit which systems consume which credentials without grepping dotfiles.
Graceful Degradation: If the EC2 instance is down, interactive claude still works. If you exhaust API quota, interactive use is unaffected.
Implementation Checklist
- Remove
export ANTHROPIC_API_KEY=...from~/.zshrc. - Verify keychain entry
Claude Code-credentials(accountcb) exists. - Test interactive
claudelogin — should prompt for claude.ai auth if keychain is empty. - Confirm
repos.envcontains API key and model defaults. - Create farm-out wrapper script with SSH tunnel logic.
- Test
ssh ec2-cheapclaude "ANTHROPIC_API_KEY=... claude --version"to verify connectivity. - Update CI/CD pipelines to source
repos.envbefore invoking Claude programmatically.
What's Next
Once this is stable, the next phase is automating farm-out decisions. A heuristic router could examine task complexity (token count, instruction depth, reasoning requirements) and automatically decide subscription vs. cheap routes without manual wrapping.
We could also instrument the EC2