API keys¶
The bcdock CLI authenticates with API keys (bdk_…) almost everywhere. Long-lived (until revoked), scope-bounded, no refresh-token gymnastics. Mint them in the portal at Profile → API keys.
For the JWT vs API-key mental model, see authentication.
Scopes¶
Pick the smallest set that lets the consumer do what it needs.
| Scope | Allows | Don't grant if… |
|---|---|---|
env:read |
List environments, inspect details, read logs | Always include - required for almost every other action to surface useful errors |
env:write |
Create / delete / hibernate / resume environments; publish AL extensions | The consumer is read-only (status dashboards, billing reports) |
usage:read |
Read company usage and billing data (bcdock usage, bcdock env usage <name>) |
The consumer doesn't need cost data |
API keys minted via bcdock auth login (the OTP exchange path) automatically receive these three scopes. Platform-admin operations (pool management, image building, cross-tenant env operations, billing inspection) are staff-only and aren't reachable from the public bcdock CLI or via customer-facing API keys.
How to mint¶
From the portal¶
- Profile → API keys → Create
- Name the key (e.g.
ci-pipeline,claude-agent,release-bot) - Pick scopes
- Click Create
- Copy the
bdk_…token - you can't view it again
The portal stores only the prefix and a hash; the bytes you copy are the only way to use the key.
From an existing JWT¶
Used today for minting admin-scoped keys (which auth login won't produce). Until bcdock auth create-key --scopes admin ships:
JWT=$(bcdock companies switch <admin-company> -o json | jq -r .token)
curl -X POST https://api.bcdock.io/api/v1/api-keys \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{"name":"ops","scopes":["admin","env:read","env:write"]}'
Returns the bdk_… token; pipe to bcdock auth set-token to persist locally.
How to use¶
Three paths, in order of preference:
| Path | When | Persistence |
|---|---|---|
BCDOCK_TOKEN env var |
CI, agents, ephemeral runners | none - dies with the process |
bcdock auth login |
Daily dev on a personal laptop | ~/.config/bcdock/credentials.json (mode 0600) |
bcdock auth set-token <bdk_…> |
When you have a key already and want to persist (e.g. admin-scoped key minted via curl) | same as auth login |
Rule of thumb: never persist secrets to disk in CI - use BCDOCK_TOKEN.
Verify what a key has¶
Prints the token's identity (email, role, company). Doesn't print the scopes directly today; if you need that, list keys in the portal - the row shows the scopes you picked at creation.
Revoke¶
Portal¶
Profile → API keys → Revoke on the row. Takes effect immediately for new requests; in-flight requests complete.
Programmatic¶
curl -X DELETE https://api.bcdock.io/api/v1/api-keys/<keyId> \
-H "Authorization: Bearer <jwt-or-key-with-admin-scope>"
The keyId is shown in the portal row; not the same as the bdk_… token bytes.
Rotation¶
We don't auto-rotate keys. When a contributor leaves or a runner is decommissioned:
- Mint a fresh key with the same scopes
- Update the consumer's secret store
- Revoke the old key
Both keys work in parallel during the swap, so there's no service interruption.
Common pitfalls¶
- Pasting
bdk_…into chat history (Slack, agent conversation log, Discord). Treat it like a password. - Granting
admin"for safety". The whole point of scopes is the smallest grant that works.adminis for BCDock operators, not customer pipelines. - Reusing one key across multiple unrelated repos / pipelines. Hard to attribute audit-log entries; rotation has higher blast radius. One key per consumer is the right shape.
- Storing keys in
~/.config/bcdock/credentials.jsonon shared servers. Every user with read access on that file can act as that key. UseBCDOCK_TOKENper-process for shared infrastructure.
Related¶
- Authentication - API key vs JWT, the three credential paths
- Exit codes -
exit 3is auth,exit 1with "scope insufficient" is the wrong scopes - Agent quickstart -
BCDOCK_TOKENis the right shape for agent loops