Everything that talks to oak.space β the CLI, the HTTP API, CI jobs, agents β authenticates with a bearer token:
Authorization: Bearer oak_...
There are three kinds of token.
| Token | Comes from | Lifetime | Use it for |
|---|---|---|---|
| Login session | oak login | 90 days | You, at your own terminal. |
| Personal API key | You create it | Until revoked | Scripts and tools acting as you. |
| Service-account key | An org admin creates it | Until revoked | Bots, CI systems, and agents with their own identity. |
Personal API keys#
Create one under Settings β API keys on oak.space, or over the API:
curl -X POST https://oak.space/api/api-keys \
-H "Authorization: Bearer $OAK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "release script", "scope": "read", "repo": "acme/web"}'
| Field | Value |
|---|---|
name | 1β100 characters, for your own reference. |
scope | full (default) or read β a read key can only make read requests. |
repo | Optional owner/name. The key then works only for that one repo. |
The full key is shown once, when it's created. After that only its first characters are displayed, so store it somewhere safe. List your keys with GET /api/api-keys and revoke one with DELETE /api/api-keys/{id}.
A personal key acts as you, with your access β a repo-scoped or read-only key can only do less, never more.
Service accounts#
A service account is a non-human identity owned by an organization β a deploy bot, an external CI system, a coding agent that runs unattended. Its actions show up under its own name in the audit log, and its keys carry explicit scopes, so you can give it exactly what its job needs.
Organization owners and admins manage them under the organization's Settings β Agents, or over the API:
# Create the account
curl -X POST https://oak.space/api/orgs/acme/service-accounts \
-H "Authorization: Bearer $OAK_TOKEN" -H "Content-Type: application/json" \
-d '{"name": "nightly-agent", "display_name": "Nightly refactor agent"}'
# Give it a key that can push branches but never merge
curl -X POST https://oak.space/api/orgs/acme/service-accounts/nightly-agent/keys \
-H "Authorization: Bearer $OAK_TOKEN" -H "Content-Type: application/json" \
-d '{"name": "prod", "scopes": "read,write"}'
| Scope | Allows |
|---|---|
read | Every read request: clone, pull, list, view. |
write | Push branches, create repos and branches, and other changes β except the ones below. |
merge | Merge branches onto main. |
admin | Organization management (/api/orgs/...). |
release | Authorize protected releases. Never implied by any other scope, including admin. |
Scopes are required on service-account keys. A key can never do more than the person who created it can. A request outside a key's scopes gets a 403 naming the missing scope.
Disable a service account (PATCH β¦/service-accounts/{name} with {"disabled": true}) to stop all its keys at once, or delete it. Revoke one key with DELETE β¦/service-accounts/{name}/keys/{key_id}.
For coding agents, a
read,writeservice-account key is a good default: the agent can clone, push its branches, and file what it did, but can't merge tomainβ merging stays a human decision.
Using a key with the CLI#
export OAK_API_KEY=oak_...
oak clone acme/web
oak auth status # confirms which credential is in use and who it resolves to
The CLI picks its credential in this order:
OAK_API_KEY, if set.- A key stored in the checkout itself (
oak clonesaves one, bound to the server it came from). - Your
oak loginsession for that server, from~/.oak/credentials.
If a push or pull unexpectedly says a repo you know exists isn't found, a stale key from step 2 is the usual suspect β oak auth status shows which source is winning.
Using a key with the API#
curl https://oak.space/api/repos -H "Authorization: Bearer $OAK_API_KEY"
See the HTTP API reference.