Scopes
A scope is a single permission — one action on one resource. Every API key carries a set of them, and they’re the core of how managed.dev beats coarse host tokens: instead of one all-or-nothing credential, you grant exactly the actions a key needs and nothing more.
The grammar
Section titled “The grammar”Scopes follow the form resource:action. Resources in the dynamic application
layer — the WordPress-specific surface that generic platforms don’t expose at all —
are dotted: wp.plugins, wp.cli.
sites:read # resource:actionsites:writeenvironments:writewp.cli:exec # dotted resource, standalone actionwp.plugins:write # dotted resourcevulnerabilities:readTwo relations cut down how many scopes you list:
writeimpliesread. Grantingsites:writealso grantssites:read. You never need to list both.adminimplieswrite(and thereforeread).teams:admincoversteams:writeandteams:read.
Two things are deliberately outside the implication chain:
- Standalone actions are never implied.
execandrawsit outside read/write/admin — nothing short of grantingwp.cli:execgrantswp.cli:exec. vulnerabilities:readis not implied bysecurity:read. The vulnerability feed is its own grant, so a general security-read key doesn’t automatically see your CVE exposure.
Wildcards expand along the same lines: sites:* grants every action on sites
(read, write, admin), and the global * grants every scope in the catalog — except
the isolated scopes below, which no wildcard ever includes.
The isolation rule
Section titled “The isolation rule”Five scopes are dangerous enough that they’re excluded from every wildcard, including
the global *. You can only grant them explicitly, one at a time:
| Scope | What it unlocks |
|---|---|
credentials:read |
Reveal SFTP/SSH/DB credentials; list authorized SSH keys. |
credentials:write |
Add/remove SSH keys, reset the admin password, mint magic-link SSO. |
exec:raw |
Lift the WP-CLI denylist — run arbitrary commands, not just the safe set. |
keys:read |
List and inspect API-key metadata. |
keys:write |
Mint, roll, and revoke API keys. |
So a * key — about as broad as you can mint — still cannot reveal an SFTP password, run
an unfiltered command, or mint (or even enumerate) other keys. Those require deliberate,
single grants. This is the line that keeps a broad automation key from quietly becoming a
root credential. See the security model for why this
boundary holds.
Down-scoping: the triple intersection
Section titled “Down-scoping: the triple intersection”A scope grants nothing on its own — it only ever narrows what the key’s owner could already do. Effective permission on any request is the intersection of three things:
effective = perms(RBAC role) ∩ scopes(key) ∩ resource-constraint(key)perms(RBAC role)— what the principal that minted the key is allowed to do under their team role. A key minted by abilling-only member can never deploy, no matter which scopes it lists.scopes(key)— the scopes you granted at mint time.resource-constraint(key)— the optional site the key is pinned to. (Team, project, and environment pin types are defined but not mintable yet.)
The headline consequence: a key can never exceed its owner. Scopes can only subtract. This is the same mental model as Cloudflare and Stripe scoped tokens, and it’s why a narrowly-minted key is safe to hand to CI or a contractor — it’s structurally incapable of escalating past the person who created it. Keys are additionally forced to a non-admin role, so there is no admin scope at all; see the security model.
Runtime-gated and reserved scopes
Section titled “Runtime-gated and reserved scopes”The dotted wp.* scopes (plus cron:* and db:*) are only meaningful where the
runtime advertises the matching capability — wp.plugins:write does nothing on a
static site because a static site has no plugins. Clients shouldn’t hard-code
if runtime == "wordpress"; they should ask the site what it can do via
capability discovery and grant scopes accordingly.
A call against a site you can’t see returns 404 site.not_found; a call the site’s
runtime can’t fulfil returns 409 capability.unsupported. Both are covered in
capability discovery.
Of this family, wp.cli:exec is live today — it gates the WP-CLI exec endpoint.
The rest — wp.plugins, wp.themes, wp.content, wp.users, cron, db — are
reserved Preview: the grammar defines them, but
the endpoints they’ll gate aren’t wired yet.
Common scopes
Section titled “Common scopes”A representative slice — the full list lives in the scope catalog.
| Scope | Gates | Runtime gate |
|---|---|---|
sites:read / :write |
Site get, config reads, capability discovery; create, restart, config writes, PHP-version switch. | always |
environments:write |
Create, delete, refresh, reset, push, suspend, resume environments. | env-capable |
deployments:write |
Trigger builds, deploy artifacts, promote, roll back. | always |
backups:write |
Take backups; restore snapshots. | always |
observability:read |
Insights, logs, traces, requests, resource metrics. | always |
security:read / :write |
Malware overview and scan history; trigger scans, restore or dismiss detections. | always |
vulnerabilities:read |
The vulnerability report — not implied by security:read. |
always |
jobs:read |
List and watch async jobs (incl. SSE). | always |
webhooks:read / :write |
Webhook endpoints, deliveries, and replays. | always |
cache:write |
Purge the page cache — all, by URL, or by tag. | always |
wp.cli:exec |
Denylisted WP-CLI through the exec endpoint — no raw shell. | runtime=wordpress |