Non-obvious core fact: claude setup-token (Claude Code v2.1.x) uses code=true OAuth. After Authorize, the browser lands on https://platform.claude.com/oauth/code/callback?code=<C>&state=<S>. The string the CLI's "Paste code here if prompted >" expects is <C>#<S> — the authorization code AND the state, joined by a literal #. The callback PAGE body spells it out: "Paste this into Claude Code: <C>#<S>". Delivering only the URL code= param (without #<S>) fails with "OAuth error: Invalid code. Please make sure the full code was copied" — the "full code" wording is literal: it means code+#+state. This cost ~6 wasted Authorize cycles on 2026-05-16 before reading the callback page body instead of just parsing the URL query.
Delivery mechanism that works (box / headless):
- Run setup-token inside a tmux session (real PTY):
tmux new-session -d -s skyauth -x 400 -y 60 "bash -lc 'claude setup-token; echo SETUP_EXIT=$?; sleep 86400'"(wide -x 400 so the OAuth URL prints unwrapped). - Inject the answer with
tmux send-keys -t skyauth -l -- '<C>#<S>'then a separatetmux send-keys -t skyauth Enter. tmux send-keys delivers the full string into the Ink TUI intact. Ascript -qfc ... < fifoPTY path does NOT reliably deliver (Ink dropped the burst silently — confirmed dead end). - Watcher must be state-gated: only consume a callback tab whose
state== the expected state (stale prior-round callback tabs otherwise get grabbed and burn cycles). - Open the authorize URL via cloud_bridge
open_tab(FULL_URL)(Playwright). NEVERcurl http://127.0.0.1:9222/json/new?<url>for a multi-param URL — it truncates at the first&, producing?code=trueonly → "Invalid OAuth Request: Missing client_id" error page. - Retry ("Press Enter to retry") REUSES the same code_challenge/state, so the consent URL stays valid across a retry. A previously-rejected code is NOT consumed if the rejection was the malformed half-code (server rejects without burning the grant) — you can deliver the correct
code#statefor the same code after pressing Enter.
Linux /tmp gotchas hit along the way: fs.protected_fifos/fs.protected_regular block root doing O_CREAT on a skyrun-owned file/FIFO in sticky /tmp — rm as root first then recreate, or use os.open(path, os.O_WRONLY) (no O_CREAT). pkill -f oauth_watch run inline inside an ssh command whose own string contains "oauth_watch" self-kills the ssh shell (exit 255) — always put such pkills in a script file.
Box Max auth (installed 2026-05-16): token created (1-yr) and stored on the cloud box at ~/.config/skyrun/max_oauth_token (chmod 600, skyrun) + sourceable ~/.config/skyrun/max_env.sh (export CLAUDE_CODE_OAUTH_TOKEN=...). Headless verified: CLAUDE_CODE_OAUTH_TOKEN=$(cat ~/.config/skyrun/max_oauth_token) claude -p "..." returns answers → box runs Claude Code headlessly on Joseph's Max 20x plan, no per-token API bill. setup-token does NOT write ~/.claude/.credentials.json — it only prints the token once; capture it from the tmux pane on SETUP_EXIT=0. Renew before the 1-yr expiry (~2027-05-16) using this same procedure.