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_KEY from the global shell environment (specifically ~/.zshrc). The claude CLI will then fall back to the keychain-stored OAuth token under the Claude Code-credentials entry (account cb), which automatically gates subscription billing.
  • Programmatic/farm-out use: Keep the API key in repos.env and 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 claude calls 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 (account cb) exists.
  • Test interactive claude login — should prompt for claude.ai auth if keychain is empty.
  • Confirm repos.env contains 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.env before 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