fix: shared Claude OAuth credentials in containers — mount + flock to prevent token rotation race (#693)
- Make ~/.claude volume mount read-write (was :ro) so containers can write back refreshed OAuth tokens - Wrap Claude CLI in flock(1) inside tmux sessions using ~/.claude/session.lock — prevents concurrent token refresh races across agents sharing the same credentials - Add ANTHROPIC_API_KEY detection in entrypoint.sh: when set, skips OAuth entirely (no rotation issues, metered billing) - Log active auth method (API key vs OAuth vs missing) at container startup for easier 401 debugging - Document 'claude auth login' requirement in disinto init output Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
cfdbaeeb5b
commit
cf6400e8f3
3 changed files with 34 additions and 2 deletions
10
bin/disinto
10
bin/disinto
|
|
@ -223,7 +223,7 @@ services:
|
||||||
- agent-data:/home/agent/data
|
- agent-data:/home/agent/data
|
||||||
- project-repos:/home/agent/repos
|
- project-repos:/home/agent/repos
|
||||||
- ./:/home/agent/disinto:ro
|
- ./:/home/agent/disinto:ro
|
||||||
- ${HOME}/.claude:/home/agent/.claude:ro
|
- ${HOME}/.claude:/home/agent/.claude
|
||||||
- ${HOME}/.claude.json:/home/agent/.claude.json:ro
|
- ${HOME}/.claude.json:/home/agent/.claude.json:ro
|
||||||
- CLAUDE_BIN_PLACEHOLDER:/usr/local/bin/claude:ro
|
- CLAUDE_BIN_PLACEHOLDER:/usr/local/bin/claude:ro
|
||||||
environment:
|
environment:
|
||||||
|
|
@ -1517,6 +1517,14 @@ p.write_text(text)
|
||||||
else
|
else
|
||||||
echo " Mode: bare-metal"
|
echo " Mode: bare-metal"
|
||||||
fi
|
fi
|
||||||
|
echo ""
|
||||||
|
echo "── Claude authentication ──────────────────────────────"
|
||||||
|
echo " OAuth (shared across containers):"
|
||||||
|
echo " Run 'claude auth login' on the host once."
|
||||||
|
echo " Credentials in ~/.claude are mounted into containers."
|
||||||
|
echo " API key (alternative — metered billing, no rotation issues):"
|
||||||
|
echo " Set ANTHROPIC_API_KEY in .env to skip OAuth entirely."
|
||||||
|
echo ""
|
||||||
echo " Run 'disinto status' to verify."
|
echo " Run 'disinto status' to verify."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,18 @@ if ! command -v claude &>/dev/null; then
|
||||||
fi
|
fi
|
||||||
log "Claude CLI: $(claude --version 2>&1 || true)"
|
log "Claude CLI: $(claude --version 2>&1 || true)"
|
||||||
|
|
||||||
|
# ANTHROPIC_API_KEY fallback: when set, Claude uses the API key directly
|
||||||
|
# and OAuth token refresh is not needed (no rotation race). Log which
|
||||||
|
# auth method is active so operators can debug 401s.
|
||||||
|
if [ -n "${ANTHROPIC_API_KEY:-}" ]; then
|
||||||
|
log "Auth: ANTHROPIC_API_KEY is set — using API key (no OAuth rotation)"
|
||||||
|
elif [ -f /home/agent/.claude/credentials.json ]; then
|
||||||
|
log "Auth: OAuth credentials mounted from host (~/.claude)"
|
||||||
|
else
|
||||||
|
log "WARNING: No ANTHROPIC_API_KEY and no OAuth credentials found."
|
||||||
|
log "Run 'claude auth login' on the host, or set ANTHROPIC_API_KEY in .env"
|
||||||
|
fi
|
||||||
|
|
||||||
install_project_crons
|
install_project_crons
|
||||||
|
|
||||||
# Configure tea CLI login for forge operations (runs as agent user).
|
# Configure tea CLI login for forge operations (runs as agent user).
|
||||||
|
|
|
||||||
|
|
@ -287,8 +287,20 @@ create_agent_session() {
|
||||||
if [ -n "${CLAUDE_MODEL:-}" ]; then
|
if [ -n "${CLAUDE_MODEL:-}" ]; then
|
||||||
model_flag="--model ${CLAUDE_MODEL}"
|
model_flag="--model ${CLAUDE_MODEL}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Acquire a session-level mutex via flock to prevent concurrent Claude
|
||||||
|
# sessions from racing on OAuth token refresh (rotating the refresh token
|
||||||
|
# invalidates it for other sessions). The lock is held for the lifetime
|
||||||
|
# of the Claude process inside the tmux session.
|
||||||
|
# Use ~/.claude/session.lock so the lock is shared across containers when
|
||||||
|
# the host ~/.claude directory is bind-mounted.
|
||||||
|
local lock_dir="${HOME}/.claude"
|
||||||
|
mkdir -p "$lock_dir"
|
||||||
|
local claude_lock="${lock_dir}/session.lock"
|
||||||
|
local claude_cmd="flock -n ${claude_lock} claude --dangerously-skip-permissions ${model_flag}"
|
||||||
|
|
||||||
tmux new-session -d -s "$session" -c "$workdir" \
|
tmux new-session -d -s "$session" -c "$workdir" \
|
||||||
"claude --dangerously-skip-permissions ${model_flag}" 2>/dev/null
|
"$claude_cmd" 2>/dev/null
|
||||||
sleep 1
|
sleep 1
|
||||||
tmux has-session -t "$session" 2>/dev/null || return 1
|
tmux has-session -t "$session" 2>/dev/null || return 1
|
||||||
agent_wait_for_claude_ready "$session" 120 || return 1
|
agent_wait_for_claude_ready "$session" 120 || return 1
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue