Provider sign-in

Run models from OAuth subscription accounts (Claude Code, Codex, Copilot, Gemini CLI…) or free-tier endpoints: the two sign-in flows, remote use, token refresh and storage, binding accounts to models, and troubleshooting.

Provider Sign-in (Settings → Provider Sign-in, in both the Web UI and the desktop app) runs SenClaw's models from two kinds of source:

  1. Subscription accounts — OAuth sign-in with a subscription you already pay for (Claude Code, OpenAI Codex, Antigravity, GitHub Copilot, Qwen, Kimi, Grok, iFlow, Gemini CLI). No API key involved.
  2. Free-tier providers — a catalog of endpoints with a free allowance (Google AI Studio, NVIDIA NIM, …). Each needs its own API key unless marked otherwise.

⚠️ Read this first — it is the banner at the top of the page. Subscription credentials are licensed by the vendor for their own client. Using them from SenClaw can get the account suspended, and vendors can detect it. SenClaw deliberately identifies itself honestly instead of impersonating a vendor's client — a provider that blocks third parties fails with a clear error rather than breaking silently. For anything that matters, use an API key. Each provider card also carries its own ⚠ icon — hover it for that provider's specific risk.

The subscription providers

ProviderSign-in kindNotes
Claude CodeBrowser redirect
OpenAI CodexBrowser redirectNeeds port 1455 free — OpenAI registers exactly one loopback redirect. A busy port fails the moment you press Connect
AntigravityBrowser redirect1M context. The first chat discovers the Code Assist project id
GitHub CopilotDevice codeWorks remotely / over SSH
Qwen CodeDevice codeWorks remotely / over SSH
Kimi for CodingDevice codeWorks remotely / over SSH
Grok CLIDevice codeWorks remotely / over SSH
iFlowBrowser redirect
Gemini CLIBrowser redirect1M context

The two sign-in kinds:

  • Browser redirect (auth code + PKCE): the daemon opens a temporary listener on 127.0.0.1; the browser finishes the sign-in and redirects the code back to it. Fastest — but it only works when the browser and the daemon are on the same machine.
  • Device code: you are shown a code, you open the vendor's verification page yourself and type it in. No listener → works when the Web UI is opened from another machine, or over SSH.

Browser-redirect sign-in

  1. Press Connect on the provider card. A browser tab opens (toast: "Finish the sign-in in the browser tab that just opened.").
  2. Sign in to the vendor account and approve (Allow / Authorize).
  3. The browser shows "Returning to SenClaw to finish connecting… You can close this tab."this is not the success signal yet; SenClaw still validates and exchanges the code behind the scenes. The real outcome shows up in SenClaw (the UI polls every 2 seconds).
  4. Success → the account appears in the Connected accounts table.

Notes:

  • A 5-minute timeout — if you wander off mid-flow, press Connect again and start over.
  • Pressing Deny on the vendor page → an error toast "…denied the sign-in".

Device-code sign-in

  1. Press Connect → a modal shows a large code with Copy code and Open page buttons.
  2. Open the verification page (some providers pre-fill the code in the URL), enter the code, approve.
  3. SenClaw detects the vendor's confirmation by itself and closes the modal; the account appears in the table.

Notes: the code expires (5 minutes at most). Closing the modal only hides the UI — press Connect again to restart. After expiry you get "the device code expired — start the sign-in again".

Remote use (Web UI on a different machine than the daemon)

Browser redirect cannot work remotely: the vendor page redirects to http://localhost:<port> — which is the machine running the browser, not the daemon. The daemon-side listener never receives the code and the flow hangs until the timeout. This is a deliberate safety property: the listener binds strictly to loopback, and there is no knob to open it to the LAN — doing so would let anyone on the network race for the authorization code.

→ Remotely, use the four Device code providers: GitHub Copilot, Qwen, Kimi, Grok.

After connecting — the Connected accounts table

One row per account:

  • Label + email (the email only when the provider returns one — see Add another below).
  • A token-expiry pill: 45m left / 2h left… — orange under 10 minutes, Expired when gone, No expiry reported when the provider says nothing.
  • The **No auto-refresh** tag: the provider issued no refresh token — once expired, sign in again by hand (the Refresh button is disabled).
  • The **Needs attention** tag (red): the vendor rejected a refresh — hover to read the error; usually Disconnect and Connect again.

The buttons on each account:

ButtonEffect
Use as modelCreate a model configuration backed by this account (next section)
RefreshRefresh the token now (disabled when there is no refresh token)
DisconnectForget the stored token. Warning: every model bound to the account stops working until you reconnect
Connect / Add anotherSign in one more account on the same provider

Tokens refresh themselves: the daemon checks every 60 seconds and refreshes ahead of expiry; if a token still expires mid-flight, the 401 triggers one refresh-and-retry. You normally never press Refresh.

Add another: providers that return no email (Claude, Copilot, Kimi, iFlow, Gemini CLI) create a new row on every sign-in (there is nothing to dedup on) — if you end up with duplicates, Disconnect the spares.

