API key vs OAuth
Two ways to authenticate to CMDOP — pick by who’s calling and how often.
TL;DR
| Use case | Pick |
|---|---|
| Interactive human (terminal, desktop) | OAuth |
| CI / unattended agent | Workspace API key |
| Bot / scheduled script | Workspace API key |
| SDK during development | OAuth (your login) |
| SDK in production | Workspace API key |
| Mixed (script today, you tomorrow) | Both — resolver picks |
OAuth
OAuth tokens come from cmdop login (device flow). Properties:
- Identity — your personal account.
- Workspaces — every workspace you belong to.
- Lifetime — short access token (typically 1 hour) auto-refreshed 5 minutes before expiry.
- Storage —
token_<mode>.jsonin CMDOP’s config dir (token_prod.json,token_dev.json). - Revocation — sign out, rotate the OAuth secret, or wait for refresh expiry.
OAuth is the right pick when a human is around to walk through device flow and the action is “yours” in the audit log.
Workspace API key
API keys are long-lived bearer tokens scoped to one workspace. Properties:
- Identity — the workspace, plus a name you set at creation.
- Workspaces — exactly one.
- Lifetime — never expires unless you set an expiry. Defaults to “no expiry” — set one anyway if you can.
- Storage —
ssh_workspaces.json(mode 0600). - Revocation — explicit revoke from Workspace settings.
API keys survive member churn — a CI pipeline does not break because someone left the company.
Side by side
| Property | OAuth token | Workspace API key |
|---|---|---|
| Bound to | Person | Workspace |
| Multi-workspace | Yes | No (one key per workspace) |
| Refresh | Automatic (5 min before expiry) | None — long-lived |
| Setup time | Device flow + browser | Cabinet click → copy secret |
| Revoke | Logout / rotate | Cabinet → revoke |
| Best for | Humans | Machines |
| Audit attribution | [email protected] | apikey:gha-deploy |
Credential resolver order
When you run cmdop ..., the resolver picks credentials in this order (internal/connect/workspace/Resolver.ResolveCtx):
--api-keyflag (explicit per-call override).CMDOP_API_KEYenvironment variable.--workspace=<name>flag — uses the named workspace’s stored key.- Active workspace key from
ssh_workspaces.json. - Legacy
cfg.Chat.GrpcAPIKey(one-shot migration). - OAuth access token (fallback — “just works”).
Each Source is tagged in error messages so you know what was picked when something fails.
Rotating an API key without downtime
- Issue a new key with the same scopes in the cabinet.
- Roll the new value out to consumers (CI secret, deployment env).
- Watch audit log for the old key’s last use.
- Once you see the new key in audit (and the old key idle), revoke the old key.
Mixing in CI
Common pattern:
# In CI, where humans aren't around:
export CMDOP_API_KEY="<workspace key>"
cmdop connect prod-1 exec 'systemctl status myapp'
# At your desk:
unset CMDOP_API_KEY
cmdop login # OAuth
cmdop connect prod-1 exec 'systemctl status myapp'The resolver prefers the env var, so CI uses the API key and your laptop falls back to OAuth.
API keys are workspace-bound. To use machines in workspace B you need a key for workspace B (or sign in with OAuth as a member of B).