Access Tokens & Capabilities

Access tokens authenticate the CLI, Terraform, and API requests as a Bearer token. Every token carries a set of capabilities that bound what it can do. The stategraph user access-tokens commands let you mint, list, and revoke capability-scoped tokens directly from the CLI using an existing API key — no browser session required.

This is the recommended way to issue least-privilege credentials: a token can be limited to, for example, applying changes to a single state while being unable to manage users or create further tokens.

Capability Model

A token's capabilities are a subset of the capabilities held by the session that created it. A token can never exceed its creator.

Capability Grants Scope
admin Full access. Installation-wide when it names no tenants ({}); scoped to specific tenants when it does ({"tenants": [...]}). See Tenants and Administration. Installation-wide or per-tenant
plan Plan changes through transactions. Per-tenant, per-state, and per-subgraph
apply Apply changes through transactions. Per-tenant, per-state, and per-subgraph
users-manage Create, update, and delete users, and reset the passwords of users holding less authority. Never grants admin. See Who can act on whom. Installation-wide or per-tenant
sudo Act on behalf of specific users. Per-user
access-token-create Issue new access tokens. Global
access-token-refresh Refresh existing access tokens. Global

When no capability flags are given, the token inherits the creating session's capabilities — the least-surprising default for "give me a token that can do what I can do."

In the raw capabilities JSON — what --capabilities-json accepts and what stategraph user whoami --format json returns — the plan and apply capabilities appear under their internal keys preview and commit respectively. The CLI flags and table output use plan and apply.

stategraph user access-tokens create

Create an access token for the current user.

stategraph user access-tokens create --name <name> [capability flags]

The --name flag is required. The capability flags below select and scope what the token may do:

Flag Description
--admin Grant the admin capability, over the whole installation unless --admin-tenant is given.
--admin-tenant <TENANT> Restrict admin to these tenants (repeatable).
--plan Grant plan; unrestricted unless scoped with --plan-tenant, --plan-modified, or --plan-pulled-in.
--plan-tenant <TENANT> Restrict plan to these tenants (repeatable).
--plan-modified <STATE_ID=PATTERN> Restrict plan to the directly modified resources in a state that match PATTERN (repeatable).
--plan-pulled-in <STATE_ID=PATTERN> Restrict what plan may pull in: the resources pulled in as cone/blast dependencies in that state must match PATTERN (repeatable).
--apply Grant apply; unrestricted unless scoped with --apply-tenant, --apply-modified, or --apply-pulled-in.
--apply-tenant <TENANT> Restrict apply to these tenants (repeatable).
--apply-modified <STATE_ID=PATTERN> Restrict apply to the directly modified resources in a state that match PATTERN (repeatable).
--apply-pulled-in <STATE_ID=PATTERN> Restrict what apply may pull in: the resources pulled in as cone/blast dependencies in that state must match PATTERN (repeatable).
--users-manage Grant users-manage; unrestricted unless scoped with --users-manage-tenant.
--users-manage-tenant <TENANT> Restrict users-manage to these tenants (repeatable).
--sudo-user <USER> Grant sudo over these users (repeatable).
--access-token-create Grant the access-token-create capability.
--access-token-refresh Grant the access-token-refresh capability.
--capabilities-json <json> Raw capabilities JSON object. Mutually exclusive with the individual capability flags above.

Tenant and user values are rules: a tenant id (or a user), or a prefix ending in * (* alone matches everything); the most specific value that matches decides, and a leading ! refuses. When every value refuses, everything else is reached — --admin-tenant '!t1' grants admin over every tenant except t1, not admin over nothing.

State patterns take the form STATE_ID=PATTERN. Use * for the whole state (for example sid=*), a prefix such as sid=module.foo.*, or a leading ! to deny — for example --apply-pulled-in 'sid=!module.foo.output.*'. Repeated flags accumulate their patterns. A STATE_ID of * stands for the states that no other flag of the same name names, so --apply-modified '*=*' --apply-modified 'sid=!*' reaches every state except sid.

The --*-modified and --*-pulled-in flags check different things. --*-modified matches the resources the change directly modifies; --*-pulled-in matches the resources the change pulls in as cone or blast dependencies. Without --*-pulled-in, what a change pulls in is only restricted to the tenants the capability reaches, so a token that may modify one module but must not drag in anything outside it needs both.

Least-privilege example

Mint a token that can only apply changes to one specific state:

stategraph user access-tokens create \
  --name ci-prod-apply \
  --apply \
  --apply-modified 'a1b2c3d4-5678-90ab-cdef-1234567890ab=*'

The token value is printed once in the response and cannot be retrieved again — store it securely.

field  value
-----  ------------------------------------
token  eyJhbGciOiJIUzI1NiIs...

For scripting, use --format json:

{ "token": "eyJhbGciOiJIUzI1NiIs..." }

stategraph user access-tokens list

List the access tokens owned by the current user.

stategraph user access-tokens list

By default, output is a table with the columns id, name, created_at, expiration, owner_name, and owner_type. The expiration column is blank for tokens that never expire.

id                                    name           created_at            expiration  owner_name        owner_type
------------------------------------  -------------  --------------------  ----------  ----------------  ----------
f02791c8-aa63-4cdf-acae-a7c968d8a831  ci-prod-apply  2026-06-04T10:30:00Z              jane@example.com  user

For scripting, use --format json. Each token object also includes owner_id:

{
  "tokens": [
    {
      "id": "f02791c8-aa63-4cdf-acae-a7c968d8a831",
      "name": "ci-prod-apply",
      "created_at": "2026-06-04T10:30:00Z",
      "owner_id": "550e8400-e29b-41d4-a716-446655440000",
      "owner_name": "jane@example.com",
      "owner_type": "user"
    }
  ]
}

stategraph user access-tokens delete

Delete (revoke) an access token by id. The --token-id flag is required.

stategraph user access-tokens delete --token-id <uuid>
field    value
-------  ------------------------------------
id       f02791c8-aa63-4cdf-acae-a7c968d8a831
revoked  true

Inspecting your capabilities

Run stategraph user whoami to confirm the current identity and the exact capabilities a token or session holds. The capabilities object in its output mirrors the model above, so you can verify a least-privilege token is scoped the way you intended.

Default capabilities for new users

When a new user is created, they are granted a system-wide default capability. Out of the box this default is lenient, which is convenient when ACLs are not required. In an ACL-reliant setup you typically tighten it to a minimal baseline and grant additional rights per user or per group (for example, based on OAuth group membership).

Two admin commands manage this default:

# Show the capabilities granted to newly created users
stategraph capabilities default show

# Tighten the default so new users start with plan-only rights
stategraph capabilities default set --plan

stategraph capabilities default set accepts the same capability flags as token creation — see the table above — including --capabilities-json for setting the raw capabilities object directly. It takes no --name.

Changing the default applies to users created afterward; it does not retroactively change the capabilities of existing users.

API

The CLI is a thin wrapper over three endpoints. See the API Reference for full details.

Method Endpoint Purpose
POST /api/v1/user/access-tokens Create a token. The request body accepts name and an optional capabilities object.
GET /api/v1/user/access-tokens List the current user's tokens.
POST /api/v1/user/access-tokens/revoke?token_id=<id> Revoke a token.

When capabilities is omitted from the create request, the token inherits the creator's full session capabilities; any requested capabilities are capped at the creator's, so a token can never exceed the identity that created it.

Next Steps