Skip to content

Machine credentials

A machine records sessions where no person can sign in — CI runners, ephemeral agent sandboxes, anywhere a browser login is impossible. It records like a user but is never an administrator and never a member of a project. Instead of kcap login, the runner carries a machine credential: two environment variables that kcap exchanges for a short-lived access token by itself. No profile, no token files, nothing to refresh.

This is the right tool whenever recording has to survive a fresh environment. The interactive flows (kcap login, kcap login --device) both need a person at least once and leave a personal token behind; a machine credential is owned by the organization, works from the runner’s first boot, and can be revoked without touching anyone’s login.

Creating a machine requires being signed in (kcap login), the owner or admin role in your organization, and Capacitor administrator rights.

Terminal window
kcap machine create ci-runner

This provisions the credential and registers the machine with your Capacitor server. It prints the client ID (client_01…, public — safe to commit) and the secret, which is the only thing written to stdout; everything else goes to stderr.

Because the secret is alone on stdout, you can pipe it straight into a secret store without it ever touching disk — the client id and instructions still print to your terminal:

Terminal window
kcap machine create ci-runner | gh secret set KCAP_CLIENT_SECRET # only the secret enters the store
gh secret set KCAP_CLIENT_ID --body client_01ABC... # the client id, from the output above

Missed the client id? kcap machine list shows it any time — only the secret is unrecoverable.

create also accepts --visibility <private|org_public|public> — it doesn’t configure anything by itself; it selects the value the printed setup instructions tell you to set on the runner in step 3. Machines get the member role; a machine is never an administrator.

The runner needs the CLI, hooks for whichever coding agent it runs, and the two credential variables in its environment:

VariableWhat it is
KCAP_CLIENT_IDThe client id from step 1. Public.
KCAP_CLIENT_SECRETThe secret from step 1. Keep it in your CI’s secret store.
Terminal window
npm install -g @kurrent/kcap
kcap setup --server-url https://acme.kcap.ai --default-visibility org_public --no-prompt

With both variables set, every kcap invocation — hooks, watchers, imports — mints its own token (valid for about an hour) and holds it in memory. There is nothing to log in, nothing to refresh, and nothing written to disk.

  • Pre-baked images — if your runner image already has hooks installed, you can skip kcap setup and point kcap at the server with KCAP_URL=https://acme.kcap.ai instead. KCAP_URL outranks any profile the image might carry (see the resolution order), and a machine needs no profile at all.
  • Egress — the token exchange calls WorkOS directly, so a runner behind an egress policy needs signin.kcap.ai reachable in addition to your Capacitor server.

3. Choose what its sessions are visible to

Section titled “3. Choose what its sessions are visible to”

A machine’s sessions follow the same visibility system as a person’s: they record with the default_visibility of the profile the machine runs under, and with no profile at all they record org_public. The valid values are private, org_public, and public — not project, which is member-only, and a machine is never a project member.

org_public is almost always what you want for CI: anyone in the org can open the runner’s sessions in the dashboard. For a machine this holds regardless of which repository the session ran in — unlike a person’s org_public sessions, a machine’s never fall back to private in personal or unrecognised repos. Set it in the kcap setup line above, or directly:

Terminal window
kcap config set default_visibility org_public

Run that on the machine itself — visibility lives in the profile the machine records with, exactly as it does for a person; nothing is stored on the credential. Avoid private unless you mean it: a private machine session is browsable by nobody in the dashboard, because the machine is not a person who can sign in and look.

On the runner:

Terminal window
kcap status

With the credential in place, the auth line reads:

Auth: machine credential (KCAP_CLIENT_ID and KCAP_CLIENT_SECRET set) — kcap records as the machine, not as your login.

Then run your coding agent as you normally would. The session appears in the dashboard owned by the machine, visible per step 3.

Terminal window
kcap machine list # find the service id
kcap machine revoke service:9e96baf8f4f55363bc23d9a3a79939b3

Revoking stops the machine authenticating from its next request. A token it already holds stays valid until it expires (up to an hour) but is no longer honoured; to cut it off at the source as well, delete the application in the WorkOS dashboard. Revocation cannot be undone — restoring access means creating a new machine. That is also the rotation story: secrets are never re-shown, so rotate by creating a replacement machine and revoking the old one.

Admins can review and revoke machines in the console too, under Settings → Machines — creation stays in the CLI, because the secret travels from WorkOS to your terminal without ever passing through the Capacitor server.

SymptomCause and fix
You need the owner or admin role in this organization to create a machine.Your organization role is member. Ask an org owner or admin to run the create.
You need to be a Capacitor administrator to register a machine.The credential was issued but registration needs Capacitor admin rights on the server. Ask an admin.
kcap machine create reports machine credentials aren’t enabledThe feature isn’t switched on for your tenant. Contact Kurrent.
KCAP_CLIENT_ID is set but KCAP_CLIENT_SECRET is not — a machine needs both.Half-configured runner. Set the missing variable (and check it’s stored as a secret, not a variable).
the machine credential was rejected by … (HTTP …)Wrong or revoked values. Check the runner’s secret store; if in doubt, revoke the machine and create a fresh one.
Sessions record but nobody can see themThe runner’s default_visibility is private. Set org_public (step 3).
The runner can’t reach the token endpointAllow egress to signin.kcap.ai as well as your Capacitor server.