# Claude Focus Sandbox Auth The `cf --sandbox` workflow creates a Firecracker microVM via slicer, provisions it with Claude Code, and launches Claude inside it. **By default, Claude Code has no credentials inside the VM.** This document explains how to configure a long-lived API token so Claude launches authenticated automatically on every provisioned VM. > [!info] One-time setup, automatic after that > Run `cfauth setup` once to store a token in macOS Keychain. Every subsequent > `cf --sandbox` session will inject that token during provisioning — no manual > login needed inside the VM. ## Why Auth Forwarding Is Needed `cf --sandbox` targets a Linux Firecracker VM. The host machine uses corporate auth (`api_key_helper`, `ANTHROPIC_BASE_URL`, macOS Keychain entry `Claude Code-credentials`). None of these mechanisms are portable to a Linux VM. The solution is a **long-lived personal API token** stored once on the host and injected automatically during provisioning. > [!tip] Native slicer alternative > If token management becomes cumbersome, the entire auth path can be replaced with > slicer's built-in agent support: > ```bash > slicer claude . --credentials-file ~/.claude/.credentials.json > ``` > This uses slicer's native auth forwarding and eliminates the need for manual token > management. See [slicer docs](https://docs.slicervm.com/mac/coding-agents/). ## One-Time Setup ### Step 1: Generate a long-lived token ```bash claude setup-token ``` Follow the interactive flow. Claude Code will guide you through generating a token at claude.ai and storing it locally. ### Step 2: Store the token for `cf` to read ```bash cfauth setup ``` This prompts you to paste the token and stores it in macOS Keychain under `service=cf-claude-token, account=$USER`. The token is never written to disk in plaintext. ## Token Source Lookup Order When provisioning a slicer VM, `cf` tries these sources in order: | Priority | Source | Details | |----------|--------|---------| | 1 | `CF_CLAUDE_API_KEY` env var | Explicit override — highest priority | | 2 | macOS Keychain | `security find-generic-password -s "cf-claude-token"` | | 3 | Token file | `~/.config/cf/claude-token` — must be mode **600** | | 4 | Not found | Warning printed; provisioning continues without auth | If no token is found, Claude launches unauthenticated inside the VM and will prompt for login interactively on first use. This is a **soft failure** — it does not abort the session. ## Per-VM Provisioning Flow Auth injection is phase 3d of the provisioning pipeline: ``` 3a. Bootstrap (uid 0) — install node, tmux, claude 3b. Dotfiles (uid 1000) — nvim, stow, gh, dotfiles 3c. gh auth (uid 1000) — gh auth login --with-token 3d. Claude auth (uid 1000) — NEW: inject ANTHROPIC_API_KEY into ~/.bashrc ← this doc 4. Launch (uid 1000) — slicer vm shell ... claude ``` The token is injected into the VM's `~/.bashrc` inside a marker block: ```bash # cf-claude-auth export ANTHROPIC_API_KEY='...' export ANTHROPIC_BASE_URL='...' # only if set on host export ANTHROPIC_CUSTOM_HEADERS='...' # only if set on host # end-cf-claude-auth ``` Injection is **idempotent**: re-provisioning the same VM replaces the existing block rather than appending a duplicate. The token is passed via stdin pipe (not env vars or command arguments) to prevent it from appearing in `ps aux` output. ## `cfauth` Subcommand Reference ``` cfauth [setup|reroll|status|clear] ``` | Command | Action | |---------|--------| | `cfauth setup` | Prompt for token, store in macOS Keychain | | `cfauth reroll` | Revoke old token (prompts), then prompt for new one | | `cfauth status` | Show which source is currently active | | `cfauth clear` | Remove token from macOS Keychain | ### Examples ```bash # Check current auth source cfauth status # → cf auth: source=keychain:cf-claude-token # Rotate a token (revoke old one at claude.ai/settings first) cfauth reroll # Remove the stored token cfauth clear # → cf auth: keychain token cleared ``` > [!warning] Revoking before rerolling > Run `cfauth reroll` (not `cfauth clear` + `cfauth setup`) to rotate a token. > The `reroll` command reminds you to revoke the old token at claude.ai/settings > before storing the new one, preventing stale token confusion. ## What Gets Forwarded | Variable | Condition | |----------|-----------| | `ANTHROPIC_API_KEY` | Always forwarded (from token lookup) | | `ANTHROPIC_BASE_URL` | Forwarded if set on host (corporate proxy) | | `ANTHROPIC_CUSTOM_HEADERS` | Forwarded if set on host (corporate auth headers) | This means the VM uses the same API endpoint and auth headers as the host machine — important on corporate networks with a custom proxy. ## Troubleshooting ### `cf auth: no Claude API token found` No token is configured. Run `cfauth setup` to store one. ### Claude prompts for login inside the VM Auth injection failed silently during provisioning, or the token has expired. Check `cfauth status` on the host, then re-inject manually: ```bash # Inspect what's in the VM's .bashrc slicer vm exec <vm-hostname> --uid 1000 -- grep -A5 'cf-claude-auth' ~/.bashrc # Re-inject manually (slicer vm exec reads from stdin) cfauth status # confirm token exists on host first ``` ### Token file has unsafe permissions ``` cf: token file ~/.config/cf/claude-token has unsafe permissions (644) — expected 600, skipping ``` Fix with: ```bash chmod 600 ~/.config/cf/claude-token ``` ### ANTHROPIC_BASE_URL not forwarded to VM The variable must be set in the **host shell** at the time `cf --sandbox` is run. If using a shell config file (e.g. `~/.zshrc.local`), ensure it exports the var before launching `cf`. ### Auth works locally but not in remote slicer VMs Remote VM auth (`--remote` with a slicer backend) uses the same injection path via SSH. Confirm the remote host can reach the Claude API endpoint: ```bash ssh <remote-host> 'curl -s https://api.anthropic.com/healthz' ``` If you're behind a corporate proxy, `ANTHROPIC_BASE_URL` must be set on the host so it gets forwarded during provisioning.