Bifurcating Claude API Authentication: Subscription CLI + Farm-Out Architecture for Cost Optimization
We recently restructured our Claude authentication layer to support two distinct execution paths: interactive subscription-based CLI work and programmatic API-key-driven farm-out tasks on EC2. This post details the technical decisions, implementation, and the infrastructure that now allows us to reduce API spend by an order of magnitude while maintaining reliability where it matters.
The Problem: API Cost Runaway
Our previous architecture exported a single ANTHROPIC_API_KEY globally in the shell environment, which meant every invocation of Claude—whether interactive development work, scheduled tasks, or delegated farm-out jobs—consumed the same expensive token pool. This created two problems:
- Cost collision: Interactive work and programmatic automation competed for the same billing envelope, making it impossible to cap experimental spend.
- Reliability mismatch: We couldn't afford to use cheaper models (Haiku) for atomic, well-scoped tasks without risking interactive work falling back to an underpowered endpoint.
At ~$1,500/month on the API token plan, we needed to drop to 1/10th or 1/20th of that spend. The leverage point was recognizing that not all work requires the subscription tier.
Technical Architecture: Two Paths
Path 1: Interactive CLI (Subscription)
The claude command invoked interactively should use the subscription billing plan. This work is exploratory, requires strong reasoning, and the user is actively waiting. The authentication mechanism here is the OAuth keychain token already stored by Claude Code:
- Location:
~/.claude/credentials/Claude Code-credentials(managed by the Claude client) - Trigger: When
ANTHROPIC_API_KEYis absent from the interactive shell, the Claude CLI automatically falls back to keychain-stored OAuth credentials. - Why this works: The keychain credential is tied to the user's
claude.aisubscription account (accountcbin this case) and carries the subscription billing context automatically.
Path 2: Programmatic Farm-Out (API Key + Haiku)
Automated tasks—lead responders, digest generation, daemon-driven workflows—should use a dedicated API key scoped to cheap models. These jobs are:
- Broken down into small, well-defined tasks before delegation.
- Fault-tolerant (failures can be retried with degraded output).
- Frequently stateless or have bounded context windows.
The API key lives in a secure, repo-specific dotenv file instead of the global shell:
- File:
/Users/cb/Documents/repos/repos.env(git-ignored, never committed) - Convention: Programmatic scripts and farm-out wrappers explicitly
source repos.envbefore invoking Claude. - Model constraint: Scripts using this path default to
claude-3-5-haiku-latestunless overridden per-task.
Implementation Details
Shell Configuration Changes
The ~/.zshrc file was modified to remove the global ANTHROPIC_API_KEY export while preserving all other environment setup:
# BEFORE (problematic):
export ANTHROPIC_API_KEY="sk-ant-..." # ← Causes CLI to ignore keychain
# AFTER (fixed):
# ANTHROPIC_API_KEY is NOT exported in .zshrc
# Interactive 'claude' command now uses keychain OAuth automatically
# Programmatic work sources repos.env on-demand
The .zshrc syntax was validated with zsh -n ~/.zshrc to ensure no breakage in the interactive shell. A utility function burst-valve was added to safely test the farm-out path without exposing credentials in command history.
Programmatic Script Changes
Two automation scripts were updated to source the API key only when needed:
gmb_lead_responder.py: Responds to Google My Business leads. Sourcesrepos.envat runtime and uses the Anthropic Python SDK with explicit model selection.carole_digest.py: Aggregates email and generates daily digests. Similar pattern: sources env, uses Haiku for text summarization tasks.
Both scripts were compile-checked and dry-run against live IMAP/Gmail credentials to confirm the credential loading mechanism works without breaking existing functionality.
EC2 Farm-Out Infrastructure
The EC2 Lightsail instance at 34.239.233.28 (internal hostname ip-172-26-6-34ubuntu) serves as the farm-out node for delegating work. The infrastructure details:
- SSH access: Passwordless key-based auth using
~/.ssh/LightsailDefaultKey-us-west-2.pem. Connection verified withssh -o BatchMode=yesprobe. - Claude daemon:
/usr/bin/claudebinary present and running under systemd unitjada-agent.service(status: active). - Auth injection: The daemon does not read
ANTHROPIC_API_KEYfrom the login shell. Instead, API credentials are passed per-invocation by the farm-out wrapper. This prevents credential leakage and allows model/key rotation without daemon restart. - EnvironmentFile pattern: Daemon configuration sources an
EnvironmentFilefor Fourthwall secrets and other deployment context, decoupling configuration from code.
Farm-Out Wrapper Design
A wrapper function (not yet committed, but tested) handles routing:
# Pseudo-code structure:
function farm_to_cheap_claude() {
local task="$1"
# Only invoke cheap claude if task is atomic and well-scoped
ssh ubuntu@34.239.233.28 \
"ANTHROPIC_API_KEY= claude "
}
# Usage in main scripts:
if is_atomized_task; then
farm_to_cheap_claude "$work"
else
claude # Uses subscription (interactive path)
fi
This pattern ensures that the EC2 daemon receives credentials per-call and can enforce model constraints without affecting the local interactive environment.
Key Design Decisions
- Why not use
forceLoginMethod? This setting is enterprise-only and silently ignored in usersettings.json. The real lever is shell environment state. - Why keep the API key in
repos.env? It's already the convention for sensitive deployment variables. Scripts that need it explicitly opt in by sourcing the file, reducing the attack surface compared to a global export. - Why Haiku for farm-out? For atomized tasks (single-turn classification, summarization, routing), Haiku's cost-to-performance ratio is optimal.