Skip to content

Tokens

Every request to cache.bento.build carries a team-scoped JWT. Tokens are signed with Ed25519 by the control plane; the CAS worker only ever holds the public key, which it fetches from https://api.bento.build/.well-known/jwks.json.

There are two ways to get one, and they’re for different jobs.

Terminal window
bento login

Runs the device-code flow, then stores the token for you — OS keychain first (entry bento / cache-token), ~/.bento/credentials at mode 0600 as the headless fallback. Nothing is written into your repo. This is the right path for a laptop; see Hosted cache for the full walkthrough.

bento login --agent asks for a read token that expires in an hour instead — the right shape for a coding agent that should benefit from the cache without writing to it. --scope read|read_write and --ttl 30m|2h|7d set the two independently, and the approval page shows exactly what it is about to grant before you click. admin is dashboard-only.

Sign in at app.bento.build, open Tokens, and mint one. You need the admin role on the team.

FieldRules
Label3–40 characters of a-z, 0-9, and -; no leading or trailing dash. Required, and it’s a real audit field — it shows up in bento doctor --cloud output and in the dashboard’s token list next to last-used time. Name it after the job: gha-ci, release-runner.
Scoperead, read_write, or admin.
TTLDays from now. Defaults to 90, clamped to 1–365. The dashboard offers 30 (CI), 90, and 365; anything in range is reachable through the API.

The token list flags rows that want attention: expires in Nd under two weeks left, expired past the date, and unused for a token nobody has presented more than a week after minting. Each row also shows which client last used it.

The JWT is shown once. The control plane persists only the metadata (id, label, scope, expiry, signing key) — it cannot show you the token again, and neither can we. Lose it and mint another.

ScopeRead cacheWrite cacheManage tokens + team
read
read_write
admin

read_write is the normal CI token. Use read for jobs that should benefit from the cache but never populate it — untrusted fork PRs are the usual case. admin is for tooling that mints or revokes other tokens; it does not belong in a workflow file.

Scopes serialize as those exact snake_case strings in the JWT and in every API response, and they’re additive: future deploy-oriented scopes won’t change the meaning of these three.

The CLI reads it from an environment variable whose name you choose in bento.toml:

[cache]
remote = "bento://cache.bento.build"
remote_token_env = "BENTO_CACHE_TOKEN"

BENTO_CACHE_TOKEN is the conventional name and the CLI’s fallback default — use it unless you have a reason not to. The variable name is safe to commit; the value is a secret.

Terminal window
gh secret set BENTO_CACHE_TOKEN -R <owner>/<repo>
env:
BENTO_CACHE_TOKEN: ${{ secrets.BENTO_CACHE_TOKEN }}

Setting it at the job level (rather than per step) covers every bento invocation in the job. For non-GitHub CI, the mechanism is the same: put the JWT in the runner’s secret store, expose it under that variable name.

Resolution order is env var → keychain → ~/.bento/credentials, first non-empty wins, so an exported BENTO_CACHE_TOKEN always beats a stale bento login on the same machine.

Revoke from the dashboard’s Tokens page. Revocation flips the row in the control plane and pushes the token’s id into the CAS worker’s revocation list, so a leaked token stops working at the edge rather than at expiry.

Two properties worth knowing:

  • Expiry is the backstop. Every token carries exp; even if revocation propagation fails, the blast radius is bounded by the TTL you chose at mint. Short TTLs on CI tokens are cheap insurance.
  • High-privilege tokens fail closed. Tokens minted with admin scope, or with a TTL longer than 7 days, are marked so the worker treats the revocation lookup as load-bearing — if it can’t check, it rejects. Short-lived low-privilege tokens skip that lookup for speed.

To rotate: mint the replacement, update the secret, confirm a green build, then revoke the old one. There’s no in-place rotation, deliberately — you’re never in a window where neither token works.

Terminal window
bento doctor --cloud

Decodes the JWT you have loaded and reports iss, team_id, scope, and label, plus how long until it expires, alongside reachability probes for the cache and the API. An expired token fails the cloud.token.expiry check and tells you to re-mint.

One wrinkle: cloud.token.env (and bento prime’s remote: line) only inspect the environment variable. A keychain-only login reports “not set” there while builds work fine.

Claim-by-claim breakdown: JWT claims.