Bifurcating Claude API Authentication: Subscription UI for Interactive Work, Cost-Optimized Haiku for Programmatic Farming

Problem Statement

API token costs for Claude have become unsustainable—reaching $1500/month—while interactive development work via claude.ai requires a subscription token. The core conflict: environment variable collision between ANTHROPIC_API_KEY and OAuth-based subscription authentication means choosing one globally breaks the other. This post documents the mechanism used to achieve simultaneous support for both authentication paths within a single development environment, with programmatic work automatically farming out to a cost-optimized Claude instance running on EC2.

What Was Done

The solution implements shell-level auth scoping rather than application-level configuration, enabling:

  • Interactive claude invocations use the subscription token (OAuth via keychain, stored as Claude Code-credentials with account cb)
  • Programmatic/API work sources ~/repos.env to inject ANTHROPIC_API_KEY only where needed
  • Farm-out wrapper detects complexity and delegates to a Lightsail-hosted Claude instance running Haiku (cheaper, acceptable error rates for atomic subtasks)
  • No application settings modified; no destructive changes to existing shell configuration

Technical Details: Authentication Mechanics

The critical insight is that ~/.claude/settings.json does not have a forceLoginMethod key that affects consumer-grade Claude Code (that's managed/enterprise-only and silently ignored). Instead, Claude Code's auth decision is purely environment-driven:

  • If ANTHROPIC_API_KEY is set: uses API key authentication (Claude backend, billing to account)
  • If ANTHROPIC_API_KEY is absent: falls back to keychain OAuth token (Claude Code-credentials, account cb), which is automatically populated by the subscription login flow

The previous global export in ~/.zshrc:

export ANTHROPIC_API_KEY="sk-ant-..."

…was present in the interactive shell, shadowing the keychain token and forcing all interactive claude invocations to consume API quota. The fix removes this global export entirely.

Scoped Key Injection for Programmatic Work

Existing scripts and CI workflows already follow the pattern of sourcing ~/repos.env for environment setup. This file contains:

export ANTHROPIC_API_KEY="sk-ant-..."
export DEV_TUNNEL_URL="..."
export DEV_AGENT_SECRET="..."

Scripts that require API key access now explicitly source this file:

#!/bin/bash
source ~/repos.env
claude some-complex-task

This ensures the key is only in scope where needed, not polluting the interactive shell.

Infrastructure: EC2 Farm-Out Node

A Lightsail instance serves as the cost-optimized Claude provider:

  • Host: ubuntu@34.239.233.28 (Lightsail; internal IP ip-172-26-6-34)
  • SSH Key: ~/.ssh/LightsailDefaultKey-us-west-2.pem (passwordless, verified with BatchMode SSH probe)
  • Claude Installation: /usr/bin/claude present and functional
  • Service: jada-agent.service (active systemd unit) manages the daemon

Authentication on the Farm-Out Box

Critically, the EC2 instance does not store API credentials in the login shell. Instead, jada-agent.service (the systemd service managing Claude) injects credentials per-invocation. This means:

  • SSH into the box and spawn a shell: credentials are not present in that interactive session
  • The farm-out wrapper (running locally) must explicitly pass ANTHROPIC_API_KEY and model selection when invoking remote Claude over SSH
  • This prevents accidental credential leakage and keeps the EC2 box stateless with respect to secrets

A farm-out wrapper invokes the remote Claude like this (pseudocode; no real secrets):

ssh -i ~/.ssh/LightsailDefaultKey-us-west-2.pem ubuntu@34.239.233.28 \
  "ANTHROPIC_API_KEY='${API_KEY}' /usr/bin/claude --model claude-3-5-haiku-20241022 '${PROMPT}'"

Key Architectural Decisions

1. Why Shell-Level Scoping, Not Application Config?

Claude Code's authentication layer is environment-first. Attempting to override it via ~/.claude/settings.json (e.g., with a hypothetical forceLoginMethod key) would either:

  • Have no effect (ignored by consumer builds)
  • Require patching the application itself

Shell environment variables are the intended control surface and require zero application changes.

2. Why Keep the API Key in repos.env Instead of a Dedicated Secret Manager?

The key was already there, following the project's existing convention. Moving it to AWS Secrets Manager or HashiCorp Vault would introduce:

  • Additional CLI dependencies (aws-cli, vault)
  • Latency on every script invocation (API call to fetch)
  • More complex audit trails

For a developer machine with physical access controls, file-based storage with strict permissions (600) is acceptable. CI/CD systems would benefit from secret injection, but that's out of scope for this interactive+farm-out setup.

3. Why Haiku on EC2 Instead of Opus or Sonnet?

Cost optimization. Haiku is ~90% cheaper than Opus. The use case assumes work is only farmed out after being "atomized"—broken into small, well-defined subtasks that a simpler model can execute reliably. Interactive work (which requires higher accuracy and context awareness) stays on the subscription plan. This creates a two-tier system:

  • Subscription (interactive): full Claude intelligence, no marginal cost per token
  • API Haiku (farmed): ~1/20th the cost, acceptable for low-complexity tasks

Verification and Testing

Before finalizing, the EC2 farm-out node was verified via:

  • SSH connectivity: BatchMode probe using the Lightsail private key succeeded without interactive auth
  • Claude binary: confirmed present at /usr/bin/claude and executable
  • Service status: jada-agent.service is active and running