# Architecture ## Token Flow Diagram ``` +--------------------+ auto-refresh +------------------------------------------+ | Claude Code CLI | ================> | .credentials.json | | (inside | (every ~8 hours, | { | | claude-proxy | built-in to CLI) | "claudeAiOauth": { | | container) | | "accessToken": "sk-ant-oat01-...", | +--------------------+ | "refreshToken": "sk-ant-ort01-...", | | "expiresAt": 1772120060006 | | } | | } | +-------------------+-----------------------+ | inotifywait detects CLOSE_WRITE / MOVED_TO | +-------------------v-----------------------+ | sync-oauth-token.sh | | (systemd service, runs continuously) | +---+----------------+----------------+-----+ | | | +----------------+ +-----------+---------+ | | | | | +---------v---------+ +--------v--------+ +--------v--------+ | oauth.json | | .env | | docker compose | | { | | ANTHROPIC_ | | down/up gateway | | "anthropic": { | | OAUTH_TOKEN= | | (reloads env) | | "access":..., | | "sk-ant-oat01-" | +---------+--------+ | "refresh":...,| +-----------------+ | | "expires":... | +----------v----------+ | } | | OpenClaw Gateway | | } | | (fresh token loaded | +--------+----------+ | from container env) | | +----------+----------+ | mergeOAuthFileIntoStore() | | (reads on startup) | +-------------------->+ | | +--------v---------+ +------------->| api.anthropic.com| | Claude Opus 4.6 | +------------------+ ``` ## Volume Mounts (Docker) ``` HOST PATH CONTAINER PATH ========= ============== Gateway container (openclaw-openclaw-gateway-1): /root/.openclaw/ -> /home/node/.openclaw/ /root/.openclaw/credentials/oauth.json -> /home/node/.openclaw/credentials/oauth.json /root/.openclaw/agents/*/agent/auth-profiles.json -> /home/node/.openclaw/agents/*/agent/auth-profiles.json /home/node/.claude/ -> /home/node/.claude/ /root/openclaw/.env -> loaded as container env vars (at creation time only) Claude CLI container (claude-proxy): /root/.openclaw/workspaces/workspace-claude-proxy/ config/ -> /root/ config/.claude/.credentials.json -> /root/.claude/.credentials.json ``` ## Auth Resolution Order (inside gateway) When the gateway needs to authenticate with Anthropic: ``` 1. resolveApiKeyForProvider("anthropic") 2. -> resolveAuthProfileOrder() 3. -> reads agents//agent/auth-profiles.json 4. -> isValidProfile() checks each profile: 5. - type:"api_key" -> requires cred.key 6. - type:"oauth" -> requires cred.access (NOT cred.key!) 7. - type:"token" -> requires cred.token 8. -> If valid profile found: use it 9. -> If no valid profile: resolveEnvApiKey("anthropic") 10. -> Reads ANTHROPIC_OAUTH_TOKEN from container env 11. -> isOAuthToken(key) detects "sk-ant-oat" prefix 12. -> Uses Bearer auth + Claude Code identity headers 13. -> Sends request to api.anthropic.com On gateway startup: mergeOAuthFileIntoStore() -> Reads /home/node/.openclaw/credentials/oauth.json -> Merges into auth profile store (if profile doesn't exist) ``` ## Why down/up and NOT restart ``` docker compose restart openclaw-gateway -> Sends SIGTERM to container process -> Restarts the SAME container (same env vars from creation time) -> .env changes are NOT reloaded -> Result: gateway still has OLD token docker compose down openclaw-gateway && docker compose up -d openclaw-gateway -> Stops and REMOVES the container -> Creates a NEW container (reads .env fresh) -> New env vars are loaded -> Result: gateway has NEW token ``` ## Source Code References (inside gateway container) | File | Line | Function | |------|------|----------| | `/app/dist/paths-CyR9Pa1R.js` | 190 | `OAUTH_FILENAME = "oauth.json"` | | `/app/dist/paths-CyR9Pa1R.js` | 198-204 | `resolveOAuthDir()` -> `$STATE_DIR/credentials/` | | `/app/dist/paths-CyR9Pa1R.js` | 203 | `resolveOAuthPath()` -> joins dir + filename | | `/app/dist/model-auth-CmUeBbp-.js` | 3048 | `mergeOAuthFileIntoStore()` -- reads oauth.json | | `/app/dist/model-auth-CmUeBbp-.js` | 3358 | `buildOAuthApiKey()` -- returns `credentials.access` | | `/app/dist/model-auth-CmUeBbp-.js` | 3832 | `isValidProfile()` -- for oauth, checks `cred.access` | | `/app/dist/model-auth-CmUeBbp-.js` | 3942 | `resolveApiKeyForProvider()` -- profiles then env fallback | | `/app/dist/model-auth-CmUeBbp-.js` | 4023 | `resolveEnvApiKey("anthropic")` -> reads env var |