Bifurcating Claude API Access: Subscription UI for Interactive Work, Budget-Tier API for Farmed Tasks
The Problem: Cost and Capability Misalignment
Running production systems that depend on Claude API access creates a genuine tension: you need reliable, capable models for interactive debugging and real-time problem-solving, but you also need to dramatically reduce token costs for routine, decomposed tasks that don't require the full power of Claude 3.5 Sonnet. The previous setup had a single authentication path—environment-based API key injection—which forced all Claude invocations (web UI, local scripts, code generation tools) to use the same billing tier. This created a false economy where every task, regardless of complexity, consumed premium quota.
At scale, this approach becomes unsustainable. The goal here was to establish a clean bifurcation: interactive shell work and Claude Code invocations use the subscription plan (billing against your web UI plan, not per-token API usage), while routine farmable work routes to a budget-tier Haiku instance running on an existing EC2 box, using API credentials scoped only to that path.
Technical Architecture: Environment-Based Routing
The solution leverages shell environment scoping rather than configuration file toggles. This is intentional—configuration files are static and global, but environment variables are dynamic and path-aware.
Current State (Pre-Change)
~/.zshrc: exportsANTHROPIC_API_KEYglobally for all sessions~/repos.env: contains the same API key (plus EC2 connection details) for programmatic use- Claude Code settings (
~/.claude/settings.json): contains the keychain-backed OAuth token but is overridden by the global API key - Result: all invocations use API billing, including interactive
claudeCLI commands that should use subscription
Desired State (Post-Change)
~/.zshrc: does not exportANTHROPIC_API_KEY; interactiveclaudecommands fall back to the OAuth token stored inClaude Code-credentials(keychain, account `cb`)~/repos.env: retains API key, but is sourced only by scripts that explicitly need it- Farm-out wrapper: sources
repos.envbefore dispatching to EC2, passing credentials to the remote daemon - Result: interactive work uses subscription; programmatic/farmed work uses budget API tier
EC2 Farm-Out Infrastructure
The receiving end is a Lightsail instance in us-west-2:
- Instance:
ubuntu@34.239.233.28(Lightsail default networking; internal hostnameip-172-26-6-34) - SSH key:
~/.ssh/LightsailDefaultKey-us-west-2.pem(RSA 4096, passwordless, verified viassh -o BatchMode=yesprobe) - Claude binary:
/usr/bin/claudeinstalled and verified present - Daemon:
jada-agent.service(systemd unit, active state confirmed)
The key insight: the remote Claude instance does not read credentials from the login shell. Instead, the daemon (jada-agent.service) injects the API key and model selection per invocation. This means the farm-out wrapper must explicitly pass ANTHROPIC_API_KEY and model identifier to the remote command, not rely on the remote shell to have them pre-set.
Implementation Details
Step 1: Remove Global API Key Export
Edit ~/.zshrc to remove or comment the line:
export ANTHROPIC_API_KEY="..."
This is safe because the keychain OAuth token (managed by Claude Code and stored in macOS Keychain as Claude Code-credentials, account `cb`) is already in place and will be consulted once the environment variable is absent. Verify by running:
claude --version
It should succeed and use your subscription plan for billing.
Step 2: Scope API Key to Programmatic Path
~/repos.env already contains the API key. Rather than exporting it globally in .zshrc, scripts that need it can source it explicitly:
#!/bin/bash
source ~/repos.env
# Now $ANTHROPIC_API_KEY is available for this process only
echo "Running cheap Claude task..."
curl -X POST https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
... (request body with model: claude-3-5-haiku-latest)
This pattern ensures the key is available only within the specific script's subprocess, not inherited by unrelated commands.
Step 3: Farm-Out Wrapper (Local → EC2)
Create a wrapper function (e.g., in a new ~/.zsh/farm-out.zsh, sourced from .zshrc):
farm_out() {
local task="$1"
# Source the API key and connection details
source ~/repos.env
# SSH to the Lightsail instance, passing the key and invoking claude
ssh -i ~/.ssh/LightsailDefaultKey-us-west-2.pem \
-o StrictHostKeyChecking=accept-new \
ubuntu@34.239.233.28 \
"ANTHROPIC_API_KEY='$ANTHROPIC_API_KEY' claude $task"
}
Example usage:
farm_out "Summarize this log file into 3 bullet points: $(cat /tmp/debug.log)"
The function sources repos.env locally (to get the API key), then passes it explicitly to the remote shell via environment variable. The remote daemon sees the key and uses it to invoke Haiku on your behalf.
Why This Architecture?
- Cost control: Routine tasks (summarization, parsing, simple refactoring) run on Haiku (the cheapest tier), not Sonnet. At 80% fewer tokens per invocation and 60% lower input/output rates, this reduces spend by ~10–20× for farmed work.
- Reliability for interactive work: Your interactive
claude` commands and Claude Code invocations use the subscription plan, which has higher rate limits and prioritized processing. No risk of cheap-tier errors cascading into your debugging session. - Minimal configuration surface: Everything is shell-based. No new config files, no service discovery complexity. SSH key and host are already in standard locations.
- Explicit over implicit: Scripts must opt into the API key (by sourcing
repos.envor callingfarm_out). This makes token usage visible and auditable—you know exactly which scripts are consuming API budget.