Skip to main content

Service accounts and API keys

This guide explains how to configure service accounts for automation, pipelines, and machine-to-machine integrations, as well as how to manage personal API keys.

Credential architecture

  • Service Account: A machine identity bound to a specific project. Service accounts receive email-style identifiers (<name>@<project-short>.cai.local) and hold assigned roles (member or admin).
  • API Key: Long-lived bearer token formatted as cai_<keyid>_<secret> — a 12-character public key id ([0-9a-v]) and a 32-character secret. The platform stores only an argon2id hash, PHC-encoded with its own parameters ($argon2id$v=19$m=...,t=...,p=...$<salt>$<hash>), so a key hashed under today's settings keeps verifying after those settings change. The raw key is displayed once upon creation.

1. Create a service account

Project admin role is required to create and manage service accounts.

platformctl service-accounts create ci-deploy --role member --display-name "CI/CD Pipeline"

2. Mint an API key (Copy-Once Dialog)

--expires-in-days accepts 1 to 3,650. It has no default: leave it off and the key never expires.

platformctl service-accounts keys create ci-deploy --expires-in-days 90

(sa is an alias for service-accounts, so platformctl sa keys create ... is the same command.)

Output:

key id: 7f3a9c1b2d4e
id: 0193b6f1-9f4c-7a1e-9a1e-5f2c8d0b4a77 (pass this to 'service-accounts keys revoke')
expires: 2026-11-13T09:22:00Z

cai_7f3a9c1b2d4e_9V2V3PFuKq7ZmT4xN1sJdR6cB0wYhLgA

copy this now - only a hash is stored, so it cannot be shown again. If it is lost, revoke this key and create another.

Two identifiers, and they are not interchangeable. key id is the public half — it is what audit lines record and what you match against the key sitting in your CI configuration. id is the row UUID, and it is what the revoke API path takes.


3. Manage personal API keys

Personal API keys inherit your user permissions dynamically. If your account role changes or project access is updated, your personal API key permissions update instantly.

Mint a personal API key:

platformctl keys create --name cli-workstation --expires-in-days 30

The flag here is --name, not --display-name — the service-account form of this command is the one that takes --display-name.

List personal API keys:

platformctl keys list

Revoke one:

platformctl keys revoke <key>

4. Revoke API keys

Revoking an API key invalidates the credential immediately across all platform endpoints.

platformctl service-accounts keys revoke ci-deploy 0193b6f1-9f4c-7a1e-9a1e-5f2c8d0b4a77

The CLI accepts the key id here too (platformctl service-accounts keys revoke ci-deploy 7f3a9c1b2d4e) — it resolves that to the row UUID by listing the account's keys first. The API does not.


5. The credential inside a workload

Every deployed agent, function, MCP server and container carries a third kind of credential in its environment: CAI_PROJECT_KEY, formatted cai_wl_<keyid>_<secret>. It is minted and injected by the platform — you never create one.

It holds no project authority at all. The tenancy check refuses a workload key on both axes, resource and metadata, for its own project as much as anyone else's, before any grant is consulted. So every project route answers 404 not found for it — not 403. A leaked workload environment is not a foothold on the project API.

Its one capability is minting a short-lived, read-only token for its own project's secrets, at POST /v1/projects/{projectID}/secrets:issue-token, and only when the key's project matches the project in the path. That is what backs the call-time secret() read described in Use secrets in workloads.

If a workload has to call a project API for anything else, give it a credential that can: create a service account, mint a key for it, store that key with platformctl secrets put, bind the secret to an environment variable, and apply.

platformctl service-accounts create indexer-bot --role member
platformctl service-accounts keys create indexer-bot --expires-in-days 90
# copy the printed cai_... secret, then:
printf %s "$KEY" | platformctl secrets put indexer-bot-key
platformctl secrets bindings set my-agent CAI_TOKEN --secret indexer-bot-key
platformctl secrets bindings apply my-agent

Security fences for machine accounts

  1. No Privilege Escalation: Service account credentials cannot mint replacement credentials or alter project IAM memberships.
  2. Immediate Revocation: Deleting a service account automatically invalidates all associated API keys in a single transaction.
  3. Permanent Name Reservation: Deleted service account names are reserved permanently to prevent identity spoofing or grant inheritance.
  4. Workload Keys Hold No Authority: The CAI_PROJECT_KEY inside a deployed workload is refused on every project route, by design, and can only mint a scoped secret-read token for its own project.