Description
Replace the current Claude Code injection path (setup-claude.sh + inject_claude_auth in devc-remote.sh) with a slim, robust forwarding mechanism. Depends on #545 (Claude Code baked into image + IS_SANDBOX=1).
Problem Statement
PR #166 ships two pieces for in-container Claude Code:
assets/workspace/.devcontainer/scripts/setup-claude.sh (~200 lines) — installs Node.js via apt with clock-skew workarounds, npm install -g @anthropic-ai/claude-code, creates a non-root claude user, ACLs the workspace, wraps claude so root invocations runuser into that user.
scripts/devc-remote.sh:386-440 inject_claude_auth — reads CLAUDE_CODE_OAUTH_TOKEN from env or macOS Keychain, then sed-edits docker-compose.local.yaml with several heuristic branches (grep -q 'services: {}', grep -q 'environment:', etc.) to inject the env var.
Both have problems:
Proposed Solution
A ~50-line replacement that:
- Drops
setup-claude.sh entirely. Image bake handles install; IS_SANDBOX=1 env handles uid check; no separate user needed.
- Drops sed-on-YAML in
inject_claude_auth. Uses one of:
compose exec -e CLAUDE_CODE_OAUTH_TOKEN=$value ... — per-exec env injection, no compose file edits at all
- bind-mount of
~/.claude/.credentials.json (file or via per-user dir) — auth state synced from host, container reads it directly
- Sources the token from a defined chain:
--account <slot> flag → ~/.claude-creds-mac/<slot>/cred (claude-switch slot)
~/.claude-creds-mac/.active → that slot
- macOS Keychain
Claude Code-credentials/$USER → live (auto-refreshed) value
- Linux
~/.claude/.credentials.json
- Warns when the credential's
expiresAt is in the past so users know to refresh before deployment fails inside the container with HTTP 401.
Out of Scope
Changelog Category
Changed (replaces existing flow)
Description
Replace the current Claude Code injection path (
setup-claude.sh+inject_claude_authindevc-remote.sh) with a slim, robust forwarding mechanism. Depends on #545 (Claude Code baked into image +IS_SANDBOX=1).Problem Statement
PR #166 ships two pieces for in-container Claude Code:
assets/workspace/.devcontainer/scripts/setup-claude.sh(~200 lines) — installs Node.js via apt with clock-skew workarounds,npm install -g @anthropic-ai/claude-code, creates a non-rootclaudeuser, ACLs the workspace, wrapsclaudeso root invocationsrunuserinto that user.scripts/devc-remote.sh:386-440inject_claude_auth— readsCLAUDE_CODE_OAUTH_TOKENfrom env or macOS Keychain, then sed-editsdocker-compose.local.yamlwith several heuristic branches (grep -q 'services: {}',grep -q 'environment:', etc.) to inject the env var.Both have problems:
claudeuser + runuser wrapper + ACLs is ~80 lines of complexity to dodge the uid-0 refusal.IS_SANDBOX=1([FEATURE] Bake agent-CLI toolkit + Claude Code into image (rg/fd/bat/eza/delta/lazygit/zoxide/starship/freeze/expect/nvim + IS_SANDBOX=1) #545) makes all of this unnecessary.greppatterns; anydocker-compose.local.yamlthe user has customized in unexpected ways will get mangled. No round-trip-safe parser.security find-generic-password -s devc-remote -a CLAUDE_CODE_OAUTH_TOKEN) duplicatesclaude-switchslot logic. A user managing multiple Anthropic accounts viaclaude-switchhas no way to pick which one ships to the container.Proposed Solution
A ~50-line replacement that:
setup-claude.shentirely. Image bake handles install;IS_SANDBOX=1env handles uid check; no separate user needed.inject_claude_auth. Uses one of:compose exec -e CLAUDE_CODE_OAUTH_TOKEN=$value ...— per-exec env injection, no compose file edits at all~/.claude/.credentials.json(file or via per-user dir) — auth state synced from host, container reads it directly--account <slot>flag →~/.claude-creds-mac/<slot>/cred(claude-switch slot)~/.claude-creds-mac/.active→ that slotClaude Code-credentials/$USER→ live (auto-refreshed) value~/.claude/.credentials.jsonexpiresAtis in the past so users know to refresh before deployment fails inside the container with HTTP 401.Out of Scope
devc-remote.shbroader refactor (separate, larger issue)Changelog Category
Changed (replaces existing flow)