Files
orca/cloud/apps/sandbox-control/migrations/0045_codex_broker.sql
T
Neil 4af8f7fe04 feat(sandbox-control): Codex account broker (contract D11, flag S2)
Codex signs in once via the person's Orca account:
- POST /v1/agent-credentials/codex/broker starts a short-lived broker sandbox (golden image, egress
  auth.openai.com + chatgpt.com only, non-persistent, tagged orcaCodexBroker/orcaInfra so the workspace
  orphan sweep and billing never see it) running `codex login --device-auth` (Codex 0.166), and returns
  {brokerId, verificationUrl, userCode}. GET .../broker/:id -> {status: pending|active|failed}; DELETE
  cancels. On completion the Worker reads auth.json through the files API, stores the tokens
  envelope-encrypted and Personal-only in agent_credentials, and deletes the broker sandbox.
- Lease refresher (cron, every 5 min): refreshes the access token with the refresh token before expiry,
  single-flight per person (D1 lease) with a compare-and-swap write, so a refresh token is spent once and
  a delete or new login is never overwritten (no refresh_token_reused). Failures mark expiring/expired
  (by the real access-token expiry) or revoked (refresh_token_reused/invalidated). A chatgpt.com 401
  triggers a refresh instead of revoking.
- codexAccount AccountProvider: placeholder access token bound to the sandbox id (D14), swapped for the
  real one on chatgpt.com with ChatGPT-Account-Id; never in a shared workspace (D13); kill switch
  AGENT_ACCOUNTS_CODEX_DISABLED=1.
- VM: orca-sandbox-env writes a placeholder ~/.codex/auth.json (no refresh token) from
  ORCA_CODEX_AUTH_PLACEHOLDER, sets a managed HTTPS ChatGPT provider (Codex's Responses WebSocket can't
  pass the egress proxy; verified live), and sets a person's own login aside / restores it.
- Migration 0045_codex_broker (codex_broker_sessions, codex_leases; no token columns).
- Flag S2 on in wrangler.dev.jsonc; absent in prod.
2026-11-10 05:03:50 -08:00

40 lines
1.8 KiB
SQL

-- Codex account broker (agent-accounts spec A2, contract D11 / flag S2). Additive only.
-- The credential itself lives in agent_credentials (0044); these tables hold no token.
-- One device login in a short-lived broker sandbox. The user code is shown only to its owner and
-- is useless after `expires_at` (Codex device codes last 15 minutes).
CREATE TABLE IF NOT EXISTS codex_broker_sessions (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
account_id TEXT NOT NULL,
provider_name TEXT NOT NULL,
session_id TEXT,
status TEXT NOT NULL CHECK (status IN ('starting', 'pending', 'completing', 'active', 'failed')),
verification_url TEXT,
user_code TEXT,
error TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
expires_at INTEGER NOT NULL,
-- Set once the broker sandbox is gone; the cron retries deletes until it is.
destroyed_at INTEGER
);
CREATE INDEX IF NOT EXISTS codex_broker_sessions_user ON codex_broker_sessions (user_id, created_at);
CREATE INDEX IF NOT EXISTS codex_broker_sessions_live ON codex_broker_sessions (destroyed_at, expires_at);
-- The refresher's single-flight lease and schedule, one row per person with a Codex credential.
-- A refresh token is single-use: only the lease holder may spend it (refresh_token_reused otherwise).
CREATE TABLE IF NOT EXISTS codex_leases (
user_id TEXT PRIMARY KEY,
lease_owner TEXT,
lease_until INTEGER,
access_expires_at INTEGER,
refreshed_at INTEGER,
-- Backoff after a transient failure (the refresher skips the row until then).
next_attempt_at INTEGER,
failures INTEGER NOT NULL DEFAULT 0,
-- An error code from the token endpoint (e.g. refresh_token_expired), never a token.
last_error TEXT
);
CREATE INDEX IF NOT EXISTS codex_leases_due ON codex_leases (access_expires_at);