Skip to content

Service accounts

A service account is a principal that belongs to an organization rather than to a person. Use one for automation that should outlive whoever set it up: a hub, a pipeline, a long-running agent. Its tokens keep working when the owner or admin who minted them leaves the org or is demoted.

A service account is written {org}/{name}, for example acme/hub-x. Its name follows the repository name rules and is unique among the org’s service accounts. A service account and a repository may share a name; every command that takes a service account says so.

  • It can’t sign in. It has no login and no email anyone can use. It acts only through the personal access tokens minted for it.
  • It takes no seat.
  • It holds a role in its org, viewer or editor, never owner or admin. That role is the live ceiling for every token minted for it: demote the account and its tokens narrow at their next request.
  • It belongs to one org. It can’t be invited into another org, added as a member, or listed among members.
  • Two permissions govern it, both on its org and both granted by org:configure: org:service-accounts-manage to create, list, view, re-role, disable, enable and delete it, and org:service-accounts-mint to mint, list, view and revoke its tokens. Owners and admins hold org:configure through their role; a member whose scope override grants org:configure on the org holds both too. A personal access token needs org:configure on that org as well, since a token never exceeds the member it acts for.
Terminal window
wh org service-account create acme hub-x --role viewer -d "Hub X reader"
wh org service-account list acme
wh org service-account view acme hub-x

The role is required; nothing defaults to editor. In the TypeScript SDK the same operations are client.org.serviceAccounts.create|list|get|setRole|disable|enable|delete, each taking a plain object such as { orgName: 'acme', name: 'hub-x' }.

Add --service-account to the usual wh token verbs:

Terminal window
wh token create --service-account acme/hub-x --name hub-x-toolset \
--scope acme/markets=repo:read --expires 90d
wh token list --service-account acme/hub-x
wh token get --service-account acme/hub-x --name hub-x-toolset
wh token revoke --service-account acme/hub-x --name hub-x-toolset
  • --scope (or --scopes-json) is required, and every scope names a resource: the org (acme=repo:read) or a repository (acme/markets=repo:read). A service account’s token never silently gets the account’s whole role.
  • The scopes you request must be within both the service account’s access and your own. An admin whose access is narrowed can’t mint past it; if your access to any repository in the org is narrowed, scope the token per repository rather than org-wide.
  • Expiry limits are the same as for any token: 30 days by default, 365 at most.
  • --committer-identity follows the usual identity-use rules, with the service account as the owner, so it can bind the account’s own Identity.
  • get and list show who minted each token. Listing and revoking work while the account is disabled, so you can clean up.

In the TypeScript SDK, pass serviceAccount: 'acme/hub-x' to client.token.create, { serviceAccount } as the second argument to client.token.get and client.token.revoke, and list with client.token.listForServiceAccount('acme/hub-x').

A token minted for a service account can mint shorter-lived child tokens with the plain wh token create, as any token can. A child is bounded by its parent and is revoked with it.

Creating a service account provisions its Identity in warmhub/users, as signing in does for a person. The Identity’s display name is the account’s external id; its org and name are not published there.

  • Writes made with its tokens are attributed to that Identity by default.
  • wh doctor and client.auth.whoami() report it as identityWref.
  • Disabling or deleting the account leaves the Identity in place.
ActionEffect on its tokens
wh org service-account set-role acme hub-x --role editorWiden or narrow at the next request
wh org service-account disable acme hub-xRejected until the account is enabled
wh org service-account enable acme hub-xWork again
wh org service-account delete acme hub-xRevoked
Archive the orgEvery service account in it is disabled
Unarchive the orgAccounts stay disabled until someone with org:service-accounts-manage enables each one

Deleting a service account keeps its history: commits and token records still point at it. The name becomes free for a new service account, which gets a new external id and inherits nothing from the old one, including its tokens.

In an archived org you can still list, view and delete service accounts, and list or revoke their tokens. Creating, enabling, disabling, changing a role and minting are refused.

Owners and admins can subscribe an org webhook to service-account changes:

Terminal window
wh sub create sa-audit --org acme --event org.service_account_created \
--webhook-url https://example.com/hooks/warmhub

The events are org.service_account_created, org.service_account_role_changed, org.service_account_disabled (also sent once per account when the org is archived), org.service_account_enabled and org.service_account_deleted. Creating a service account does not send org.member_added. See organization activity subscriptions for the payload.