Context
PR #253 (issue #251) made uploads-cli a managed official OAuth client, but scope semantics are unchanged: workspace API tokens minted via POST /v1/tokens are limited to FILE_SCOPES (files:read/write/delete, apps/api/src/auth-db.ts). CLI-driven admin/operator actions currently have exactly one path — the static ADMIN_TOKEN break-glass secret gating /admin/* (apps/api/src/admin.ts): shared, non-attributable, not revocable per person.
Proposal
Add optional operator scopes to the workspace-token mint path, gated on the minting identity:
- New scopes (e.g.
admin:read, admin:write — naming TBD) accepted by POST /v1/tokens only when the minting session user has the better-auth admin role (requireAdmin, apps/api/src/session-auth.ts). Non-admin mints requesting them fail with invalid_scopes.
/admin/* (or a successor surface) accepts these scoped tokens alongside ADMIN_TOKEN, giving per-token revocation, labels, and attribution to the minting admin.
- Default mint scopes stay files-only; admin scopes are always opt-in ("manually select and add").
This gives attenuation: an admin can mint a files-only token for an agent and a separate short-lived operator token, instead of handing out an all-or-nothing session bearer or the shared break-glass secret.
Guardrails
- Add operator scopes on the API-worker side only — do NOT add them to the auth worker's
OAUTH_SCOPES: anything there is requestable by dynamically-registered MCP clients (DCR is open by design).
- If AS-side admin scopes are ever wanted, bound them per-client via the managed-registration
scopes column (only official clients like uploads-cli), never in the DCR-registerable set.
- Keep
ADMIN_TOKEN as break-glass, consistent with the Better Auth rollout decision.
Alternative considered
CLI admin commands could present the device-flow session token as a bearer against the session+role-gated /admin-ui/* surface with zero scope changes (the bearer() plugin already supports this). Cheaper, but all-or-nothing — no attenuation, no per-token revocation — so scoped minting is the preferred design if agents/automation will perform admin ops.
Context
PR #253 (issue #251) made
uploads-clia managed official OAuth client, but scope semantics are unchanged: workspace API tokens minted viaPOST /v1/tokensare limited toFILE_SCOPES(files:read/write/delete,apps/api/src/auth-db.ts). CLI-driven admin/operator actions currently have exactly one path — the staticADMIN_TOKENbreak-glass secret gating/admin/*(apps/api/src/admin.ts): shared, non-attributable, not revocable per person.Proposal
Add optional operator scopes to the workspace-token mint path, gated on the minting identity:
admin:read,admin:write— naming TBD) accepted byPOST /v1/tokensonly when the minting session user has the better-authadminrole (requireAdmin,apps/api/src/session-auth.ts). Non-admin mints requesting them fail withinvalid_scopes./admin/*(or a successor surface) accepts these scoped tokens alongsideADMIN_TOKEN, giving per-token revocation, labels, and attribution to the minting admin.This gives attenuation: an admin can mint a files-only token for an agent and a separate short-lived operator token, instead of handing out an all-or-nothing session bearer or the shared break-glass secret.
Guardrails
OAUTH_SCOPES: anything there is requestable by dynamically-registered MCP clients (DCR is open by design).scopescolumn (only official clients likeuploads-cli), never in the DCR-registerable set.ADMIN_TOKENas break-glass, consistent with the Better Auth rollout decision.Alternative considered
CLI admin commands could present the device-flow session token as a bearer against the session+role-gated
/admin-ui/*surface with zero scope changes (thebearer()plugin already supports this). Cheaper, but all-or-nothing — no attenuation, no per-token revocation — so scoped minting is the preferred design if agents/automation will perform admin ops.