From b8629fce11b58f6083fd2444db4cc0547dfa79b3 Mon Sep 17 00:00:00 2001 From: electronicBlacksmith Date: Sun, 5 Apr 2026 04:00:25 +0000 Subject: [PATCH] fix(docker): bootstrap Claude auth for containerized phantom The entrypoint never installed authentication credentials, so every container recreate booted without them and phantom responded with "please /login" to any Slack message. Two paths: 1. Preferred: CLAUDE_CODE_OAUTH_TOKEN in .env, generated once via `claude setup-token` inside the container. This is a long-lived (1 year) token with its own session, so it never rotates and will never conflict with a Claude Code session running on the host. Skips the credentials mount entirely. 2. Fallback: docker-compose.override.yml can bind-mount the host's ~/.claude/.credentials.json to /tmp/.credentials-mount.json. On boot we install it into /home/phantom/.claude/ with 600 perms and start a background loop that re-copies when the host file changes, so host-side token refreshes propagate without a restart. WARNING in the entrypoint comment: this path shares a rotating OAuth session between the host and the container, which the auth backend rejects as concurrent use and which will eventually cause the CLI to delete its own credentials to force re-login. Only safe when nothing else on the host is running Claude Code. Auth priority is token env var > credentials mount > neither (the latter prints a message that ANTHROPIC_API_KEY is expected). --- scripts/docker-entrypoint.sh | 39 +++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh index bf60400..5a5d380 100755 --- a/scripts/docker-entrypoint.sh +++ b/scripts/docker-entrypoint.sh @@ -78,6 +78,43 @@ fi # 5. Set Docker awareness flag export PHANTOM_DOCKER=true -# 6. Start Phantom (exec replaces shell so signals reach Bun directly) +# 6. Claude Max OAuth credentials (optional) +# +# Preferred path: set CLAUDE_CODE_OAUTH_TOKEN in .env (generated once via +# `claude setup-token` inside the container). It is a long-lived token with +# its own session, does not rotate, and will not conflict with any other +# Claude Code sessions on the host or elsewhere. +# +# Fallback path: docker-compose.override.yml can bind-mount the host's +# ~/.claude/.credentials.json read-only to /tmp/.credentials-mount.json, and +# we copy it into place + keep it in sync via a background loop. WARNING: +# this shares a rotating OAuth session between the host and the container, +# which Anthropic's auth backend will reject as concurrent use. Only use the +# mount fallback on machines where nothing else is running Claude Code. +CLAUDE_CRED_SRC="/tmp/.credentials-mount.json" +CLAUDE_CRED_DST="/home/phantom/.claude/.credentials.json" +if [ -n "${CLAUDE_CODE_OAUTH_TOKEN:-}" ]; then + echo "[phantom] Using CLAUDE_CODE_OAUTH_TOKEN from environment (skipping credentials mount)" +elif [ -f "$CLAUDE_CRED_SRC" ]; then + echo "[phantom] Installing Claude Max credentials from mount..." + mkdir -p "$(dirname "$CLAUDE_CRED_DST")" + cp "$CLAUDE_CRED_SRC" "$CLAUDE_CRED_DST" + chmod 600 "$CLAUDE_CRED_DST" + + ( + while sleep 60; do + if [ -f "$CLAUDE_CRED_SRC" ] && ! cmp -s "$CLAUDE_CRED_SRC" "$CLAUDE_CRED_DST" 2>/dev/null; then + if cp "$CLAUDE_CRED_SRC" "$CLAUDE_CRED_DST" 2>/dev/null; then + chmod 600 "$CLAUDE_CRED_DST" 2>/dev/null || true + echo "[phantom] Claude credentials refreshed from mount" + fi + fi + done + ) & +else + echo "[phantom] No Claude Max credentials mount found at $CLAUDE_CRED_SRC (relying on ANTHROPIC_API_KEY)" +fi + +# 7. Start Phantom (exec replaces shell so signals reach Bun directly) echo "[phantom] Starting Phantom..." exec bun run src/index.ts