Tokens & Scopes
Bearer-token authentication, the scope vocabulary and presets (including read-only), the bootstrap token, and how browser and Telegram sessions work.
Every Runyard API call is authenticated with a bearer access token:
curl -H "Authorization: Bearer $RUNYARD_TOKEN" https://hub.example.com/api/meGET /api/me (or the whoami MCP tool) describes the token you are holding: its name and granted scopes. Tokens are issued and revoked by admins and may carry an expiry.
Token names are operator-facing labels, not secret values. Admins can rename a token without changing its token value, scopes, named secret grants, revocation state, or runner identity. Runs keep the origin label captured when they were created, so past run rows are historical snapshots; renamed tokens affect future run origin labels.
Scopes
A token carries one or more scopes; each endpoint declares which scopes may call it.
| Scope | Grants |
|---|---|
api | Operator API/CLI access: read non-admin state, start/preflight workflows, run drafts, reruns, promotion, cancel, resolve approvals. |
mcp | The same operator read and write surface for MCP clients. |
read | Read-only. Inspect workflows, runs, logs, artifacts, approvals, schedules, runners, and the menu — and nothing else. It satisfies no mutation endpoint, so a read-only token can never change state. |
runner | The runner protocol only: register, heartbeat, claim runs, report owned lifecycle, upload owned-run artifacts. It cannot inspect operator read APIs. |
approvals | Read operator state plus resolving and creating approval cards — the narrow scope for approval surfaces. |
admin | Everything. admin is a superscope: it satisfies every scope requirement, and admin-only endpoints (tokens, secrets, catalog edits, schedules, hooks, audit) require it. |
Operator reads such as listing runs, workflows, runners, schedules, usage, dashboard data, logs, and artifacts require an operator-readable scope: api, mcp, read, approvals, or admin. A token with only runner is deliberately isolated from these human/agent read APIs. Runner machine-protocol endpoints additionally enforce run ownership: only the runner token that claimed a run (or an admin) may report lifecycle, usage, and artifacts for it.
Presets
GET /api/tokens/scopes (admin; MCP tool list_token_scopes) describes the scope vocabulary and the named presets the Tokens page offers:
| Preset | Scopes | Use for |
|---|---|---|
| Everything (default) | api, mcp, approvals | A normal person or agent: read everything, run workflows, decide approvals. No admin or runner rights. |
| Read-only | read | Dashboards, monitors, and cautious integrations that must never change state. |
| Approvals only | approvals | Decision queues and bots that only decide. |
| Runner | runner | Runner machines. Runner protocol and owned-run reporting only; no deployment-wide inspection. |
| Admin | admin | Full control, including tokens and secrets. Issue sparingly. |
Finer-grained per-group write scopes (e.g. a token that can manage schedules but not run workflows) are a documented follow-up; today api/mcp grant the whole operating surface.
Named secret grants (breaking change)
Runs may reference secrets from three distinct trust paths:
workflow.secrets— the trusted, admin-authored static declaration on the workflow. Always allowed.workflow.piApiKeyEnv— the trusted, admin-authored harness endpoint key on the workflow. Always allowed.- Dynamic run input —
input.secretNamesand any harness-selection field that names a delivered env (input.piApiKeyEnv). These are caller-controlled: without a check, anyone with a run-creation scope could ask the Hub to hand any stored secret to their run by name.
Dynamic secret names are now denied by default. A token can hand a dynamic secret name to a run only if it carries that exact env-var-safe name in its secretGrants list.
- Admin scope does NOT bypass this. An admin who needs a dynamic secret in a run must either (a) issue themselves a token that lists it in
secretGrants, or (b) move the secret to the workflow's trustedworkflow.secretsdeclaration. - No wildcards in v1. Every allowed dynamic name must appear literally.
- Grants never expose values.
GET /api/tokensand thelist_tokensMCP tool return grant NAMES only, always. - Enforcement runs on the shared run-creation/dispatch path used by Web/API/CLI/MCP, and again fail-closed at claim time. A queued run whose granting token is revoked before it is claimed will have its dynamically selected secrets withheld — the run still starts, but without those envs, and the
run.secret_grant.withheldevent records what was dropped and why (granting_token_revoked/grant_revoked/no_grant_at_creation). Secret values are never logged in the event. - Admin-configured hook profiles and static
workflow.secretsremain distinct trusted paths and are unchanged.
Migration
- Existing tokens migrate to an empty grant list. Any workflow that relies on a caller-supplied
input.secretNamesorinput.piApiKeyEnvwill start failing withsecret_grant_requireduntil (a) the trusted workflow definition declares those names statically, or (b) the calling token is re-issued with an explicitsecretGrantslist. - Legitimate first-party callers (notably the CI orchestrator) authorize their jobs' secrets through the trusted internal configuration path (
system:provenance) rather than a bypass. - CLI:
runyard token-create --secret-grants NAME1,NAME2 … - API:
POST /api/tokens {name, scopes, secretGrants: ["NAME1"], …} - MCP:
create_token({name, scopes, secretGrants: ["NAME1"], …})
Bootstrap token
On first boot, if no access tokens exist, the Hub creates a bootstrap-admin token with the admin, api, runner, and mcp scopes and writes it to data/bootstrap-token.txt on the server (relative to the Hub's data directory). Use it to log in and mint narrower tokens, one per client or integration.
Browser sessions
The web app is an ordinary API client. POST /api/auth/token-login exchanges an access token for a session cookie — a signed copy of the token — so the browser session carries exactly the token's scopes, no extra power. Admin sections of the web app appear only for admin-scoped sessions. POST /api/auth/logout clears the cookie.
Telegram WebApp sessions
Approving from Telegram uses POST /api/auth/telegram-webapp, which verifies Telegram's signed init data against the configured bot and allowlisted users, then issues a session scoped to approvals only — narrower than any token session. A Telegram session can list and resolve approval cards but cannot start runs or touch admin surfaces.
API & MCP
| Endpoint | MCP tool | Notes |
|---|---|---|
GET /api/me | whoami | Token name and scopes. |
GET /api/tokens | list_tokens | Admin. |
GET /api/tokens/scopes | list_token_scopes | Admin. Scope vocabulary, presets, defaults. |
POST /api/tokens | create_token | Admin. {name, scopes, secretGrants?, expiresInDays?}. |
PATCH /api/tokens/{id} | — | Admin. Rename the token label only; values, scopes, grants, and runner identity are unchanged. |
DELETE /api/tokens/{id} | revoke_token | Admin. Also revokes named grants — queued runs authorized by the token have dynamic secrets withheld at claim. |
POST /api/auth/token-login | — | Browser session bootstrap (rate-limited). |
POST /api/auth/telegram-webapp | — | Approvals-scoped Telegram session. |
POST /api/auth/logout | — | Clears the session cookie. |
GET /api/audit | get_audit_log | Admin; who did what, most recent first. |