openclaw_oauth_sync/docs/ARCHITECTURE.md
sol 0b94cb8fd5 policies: add standard policy files, formatting, and secret scanning
- Add .editorconfig, .prettierrc, .prettierignore, .gitignore
- Add Makefile with fmt, fmt-check, secret-scan, test (skip) targets
- Add package.json with prettier
- Add tools/secret-scan.sh
- Add .secret-scan-allowlist for documentation token format references
- Sanitize documentation to use generic placeholders
- Auto-format with prettier
- make check passes clean
2026-03-01 07:28:54 +00:00

6.7 KiB

Architecture

Token Flow Diagram

+--------------------+     auto-refresh    +------------------------------------------+
| Claude Code CLI    |  ================>  | .credentials.json                        |
| (inside            |  (every ~8 hours,   | {                                        |
|  claude-proxy      |   built-in to CLI)  |   "claudeAiOauth": {                     |
|  container)        |                     |     "accessToken": "<access-token-value>",    |
+--------------------+                     |     "refreshToken": "<refresh-token-value>",   |
                                           |     "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":..., |  | "<token-prefix>" |  +---------+--------+
                    |     "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>/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 "<token-prefix>" 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