Create the first owner and issue user keys
Issue per-user authentication and execution permissions
What you will do
Create the first owner and workspace, then issue an API key for each user. Distinguish API key revocation from revoking a session's execution grant.
Before you begin
Complete installation and access configuration. Restrict bootstrap tokens and logs to operators. On Kubernetes, configure the Secret and catalogEnv so every replica receives the same BOOTSTRAP_TOKEN.
Steps
1. Create the first owner
Open /bootstrap in a browser and enter the fixed BOOTSTRAP_TOKEN, owner details, and workspace name. On a test-ops single-API installation without a configured token, scripts/test-ops.sh bootstrap-token prints the generated token once; running it again prints nothing, and no container log is read. Other single-API setups (such as scripts/local.sh) print it in the API startup log (docker logs <this-installation-api-container-id>). On Kubernetes with the default 2 replicas, use a fixed token in the Secret rather than relying on automatic generation. Reusing the token after the first owner exists returns 409 BOOTSTRAP_DONE. Sign in at /login.
2. Issue per-user keys
KEY="$(scripts/test-ops.sh key create <owner_id> \
--scopes sessions:read,sessions:write,sessions:approve,sessions:control,sessions:recover)"<owner_id> names the owner grouping this key's sessions. Choose a different value for each user. This is separate from web console accounts; do not use a workspace id.
The SDK and HTTP API connect using this server address and key (SDK installation).
On Kubernetes, use the following command.
First check the installation's context, namespace, and fullname. The werft below is the example installation name.
kubectl -n werft exec deploy/werft-api -c api -- \
bun run apps/control-host/src/api/keys.ts create <owner_id> \
--scopes sessions:read,sessions:write,sessions:approve,sessions:control,sessions:recoverThe plaintext key appears on stdout only once; stderr reports the key id. Deliver the plaintext securely to the intended user without storing it in public logs. Per-user key owner identifiers are separate from web console accounts. Limit each API key's scopes to the tasks it needs.
3. Revocation contacts and execution grants
Tell users whom to contact to revoke a key. The operator CLI in the checkout provides:
scripts/test-ops.sh key revoke <key_id>
API_CONTAINER='<api-container-id-from-this-installation>'
docker exec "$API_CONTAINER" bun run apps/control-host/src/api/grants.ts revoke <session_id> --reason <text>
docker exec "$API_CONTAINER" bun run apps/control-host/src/api/grants.ts restore <session_id> --reason <text>Replace API_CONTAINER with the API container id identified from this installation's status and Docker project label. On Kubernetes, prepare a checkout matching the installed version and host Bun and kubectl, then use keys:k8s and grants:k8s. Both NAMESPACE and RELEASE default to werft.
| Operation | Effect |
|---|---|
| key revoke | Blocks subsequent API authentication with that key. It does not stop sessions already executing |
| grants revoke | Revokes the specified session's execution grant and credentials and requests execution termination |
| grants restore | Restores the execution grant after execution is confirmed absent. It does not revive a revoked API key |
Verify
Reading /v1/sessions with each user's key must return a response within its allowed scope; a request without a key must return 401. Revoked keys must fail authentication. Check the grants output receipt and execution state to distinguish acceptance from actual termination.
Troubleshooting
Different automatically generated bootstrap tokens on replicas can cause 401 when a request reaches another Pod. Pass the fixed token to every Pod. A missing Secret key prevents the Pod from starting. Do not use a workspace id as the CLI owner id.
Roll back
Do not reset the installation to remove an already-created first owner. Revoke incorrectly issued keys and create new ones with the correct scopes. Restore grants only after the old execution is gone.