Skip to content

Service-User CI (cak_ keys)

A service user is a dedicated, non-human member of your organization that your pipelines log in as. Instead of handing CI a full-org clt_ key, you create a machine account, mint a per-user cak_ key for it, and let the pipeline act as that user — confined to what the user can see, clamped to a low role.

The guiding principle:

An org-level clt_ key reads the whole org. A cak_ service user reads only what its user owns or has been shared — and never more than a member.

This is the recommended CI/CD credential. The org-level clt_ key is kept as an instant-kill, zero-dependency fallback, and sak_ service-account keys remain the option for genuinely ownerless automation.

A cak_ is a per-user key: every request it makes is exchanged at the sign-in service for a short-lived launch token that authenticates as the user that minted it. The cloud governs those requests with the same user-scoped policy a human browser session gets:

  • Reads are confined to snapshots and targets the user owns, plus anything shared with them or shared org-wide — never another user’s private rows.
  • The role is hard-clamped. Regardless of what role the service user holds in the sign-in service, the cloud caps every cak_ request at a configured ceiling (default member). A mis-assigned admin/owner service user is still only a member on the data plane.
  • Pushes are attributed to the service user (the snapshot’s owner_user_id), so artifacts a pipeline creates are owned and accountable.

Contrast that with an org-level clt_ key, which has literal full-org access and no user identity at all.

Bootstrap: create the service user and mint its key

Section titled “Bootstrap: create the service user and mint its key”

This is a one-time, human-in-the-loop setup done in your sign-in service dashboard. cak_ creation is strictly self-scoped — an org admin cannot mint a cak_ on another user’s behalf — so you must sign in as the service user to create its key.

  1. Create a dedicated service user with a unique, pre-verified machine email (for example ci@yourorg.example) and a real login method (password, SSO, or device grant). It needs a controllable inbox or SSO because of step 3.

  2. Add the service user to your organization with a least-privilege role (member is plenty — the cloud clamps it anyway). Grant it access to specific snapshots and targets by sharing them, never by raising its role.

  3. Log in as the service user and mint its cak_. From the sign-in service, create a per-user API key while authenticated as the machine account. Copy the cak_… string immediately — it is shown only once.

  4. Confirm the Clonit app supports per-user keys. The Clonit application must declare the user_api_keys capability in the sign-in service, or key creation fails closed. (A platform admin enables this once, per application.)

  5. Store the cak_ as your CI secret and expose it to the agent as cloud.api_key. Its on-disk exposure profile is the same as a clt_ key today.

# In the CI environment's config (or via env var)
cloud:
url: "https://cloud.clonit.ai"
api_key: "cak_your_service_user_key_here"
Terminal window
# Preferred in CI / containers — pull the key from your secret store
export CLONIT_CLOUD_URL="https://cloud.clonit.ai"
export CLONIT_CLOUD_API_KEY="$CLONIT_CI_KEY" # a cak_ service-user key

The agent classifies the key by prefix automatically: a cak_ (or sak_) triggers the launch-token exchange and is sent as a Bearer token; a clt_ (or any legacy key) keeps using the X-API-Key header. You do not configure this — drop in the key and the agent does the right thing.

Least privilege is enforced by the cloud, not assumed from the key. The cloud deployment sets a hard ceiling:

# clonit-cloud deployment config
auth:
cak:
max_role: member # default; every cak_ request runs at min(localRole, ceiling)

Every cak_ request runs at min(localRole, max_role). With the default member ceiling, a cak_ can never reach owner/admin org-wide reads, even if the service user is mis-assigned a high role. Set max_role: viewer for read-only CI (pull-only pipelines). This is a cloud-side knob (CLONIT_CLOUD_AUTH_CAK_MAX_ROLE), not an agent setting.

The service user’s local role and membership status are read fresh on every request. If you demote or suspend the service user in the cloud, that takes effect immediately on its next call — a cak_ never re-escalates itself.

When a cak_ pipeline pushes a snapshot, the cloud records the service user as the snapshot’s owner (owner_user_id). The snapshot shows up as owned by the service user and is visible to org-wide roles and to anyone it’s shared with — see the access badges in Sharing Snapshots. This keeps a clear, accountable trail of what each pipeline produced.

A cak_ only works against a cloud that has the service-user arm and the sign-in service wired in. A new CLI carrying a cak_ against an older or auth-disabled cloud gets a hard 401. Sequence the rollout:

  1. Deploy the cloud first with the cak_ arm and the auth service enabled.
  2. Then switch the CI secret cloud.api_key to the cak_.
  3. Keep the clt_ key as rollback until the cak_ is proven across your pipelines. There is a single cloud.api_key slot, so this is a manual credential swap — not an automatic runtime failover.

Only retire clt_ org keys once cak_ is validated. Until then, your effective blast radius is the union of both credentials.

cak_ revocation is not instant, and there is no org-admin “revoke another user’s cak_” button (key management is self-scoped). Know which lever to pull:

Lever Who Latency
Delete the clt_ key in Clonit Cloud cloud admin instant — the genuine kill switch; keep clt_ for this
Remove the service user from the org (sign-in service) org admin ≤ ~2 min — the next exchange’s launch gate fails
Ban the service user’s email (sign-in service) platform admin ≤ ~2 min
Suspend the user in Clonit Cloud (org_members.status) cloud admin ≤ ~2 min — the read-only status guard denies the next request
Revoke/rotate the cak_ (sign-in service) the service user itself ≤ ~2 min on next exchange; outstanding launch tokens live to their ~2-minute TTL

Every CI job is a fresh, one-shot process: there is no token cache between runs, so every pipeline run does a live exchange against the sign-in service at startup. If the sign-in service is unreachable, every cak_ run fails.

For pipelines that must tolerate the sign-in service being down, use a clt_ key instead — it validates directly against the cloud with no auth-service dependency.

Before you retire a service user, deal with the artifacts it owns. Under user-scoped ownership, a removed user’s owned snapshots and targets remain visible only to org-wide roles. Reassign or org-wide-share the service user’s owned snapshots and targets first, then remove its membership and revoke its cak_.

Question Answer
Recommended CI credential A per-user cak_ minted by a dedicated service user
Who mints the cak_? The service user itself (self-scoped; sign in as it)
How much can it read? The service user’s owned ∪ shared ∪ org-wide, clamped to member
Role ceiling auth.cak.max_role on the cloud deployment (default member)
What does the agent read? cloud.api_key (or CLONIT_CLOUD_API_KEY) — a cak_ string
Deploy order Cloud (with the cak_ arm) first, then switch the CI secret
Instant kill Delete the clt_ key (kept as the zero-dependency fallback)
Fast cak_ kill Remove the service user from the org / ban its email (≤ ~2 min)
Auth dependency The sign-in service must be reachable at the start of every run