diff --git a/docs/cli/index.md b/docs/cli/index.md
index 8aa28d29f..0c9c52108 100644
--- a/docs/cli/index.md
+++ b/docs/cli/index.md
@@ -166,7 +166,7 @@ Options:
- `--workspace
`
- `--non-interactive`
- `--mode `
-- `--auth-choice `
+- `--auth-choice `
- `--anthropic-api-key `
- `--gateway-port `
- `--gateway-bind `
@@ -202,7 +202,7 @@ Options:
Manage chat provider accounts (WhatsApp/Telegram/Discord/Slack/Signal/iMessage).
Subcommands:
-- `providers list`: show configured chat providers and auth profiles (Claude CLI + Codex CLI sync included).
+- `providers list`: show configured chat providers and auth profiles (Claude Code + Codex CLI OAuth sync included).
- `providers status`: check gateway reachability and provider health (`--probe` to verify credentials; use `status --deep` for local-only probes).
- `providers add`: wizard-style setup when no flags are passed; flags switch to non-interactive mode.
- `providers remove`: disable by default; pass `--delete` to remove config entries without prompts.
@@ -227,6 +227,12 @@ Common options:
- `--no-usage`: skip provider usage/quota snapshots (OAuth/API-backed only).
- `--json`: output JSON (includes usage unless `--no-usage` is set).
+OAuth sync sources:
+- `~/.claude/.credentials.json` → `anthropic:claude-cli`
+- `~/.codex/auth.json` → `openai-codex:codex-cli`
+
+More detail: [/concepts/oauth](/concepts/oauth)
+
Examples:
```bash
clawdbot providers add --provider telegram --account alerts --name "Alerts Bot" --token $TELEGRAM_BOT_TOKEN
diff --git a/docs/concepts/model-failover.md b/docs/concepts/model-failover.md
index cbf5aaf44..b6e660d01 100644
--- a/docs/concepts/model-failover.md
+++ b/docs/concepts/model-failover.md
@@ -21,6 +21,8 @@ Clawdbot uses **auth profiles** for both API keys and OAuth tokens.
- Config `auth.profiles` / `auth.order` are **metadata + routing only** (no secrets).
- Legacy import-only OAuth file: `~/.clawdbot/credentials/oauth.json` (imported into `auth-profiles.json` on first use).
+More detail: [/concepts/oauth](/concepts/oauth)
+
Credential types:
- `type: "api_key"` → `{ provider, key }`
- `type: "oauth"` → `{ provider, access, refresh, expires, email? }` (+ `projectId`/`enterpriseUrl` for some providers)
diff --git a/docs/concepts/oauth.md b/docs/concepts/oauth.md
new file mode 100644
index 000000000..633e0d61d
--- /dev/null
+++ b/docs/concepts/oauth.md
@@ -0,0 +1,133 @@
+---
+summary: "OAuth in Clawdbot: token exchange, storage, CLI sync, and multi-account patterns"
+read_when:
+ - You want to understand Clawdbot OAuth end-to-end
+ - You hit token invalidation / logout issues
+ - You want to reuse Claude Code / Codex CLI OAuth tokens
+ - You want multiple accounts or profile routing
+---
+# OAuth
+
+Clawdbot supports “subscription auth” via OAuth for providers that offer it (notably **Anthropic (Claude Pro/Max)** and **OpenAI Codex (ChatGPT OAuth)**). This page explains:
+
+- how the OAuth **token exchange** works (PKCE)
+- where tokens are **stored** (and why)
+- how we **reuse external CLI tokens** (Claude Code / Codex CLI)
+- how to handle **multiple accounts** (profiles + per-session overrides)
+
+## The token sink (why it exists)
+
+OAuth providers commonly mint a **new refresh token** during login/refresh flows. Some providers (or OAuth clients) can invalidate older refresh tokens when a new one is issued for the same user/app.
+
+Practical symptom:
+- you log in via Clawdbot *and* via Claude Code / Codex CLI → one of them randomly gets “logged out” later
+
+To reduce that, Clawdbot treats `auth-profiles.json` as a **token sink**:
+- the runtime reads credentials from **one place**
+- we can **sync in** credentials from external CLIs instead of doing a second login
+- we can keep multiple profiles and route them deterministically
+
+## Storage (where tokens live)
+
+Secrets are stored **per-agent**:
+
+- Auth profiles (OAuth + API keys): `~/.clawdbot/agents//agent/auth-profiles.json`
+- Runtime cache (managed automatically; don’t edit): `~/.clawdbot/agents//agent/auth.json`
+
+Legacy import-only file (still supported, but not the main store):
+- `~/.clawdbot/credentials/oauth.json` (imported into `auth-profiles.json` on first use)
+
+All of the above also respect `$CLAWDBOT_STATE_DIR` (state dir override). Full reference: [/gateway/configuration](/gateway/configuration#auth-storage-oauth--api-keys)
+
+## Reusing Claude Code / Codex CLI OAuth tokens (recommended)
+
+If you already signed in with the external CLIs *on the gateway host*, Clawdbot can reuse those tokens without starting a separate OAuth flow:
+
+- Claude Code: reads `~/.claude/.credentials.json` → profile `anthropic:claude-cli`
+- Codex CLI: reads `~/.codex/auth.json` → profile `openai-codex:codex-cli`
+
+Sync happens when Clawdbot loads the auth store (so it stays up-to-date when the CLIs refresh tokens).
+
+How to verify:
+
+```bash
+clawdbot providers list
+```
+
+Or JSON:
+
+```bash
+clawdbot providers list --json
+```
+
+## OAuth exchange (how login works)
+
+Clawdbot’s interactive login flows are implemented in `@mariozechner/pi-ai` and wired into the wizards/commands.
+
+### Anthropic (Claude Pro/Max)
+
+Flow shape (PKCE):
+
+1) generate PKCE verifier/challenge
+2) open `https://claude.ai/oauth/authorize?...`
+3) user pastes `code#state`
+4) exchange at `https://console.anthropic.com/v1/oauth/token`
+5) store `{ access, refresh, expires }` under an auth profile
+
+The wizard path is `clawdbot onboard` → auth choice `oauth` (Anthropic).
+
+### OpenAI Codex (ChatGPT OAuth)
+
+Flow shape (PKCE):
+
+1) generate PKCE verifier/challenge + random `state`
+2) open `https://auth.openai.com/oauth/authorize?...`
+3) try to capture callback on `http://127.0.0.1:1455/auth/callback`
+4) if callback can’t bind (or you’re remote/headless), paste the redirect URL/code
+5) exchange at `https://auth.openai.com/oauth/token`
+6) extract `accountId` from the access token and store `{ access, refresh, expires, accountId }`
+
+Wizard path is `clawdbot onboard` → auth choice `openai-codex` (or `codex-cli` to reuse an existing Codex CLI login).
+
+## Refresh + expiry
+
+Profiles store an `expires` timestamp.
+
+At runtime:
+- if `expires` is in the future → use the stored access token
+- if expired → refresh (under a file lock) and overwrite the stored credentials
+
+See implementation: `src/agents/auth-profiles.ts`.
+
+## Multiple accounts (profiles) + routing
+
+Two patterns:
+
+### 1) Preferred: separate agents
+
+If you want “personal” and “work” to never interact, use isolated agents (separate sessions + credentials + workspace):
+
+```bash
+clawdbot agents add work
+clawdbot agents add personal
+```
+
+Then configure auth per-agent (wizard) and route chats to the right agent.
+
+### 2) Advanced: multiple profiles in one agent
+
+`auth-profiles.json` supports multiple profile IDs for the same provider.
+
+Pick which profile is used:
+- globally via config ordering (`auth.order`)
+- per-session via `/model ...@`
+
+Example (session override):
+- `/model Opus@anthropic:work`
+
+How to see what profile IDs exist:
+- `clawdbot providers list --json` (shows `auth[]`)
+
+Related docs:
+- [/concepts/model-failover](/concepts/model-failover) (rotation + cooldown rules)
+- [/tools/slash-commands](/tools/slash-commands) (command surface)
diff --git a/docs/docs.json b/docs/docs.json
index 8728ef949..c9068b8b4 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -508,6 +508,10 @@
{
"source": "/wizard",
"destination": "/start/wizard"
+ },
+ {
+ "source": "/oauth",
+ "destination": "/concepts/oauth"
}
],
"navigation": {
@@ -544,6 +548,7 @@
"concepts/agent",
"concepts/agent-loop",
"concepts/system-prompt",
+ "concepts/oauth",
"concepts/agent-workspace",
"concepts/multi-agent",
"concepts/compaction",
diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md
index 334bd4de3..cc0ebc641 100644
--- a/docs/gateway/configuration.md
+++ b/docs/gateway/configuration.md
@@ -116,6 +116,8 @@ Env var equivalent:
Clawdbot stores **per-agent** auth profiles (OAuth + API keys) in:
- `/auth-profiles.json` (default: `~/.clawdbot/agents//agent/auth-profiles.json`)
+See also: [/concepts/oauth](/concepts/oauth)
+
Legacy OAuth imports:
- `~/.clawdbot/credentials/oauth.json` (or `$CLAWDBOT_STATE_DIR/credentials/oauth.json`)
@@ -131,6 +133,10 @@ Overrides:
On first use, Clawdbot imports `oauth.json` entries into `auth-profiles.json`.
+Clawdbot also auto-syncs OAuth tokens from external CLIs into `auth-profiles.json` (when present on the gateway host):
+- `~/.claude/.credentials.json` (Claude Code) → `anthropic:claude-cli`
+- `~/.codex/auth.json` (Codex CLI) → `openai-codex:codex-cli`
+
### `auth`
Optional metadata for auth profiles. This does **not** store secrets; it maps
diff --git a/docs/start/faq.md b/docs/start/faq.md
index 8088230f3..67540700f 100644
--- a/docs/start/faq.md
+++ b/docs/start/faq.md
@@ -339,6 +339,8 @@ Fix: either provide Google auth, or remove/avoid Google models in `agent.model.f
## Auth profiles: what they are and how to manage them
+Related: [/concepts/oauth](/concepts/oauth) (OAuth flows, token storage, multi-account patterns, CLI sync)
+
### What is an auth profile?
An auth profile is a named credential record (OAuth or API key) tied to a provider. Profiles live in:
diff --git a/docs/start/hubs.md b/docs/start/hubs.md
index bd0af2226..dc6d27644 100644
--- a/docs/start/hubs.md
+++ b/docs/start/hubs.md
@@ -54,6 +54,7 @@ Use these hubs to discover every page, including deep dives and reference docs t
- [Groups](https://docs.clawd.bot/concepts/groups)
- [Group messages](https://docs.clawd.bot/concepts/group-messages)
- [Model failover](https://docs.clawd.bot/concepts/model-failover)
+- [OAuth](https://docs.clawd.bot/concepts/oauth)
## Providers + ingress
diff --git a/docs/start/onboarding.md b/docs/start/onboarding.md
index e724ff08e..4ed664933 100644
--- a/docs/start/onboarding.md
+++ b/docs/start/onboarding.md
@@ -32,6 +32,8 @@ Implementation note (2025-12-19): in local mode, the macOS app bundles the Gatew
This is the “bind Clawdbot to subscription auth” step. It is explicitly the **Anthropic (Claude Pro/Max)** or **OpenAI (ChatGPT/Codex)** OAuth flow, not a generic “login”.
+More detail: [/concepts/oauth](/concepts/oauth)
+
### Recommended: OAuth (Anthropic)
The macOS app should:
@@ -165,15 +167,29 @@ On the gateway host, create `~/.clawdbot/credentials/oauth.json` with this exact
Set permissions: `chmod 600 ~/.clawdbot/credentials/oauth.json`
-**Note:** Clawdbot auto-imports from legacy pi-coding-agent paths (`~/.pi/agent/oauth.json`, etc.) but this does NOT work with Claude Code credentials — different file and format.
+**Note:** Clawdbot can import from legacy pi-coding-agent paths (`~/.pi/agent/oauth.json`, etc.), but Claude Code/Codex CLI credentials live in different files.
-### Using Claude Code credentials
+### Using Claude Code + Codex CLI credentials (direct)
-If Claude Code is installed on the gateway host, convert its credentials:
+If these CLIs are installed on the **gateway host** and you’ve already signed in, Clawdbot auto-syncs their OAuth tokens into the per-agent auth profile store (`~/.clawdbot/agents//agent/auth-profiles.json`) on load:
+
+- **Claude Code**: reads `~/.claude/.credentials.json` → profile `anthropic:claude-cli`
+- **Codex CLI**: reads `~/.codex/auth.json` → profile `openai-codex:codex-cli`
+
+Verification:
+
+```bash
+clawdbot providers list
+```
+
+### Fallback: convert Claude Code credentials into `oauth.json`
+
+If you don’t want to install Claude Code on the gateway host, you can still seed the legacy import file:
```bash
cat ~/.claude/.credentials.json | jq '{
anthropic: {
+ type: "oauth",
access: .claudeAiOauth.accessToken,
refresh: .claudeAiOauth.refreshToken,
expires: .claudeAiOauth.expiresAt
@@ -182,12 +198,6 @@ cat ~/.claude/.credentials.json | jq '{
chmod 600 ~/.clawdbot/credentials/oauth.json
```
-| Claude Code field | Clawdbot field |
-|-------------------|---------------|
-| `accessToken` | `access` |
-| `refreshToken` | `refresh` |
-| `expiresAt` | `expires` |
-
## Workspace backup (recommended)
We suggest creating a **private GitHub repository** to back up the agent
diff --git a/docs/start/wizard.md b/docs/start/wizard.md
index b20d01a13..ee12100a0 100644
--- a/docs/start/wizard.md
+++ b/docs/start/wizard.md
@@ -56,7 +56,9 @@ Tip: `--json` does **not** imply non-interactive mode. Use `--non-interactive` (
- Full reset (also removes workspace)
2) **Model/Auth**
+ - **Anthropic OAuth (Claude CLI)**: if `~/.claude/.credentials.json` exists, the wizard can reuse it.
- **Anthropic OAuth (recommended)**: browser flow; paste the `code#state`.
+ - **OpenAI Codex OAuth (Codex CLI)**: if `~/.codex/auth.json` exists, the wizard can reuse it.
- **OpenAI Codex OAuth**: browser flow; paste the `code#state`.
- Sets `agent.model` to `openai-codex/gpt-5.2` when model is unset or `openai/*`.
- **API key**: stores the key for you.
@@ -64,6 +66,7 @@ Tip: `--json` does **not** imply non-interactive mode. Use `--non-interactive` (
- **Skip**: no auth configured yet.
- Wizard runs a model check and warns if the configured model is unknown or missing auth.
- OAuth credentials live in `~/.clawdbot/credentials/oauth.json`; auth profiles live in `~/.clawdbot/agents//agent/auth-profiles.json` (API keys + OAuth).
+ - More detail: [/concepts/oauth](/concepts/oauth)
3) **Workspace**
- Default `~/clawd` (configurable).
diff --git a/src/cli/program.ts b/src/cli/program.ts
index 218646766..8c09a5758 100644
--- a/src/cli/program.ts
+++ b/src/cli/program.ts
@@ -232,7 +232,7 @@ export function buildProgram() {
.option("--mode ", "Wizard mode: local|remote")
.option(
"--auth-choice ",
- "Auth: oauth|openai-codex|antigravity|apiKey|minimax|skip",
+ "Auth: oauth|claude-cli|openai-codex|codex-cli|antigravity|apiKey|minimax|skip",
)
.option("--anthropic-api-key ", "Anthropic API key")
.option("--gateway-port ", "Gateway port")
@@ -259,7 +259,9 @@ export function buildProgram() {
mode: opts.mode as "local" | "remote" | undefined,
authChoice: opts.authChoice as
| "oauth"
+ | "claude-cli"
| "openai-codex"
+ | "codex-cli"
| "antigravity"
| "apiKey"
| "minimax"