Running a model on a connected account

  1. Press Use as model on the account → SenClaw fetches the account's model list (asked from the vendor directly; when the vendor publishes none, a static list with a note). Pick a model → Add model.
  2. A new LLM configuration appears under Settings → LLM, labelled "{Provider} — {model}". The first one becomes active automatically. Assign it as Main / Quick / Cognitive there.
  3. The model list is a menu, not a promise — your plan may list a dozen models and actually serve a few. Test this model (or Test all N) runs a real completion ("Reply with the single word: ok") so you know for sure. Tests spend real quota; Test all runs sequentially to dodge rate limits.

A model configuration contains no token — it references the account id. Tokens live elsewhere (see Where tokens live, below).

Free-tier providers

The lower half of the page: "Ready-made endpoints with a free allowance. Each needs its own API key unless marked otherwise."

ProviderNotes
Google AI StudioA generous Gemini free tier; the key comes from AI Studio, no billing required
BazaarLinkAn aggregator with an auto:free route
Kilo GatewayFree tier includes Nemotron and Kat Coder :free models
NVIDIA NIMDeveloper credits on build.nvidia.com
KimchiFree Kimi and MiniMax families
BytePlus ArkSeed 2.0 coding models; free quota on the coding endpoint
LLM7A free relay of many frontier models
API AirforceA small relay; tight rate limits
PoolsidePoolside's Laguna models
Cloudflare Workers AIBadge **needs accountId** — asks for your Cloudflare account id (the first field of the Add modal; substituted into the endpoint URL)
Xiaomi MiMo (open)Badge **No key** — an open endpoint, no credential at all; best-effort availability

The actions:

  • Get key — opens the vendor's API-key page in a new tab.
  • Add — a modal for the API key (plus the accountId when required) and a model choice → Add model creates an ordinary API-key LLM configuration under Settings → LLM (no OAuth involved). Assign Main/Quick/Cognitive as usual.

Where tokens live — safety

  • OAuth tokens sit in **~/.senclaw/oauth.json, file mode 0600**, written atomically.
  • Tokens are **deliberately not in config.json** — GET /api/llm-config returns config.json verbatim and the daemon runs with loose CORS; every /api/oauth/* endpoint returns a redacted view that never contains a token.
  • Moving machines / backups: config.json holds only an oauthAccountId — bring oauth.json along too (keep it 0600), or every OAuth model dies on the new machine.
  • Deleting oauth.json forgets every account (without an error) — sign in again from scratch.
  • The client ids/secrets of some providers (Antigravity, iFlow, Gemini CLI) are the vendor's "public" installed-app kind — they are not your secrets.

Troubleshooting

SymptomCause / fix
Codex: "port 1455 is required … already in use"Another process is listening on 1455. Close it and press Connect again (the error fires immediately, before any sign-in effort)
"sign-in timed out after 300s"The flow took more than 5 minutes — press Connect again
The "Returning to SenClaw…" page appeared, but no account shows upRead the error toast in SenClaw: a state mismatch (just retry), or the token exchange failed
The Connect button spins foreverThe daemon restarted mid-flow (flow state is RAM-only) — reload the page and Connect again
Needs attention — "sign in again — refresh rejected"The vendor revoked the refresh token (invalid_grant…). Disconnect, then Connect again
Expired plus No auto-refreshThe provider issues no refresh token — sign in again by hand
Windows: the sign-in finished but SenClaw never got the codelocalhost on Windows prefers ::1; another app squatting on ::1:<port> swallows the code. SenClaw binds both stacks where it can — if it still bites, close the other app and retry
Antigravity/Gemini: 403 PERMISSION_DENIED / CONSUMER_INVALIDThe cached project id was rejected by the vendor — SenClaw clears the cache on its own; retry the request
A model is listed but errors when calledEntitlements are per-account — the list is a menu. Use Test this model to find the ones that really serve

REST API (quick reference)

GET    /api/oauth/providers                  # the provider registry (riskNotice, models)
GET    /api/oauth/accounts                   # connected accounts (redacted)
POST   /api/oauth/:provider/start            # begin a flow → {flowId, authorizeUrl, kind, userCode?}
GET    /api/oauth/flows/:id                  # poll: pending | awaiting_user_code | completed | failed
POST   /api/oauth/accounts/:id/refresh       # manual refresh (502 on failure)
DELETE /api/oauth/accounts/:id               # disconnect
GET    /api/oauth/accounts/:id/models        # the model list (discovered | registry)
POST   /api/oauth/test-model                 # {accountId, modelName} → a real probe completion
POST   /api/oauth/bind                       # {accountId, modelName, label?} → an LlmConfig with auth "oauth"
GET    /api/provider-catalog                 # the free-tier catalog

In the SenClaw repo

The provider registry and default model lists are src/providers/oauth/provider.rs, the flows and the callback listener src/providers/oauth/flow.rs, the token store src/providers/oauth/store.rs, the free-tier catalog src/providers/mod.rs, REST src/gateway/ui_server/oauth.rs, the web UI web/src/components/settings/OAuthSettings.tsx and the desktop section desktop_app/lib/features/settings/provider_signin_section.dart. Adding a provider is one new const in the registry — the registry tests cover it automatically.