Bifurcating Claude API Access: Subscription UI + EC2 Farm-Out for Cost Optimization
This post documents a shell-environment architecture change that separates interactive Claude subscription usage from programmatic API-key access, with automatic farm-out to a cheaper EC2-hosted Claude instance for non-critical workloads.
The Problem: Cost Ceiling and Auth Conflict
The original setup had a single ANTHROPIC_API_KEY exported globally in ~/.zshrc, creating two problems:
- Cost: All API calls—whether interactive or programmatic—consumed the same token budget, with no ability to route expensive operations to cheaper inference tiers.
- Auth conflict: Claude Code (the IDE extension) was receiving conflicting signals: the presence of an API key suppressed the keychain-backed OAuth flow, breaking the intent to use subscription-tier Claude for interactive work.
The constraint: reduce API token spend by 90–95% while preserving reliability for latency-sensitive or high-accuracy workloads.
Architecture: Scoped Credentials and Intelligent Routing
The solution uses environment isolation to create two execution contexts:
- Interactive shell + Claude Code: No global API key. Claude Code's keychain credential (
Claude Code-credentials, accountcb) is used for subscription authentication. - Programmatic scripts: Scripts that require the API key explicitly source
~/repos.env, which exportsANTHROPIC_API_KEYin a subprocess scope only.
The EC2 farm-out box acts as a conditional cheap-inference tier: atomized, error-tolerant tasks (code cleanup, documentation, test generation) run on Haiku; anything requiring GPT-4-level reasoning stays on the subscription plan.
Infrastructure: EC2 Host and Daemon Setup
The farm-out instance is a Lightsail box in us-west-2 with the following characteristics:
- Host:
ubuntu@34.239.233.28(internal IPip-172-26-6-34) - SSH key:
~/.ssh/LightsailDefaultKey-us-west-2.pem(passwordless, verified withssh -o BatchMode=yesprobe) - Claude installation:
/usr/bin/claudepresent and functional - Daemon:
jada-agent.service(systemd unit, active state)
The daemon runs as a service, not in the login shell. This is critical: the farm-out wrapper must inject the API key and model selection per-invocation, not rely on shell environment inheritance.
Implementation: Modifying ~/.zshrc
The key change is removing the global export and deferring credential setup:
# BEFORE (problematic)
export ANTHROPIC_API_KEY="sk-..."
# AFTER (scoped)
# No global ANTHROPIC_API_KEY in shell init.
# Claude Code uses keychain: ~/.claude/settings.json + macOS keychain item "Claude Code-credentials"
# Programmatic access: scripts source ~/repos.env when needed
Why this works: Claude Code doesn't consult ANTHROPIC_API_KEY for authentication in interactive mode; it reads the keychain credential first. Removing the API key from the global environment unblocks that path and forces the subscription OAuth flow.
Scripts that invoke Claude via the API now follow this pattern:
#!/bin/bash
source ~/repos.env
# ANTHROPIC_API_KEY and MODEL now set in subshell scope only
claude "your prompt" --model claude-3-5-haiku-20241022
Farm-Out Wrapper Design
The wrapper function (placed in ~/.zshrc or a sourced completion file) implements the routing logic:
- Input inspection: Analyze the prompt length, keyword heuristics (e.g., "test", "cleanup", "lint"), and error tolerance flags.
- Routing decision: If the task is atomized and low-risk, route to EC2 (Haiku) via SSH; otherwise use local subscription CLI.
- SSH invocation: Execute
ssh -i ~/.ssh/LightsailDefaultKey-us-west-2.pem ubuntu@34.239.233.28 "ANTHROPIC_API_KEY=sk-... claude ..." - Result handling: Stream output back to stdout; capture exit code.
Why EC2 injection instead of environment variables: The Lightsail instance doesn't have the API key baked in (good security practice). Each farm-out call injects it via SSH command string. The daemon on the EC2 box picks it up from the command environment, not from its systemd EnvironmentFile.
Verification and Safety
Before activation, the following checks were performed:
- SSH connectivity:
ssh -o BatchMode=yes ubuntu@34.239.233.28 echo "Connected"(no password prompt; key-based auth confirmed). - Daemon health: Remote systemctl status check showed
jada-agent.serviceactive and running. - Claude availability:
ssh ubuntu@34.239.233.28 /usr/bin/claude --versionreturns version string (no daemon restart needed). - Non-destructive change: The modification to
~/.zshrconly removes a line; the API key remains inrepos.envfor backward compatibility with existing scripts.
Cost Impact and Expectations
Routing 80–90% of workload (code cleanup, test boilerplate, documentation refinement) to Haiku on EC2 should reduce per-token costs by an order of magnitude. Haiku is ~10× cheaper than the Sonnet tier, and EC2 amortizes the per-call overhead across batched invocations.
Critical path work (architecture decisions, complex debugging, code review) stays on the subscription plan, preserving quality where it matters.
What's Next
- Monitoring: Add logging to the wrapper to track farm-out vs. local invocation rates and measure actual cost reduction.
- Feedback loop: Tune the routing heuristics based on success/failure rates of EC2-routed tasks.
- Scaling: If farm-out volume grows, add load-balancing across multiple Lightsail instances or migrate to a containerized inference endpoint.