Auth & Org Membership
Octopool has four auth surfaces: caller auth for relay traffic, admin auth for provisioning, the GitHub-CLI login exchange that mints caller tokens, and website sessions for /dashboard. All of them are pinned to a single allowed GitHub org (ALLOWED_GITHUB_ORG, openclaw).
Source: src/auth.ts, src/callers.ts, src/web-session.ts, src/provisioning.ts, src/router.ts.
#Caller auth
Relay and health requests send Authorization: Bearer <octopool_caller_token>.
- The token is hashed (SHA-256, base64url) and matched against
callers.token_hash. Raw tokens are never stored. - The caller must be
activeand granted the requested pool (caller_pools). - The caller's
org_loginmust equalALLOWED_GITHUB_ORG, else403 org_denied. - Org membership is re-verified on use once it goes stale (
ORG_VERIFY_TTL_SECONDS, default 24h), using the org verifier token. A member who leaves the org loses access at the next check.
A missing/invalid token returns 401; a valid token without the pool grant returns 401 invalid_auth.
#Admin auth
Admin endpoints (/v1/admin/...) require Authorization: Bearer <OCTOPOOL_ADMIN_TOKEN>. The comparison is constant-time. If no admin token is configured, admin endpoints return 503 admin_unconfigured. Admin auth is entirely separate from caller auth — ordinary callers can never reach admin routes.
#Website session auth
The browser dashboard uses GitHub OAuth and an opaque cookie session:
/login/githubredirects to GitHub withread:org,allow_signup=false, and a short-lived signed state mirrored in anHttpOnlystate cookie. The state payload only carries issue time, nonce, and the sanitized dashboard return path./login/github/callbackexchanges the code with GitHub, resolves the user, verifies OpenClaw membership with the configured org verifier token, and creates or refreshes the caller grant for the default login pool by immutable GitHub user id.- A random
octopool_sessioncookie is set (HttpOnly,Secure,SameSite=Lax). Only its SHA-256 hash is stored inweb_sessions; the raw session token is never stored. /dashboardand/v1/dashboardrequire a valid session, a pool grant, anddashboard_role = 'admin'.
Non-admin org members may be valid Octopool callers, but they cannot see pool-wide operator data.
#GitHub-CLI login exchange
POST /v1/login/github-cli turns a local GitHub token into an Octopool caller token. This is what octopool login calls.
Flow:
- The CLI discovers the server with
GET /.well-known/octopool, then chooses the discoveredapi_baseanddefault_poolunless flags override them. - Body carries
github_token(the user'sgh auth token) and an optionalpool. - The Worker resolves the GitHub user (
GET /user) and verifies that user is a member ofALLOWED_GITHUB_ORGusing the supplied token. - The caller row and requested default-pool grant are created or refreshed by immutable GitHub user id, org, active status, and pool.
- A new caller token (
op_…) is generated, hashed, and stored; the row is refreshed with the current login, user id, and verification time. - The plaintext token is returned once, for the CLI to store locally.
#Pool restriction
octopool login cannot self-grant an arbitrary pool. The requested pool must equal DEFAULT_LOGIN_POOL (default maintainers); anything else is 403 pool_denied. Binding by user id, not the mutable login, keeps later username changes attached to the same caller row.
#Org verification tokens
Two helpers check GET /orgs/{org}/members/{login}:
- During login, with the user's own token.
- During background freshness checks, with the configured
OCTOPOOL_GITHUB_ORG_TOKEN.
204 confirms membership; 404 denies (403 org_member_denied); anything else surfaces as 502 org_verification_failed. If the verifier token is unset, verification returns 503 org_verification_unavailable.