Security
Local API keys, JWT cloud sessions, encrypted override keys, and least-privilege agent tokens.
The platform separates two trust boundaries: the control plane (which holds LLM keys and is hosted by the platform team) and the runtime (which you host and which never holds LLM keys). Each layer has its own auth.
Authentication layers
| Surface | Auth method | Credentials |
|---|---|---|
| Dashboard | Better Auth session (W3 ID OIDC) | Your IBM W3 ID |
| CLI / device | OAuth 2.0 Device Flow (RFC 8628) | Access + refresh JWTs |
| Runtime → control plane | JWT Bearer tokens | Bootstrap-token exchange, rotated |
| Your app → runtime (local) | API key | BOT_AGENT_API_KEY (local only) |
Local runtime API key
Access to the runtime's local HTTP API (http://localhost:4484) is gated by an
API key you set via --api-key / BOT_AGENT_API_KEY. Requests may present it in
any of three ways:
Authorization: Bearer <key>X-API-Key: <key>?api_key=<key>(also used in WebSocket URLs)
Always set this in production. The key never leaves the host — it is not sent to the control plane.
Cloud session (runtime ↔ control plane)
The runtime authenticates to the control plane using JWT tokens obtained by exchanging a single-use bootstrap token (issued from the dashboard, expires in 5 minutes).
| Token | Validity | Storage |
|---|---|---|
| Access token | 30 minutes | Stateless JWT |
| Refresh token | 30 days | JWT + SHA-256 hash in DB |
| Bootstrap token | 5 minutes | Single-use, stored in DB |
- Refresh token rotation — each refresh issues a new pair and revokes the old refresh token.
- Tokens are signed with HS256 using
BETTER_AUTH_SECRET. - Refresh tokens are hashed at rest (SHA-256); they are never stored in plaintext.
Bring-your-own LLM keys (overrides)
Users may register their own LLM provider API keys for a specific
(conversationId, modelId) pair. When a matching chat request arrives, the
control plane uses the user's key and bypasses the project request limit
entirely. Override usage is tracked separately and does not count toward quotas.
These keys are treated as compromised-by-default:
- Encryption at rest: AES-256-GCM with a random salt and IV per encryption.
- Key derivation: scrypt, derived from
BETTER_AUTH_SECRET. - Display masking: the full key is never returned in list views (shows a
masked prefix + suffix like
sk-...xyz123). - Access control: project API key or project
write/adminrole required.
Never logged
Override keys are decrypted only at the moment they're forwarded to the LLM backend, and are never written to logs or traces.
Project secrets & vaults
A secret is a first-class, feature-agnostic credential owned by a project (see Secrets & Vaults). Its value comes from one of two sources, both treated as compromised-by-default:
manual— encrypted at rest with AES-256-GCM (same primitive as override keys) and stored in the control plane.vault— a reference into the project's connected HashiCorp Vault (vaultPath+ optionalvaultField), fetched live at request time.
Vault auth credentials (tokens, AppRole secret IDs, passwords) are themselves stored encrypted as a JSON blob and decrypted only transiently to log in. Login tokens are cached in-process (≤ 5 min); resolved values are cached briefly (stale 30s / expire 60s).
Protection guarantees:
- No plaintext in list views — only masked hints; the full value requires an explicit, admin-gated Reveal action.
- Transient decryption — plaintext exists only long enough to forward the credential to the LLM backend or log in to the vault.
- Admin-only management — the entire Secrets page (secrets + vault) is project-admin gated.
Today secrets back project-scoped Custom LLM backends (BYO LLM endpoints);
the backend stores a secretId reference instead of the raw key.
Least-privilege agent tokens
Agents are project-scoped and authenticate with scoped JWT tokens — no user session required. Rotate, revoke, or regenerate from the dashboard; revoke-all cascades to every issued token for that agent.
Workspace safety
Each project workspace is sandboxed to its own folder. Path traversal (../) is
sanitized, workspaces are boundary-enforced, and dangerous file types
(.exe, .sh, .bat, .app, etc.) are blocked from upload/extraction.
Audit logging
Admin actions (role changes, member changes, token revocations) are written to an append-only audit log. There is no deletion — every administrative action is recorded with the actor, target, before/after values, and timestamp.