Skip to content

Pass a long-term Claude token to sandboxed Claude on a descriptor (#504) - #505

Merged
barelyworkingcode merged 3 commits into
mainfrom
feat/504-claude-token-fd
Oct 19, 2026
Merged

barelyworkingcode merged 3 commits into
mainfrom
feat/504-claude-token-fd

Conversation

@barelyworkingcode

Copy link
Copy Markdown
Owner

Closes #504

Relay can now hand a Claude long-term OAuth token to the sandboxed Claude it starts, so those sessions start logged in with no /login and the token never rides in an environment block.

  • New setting claude_token_file (top level of settings.json, absolute or ~/), empty by default. Empty: every launch is unchanged.
  • Relay reads the file at each sandboxed Claude launch and resume (AuthorizeLaunch) and sends the bytes to relay-sessions in the launch request's claude_token field. relay-sessions writes them into a pipe, closes the write end, and the shim (--pass-token) hands the read end to Claude on fd 5, with CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR=5 set after the env scrub. fd 3 and 4 are the shim's identity and status pipes, so 5 is the first number free in every launch shape.
  • Covers provider claude sessions and sandboxed claude-code terminals. Unsandboxed and SSH host launches never get the token; relay-sessions refuses one there.
  • Every session sandbox profile denies the token path, because the seeded shell template grants ~ read-write.
  • A missing, unreadable, empty, oversized (>4096 bytes) or non-regular file refuses the launch with 400 sandbox_unavailable; the message names the path, never the content.
  • Settings > Overview shows Claude token file: <path> when it is set. relay status --json also carries the path (same view). No presence gate: the setting changes only by editing settings.json.

Limit: the Claude process that holds the token, and the tools it runs, can read it.

What you'll notice: with claude_token_file set, sandboxed Claude sessions start logged in; the post-#503 /login step goes away.

Docs: docs/session-host.md, docs/routes.md, docs/FEATURES.md row G1.45, docs/THREAT-MODEL.md, docs/architecture.md, docs/testing.md.

Roll back: revert this PR's merge commit. Sandboxed Claude then needs the /login again.

Fix attempts: 1 (plus one comment/doc round from review). Tokens: about 0.8M across subagents.

Run report: barelyworkingcode/claude-process#57

Review: no blocking findings (default model, round 1; three comment/doc minors fixed in c775c35)
Break-and-restore: dropped the token-path deny in sandboxSpecForLaunch; TestClaudeTokenFileDeniedToSession (g1_claude_token_fd_test.go:345) and TestClaudeTokenFileDeniedToShellTerminal (:380) went red with exit 0; restored byte for byte (scratch worktree discarded)

🤖 Generated with Claude Code

barelyworkingcode and others added 3 commits October 19, 2026 13:32
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@barelyworkingcode

Copy link
Copy Markdown
Owner Author

Verified: c775c35 · repo-suite: ok relaye2e/features 504.474s · devboxverify: SUMMARY pass=5 fail=0 blocked=0 notrun=2 (stack r504, api) · real-app: two sandboxed claude-code auth status launches in Acme Corp both loggedIn true, authMethod oauth_token, no /login; token in no relay log, session file or settings · devboxverify-screen: session-agent-state PASS, session-drop-in PASS, session-drop-in-tool-refused PASS, chief-of-staff-send PASS, cos-start PASS (SUMMARY pass=60 fail=4; the 4 fails are SSH-host journeys: the test machine's plain claude over ssh reports loggedIn false, environment, not this diff) · log: /tmp/r504-suite.log, /tmp/r504-stack-api.log, /tmp/r504-screen.log on the test machine

@barelyworkingcode

Copy link
Copy Markdown
Owner Author

Real-app check: Relay.app built from c775c35 with claude_token_file set; two sandboxed claude-code auth status launches both loggedIn true, authMethod oauth_token; an interactive relay sandbox claude-code started on the Claude API with no /login; before (main, relay#503): the sandbox folder needed a manual /login
Journey: no new journey; the five existing sandboxed-Claude screen journeys (session-agent-state, session-drop-in, session-drop-in-tool-refused, chief-of-staff-send, cos-start) are this feature's simple path and PASS on the head

@barelyworkingcode

Copy link
Copy Markdown
Owner Author

Weakening reviewed: t.Skip in TestClaudeTokenUnreadableFileRefused fires only when the process can open a mode-000 file (root); a mode-000 file cannot prove an unreadable refusal there; non-root runs, which every suite run is, always assert

@barelyworkingcode
barelyworkingcode merged commit c203624 into main Oct 19, 2026
@barelyworkingcode
barelyworkingcode deleted the feat/504-claude-token-fd branch October 19, 2026 21:05
@barelyworkingcode

Copy link
Copy Markdown
Owner Author

Merged: merge-check passed approved, target, verified (c775c35), contract, evidence, owner, weakening (reviewed); ci NA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Give sandboxed Claude sessions a long-term token on a file descriptor

1 participant