Admin Gate Auth
jobcache-ops-api, jobcache-ops-mcp, and ops.jobcache.corbet.ch share one admin secret. The gate code lives in the ops API; the MCP and UI cookies fan out to that gate, so there is only one secret to rotate.
Three accept paths
The admin gate in jobcache/ops/api/src/lib/auth.ts accepts any of:
- Cloudflare Access JWT — presence of the
cf-access-jwt-assertionheader means CF Access already gated the identity in front of the worker. JWKS verification is on the roadmap. - Bearer token —
Authorization: Bearer <token>matched constant-time againstADMIN_TOKEN. Used by curl, MCP clients, and operator scripts. admin_sessioncookie — set by the login flow onops.jobcache.corbet.chwithDomain=.jobcache.corbet.ch. The ops API worker reads the same cookie because it sits on a sibling subdomain.
Only GET / (liveness) is exempt.
Token unification across workers
jobcache-ops-mcp and jobcache-ops-api are deployed independently but share the same ADMIN_TOKEN:
- The MCP requires bearer auth on every POST and forwards the same token to the ops API.
- The ops API accepts that token through the same gate it offers to operators.
- One
wrangler secret put ADMIN_TOKEN --env productioncovers both. Future admin workers (e.g. a graph mutator) plug into the same gate by reading the same secret.
This is intentional: more secrets means more places to leak, and the public-private boundary is already enforced by the process split (status workers have no admin bindings, so a stolen admin token cannot be used against them).
Why this is safe enough today
Single operator. No multi-tenant admin. CF Access stands in for identity if the operator wants it; otherwise the bearer token rotates with wrangler secret put. When the operator group grows past one human, the JWKS verification path becomes the primary check and bearer tokens move into per-agent slots.
See also
jobcache/ops/api/README.md— route table and binding map.jobcache/ops/mcp/README.md— MCP-side bearer forwarding.status/api/README.md— the audit rule that proves the public worker cannot accept this token even if compromised, because it has no admin bindings.