Skip to content

API Key Scopes ​

An API key authenticates a program — CI, a script, Terraform — against the Frostmoln API. Every key carries scopes: the list of things it is allowed to do. Frostmoln keys are least-privilege, so a key does nothing until you say what it may do.

The list is served by the platform ​

There is no fixed list to copy out of this page. The catalog lives in the platform, so it always matches what key creation accepts. Read it on whichever surface you work in:

WhereHow
PortalSettings → API Keys → Create API Key. The Permissions picker lists every scope with a description of what it grants.
CLIfm account api-key scopes (add -o json to script it).
TerraformThe frostmoln_api_key_scopes data source — terraform console, then data.frostmoln_api_key_scopes.all.scopes.

What a scope looks like ​

A scope is <service>:<action>:

text
compute:read     read compute resources
compute:write    create, change and delete them
storage:read     read volumes and buckets
billing:read     read invoices and usage

For almost every service, read and write are the two actions the API enforces (the interactive-session scopes are a separate class — see below). write covers every mutation — create, update, delete, and actions such as start/stop/restart. There is no separate "create but not delete" scope: when you need that distinction, use an access policy.

Grant read and write, not the finer forms

The catalog also lists three-part entries such as compute:instances:read, and key creation accepts them. They are not enforced as key scopes — they belong to the access policy engine. A key holding only compute:instances:read is created successfully and then denied on every compute request, with a 403 that does not explain why.

The same applies to most of the other verbs the catalog carries (action, create, update, delete, list): they can be granted, but the services check :read and :write. Grant those.

The interactive-session scopes are a class of their own. sessions:forward, sessions:pty and sessions:write are real scope strings, because a session is its own privilege rather than an operation on the resource it reaches. Nothing else stands in for them: compute:action is not the scope that opens a shell on an instance, and kubernetes:* is not the one that opens the cluster-API proxy. Grant them to any key that runs fm forward, fm database <type> console, fm shell or fm kubernetes cluster proxy.

They are checked when the key is created: you cannot give a key a scope your own role does not have, so sessions:pty needs the Owner, Administrator, Developer or Member role. They are not yet checked when a session is opened — today a key that lacks them can still open one. Grant them now, so your keys are already right when that check goes live.

Wildcards ​

ScopeAPI keyWorkload identity
* (everything)Rejected — WILDCARD_SCOPE_NOT_ALLOWEDRejected — WILDCARD_SCOPE_FORBIDDEN
compute:* (one service)AllowedRejected — WILDCARD_SCOPE_FORBIDDEN
compute:readAllowedAllowed

A Workload Identity binding is deliberately stricter than an API key: a federated pod credential must name what it needs.

Note that compute:* is a genuine grant of everything that service offers, now and in future. Two named scopes are almost always the better choice.

Rules worth knowing ​

  • At least one scope is required. An empty list is rejected with SCOPES_REQUIRED, and an unrecognised scope with INVALID_SCOPES (on key creation; INVALID_SCOPE when updating a key or a workload-identity binding).
  • A key can never exceed you — at the moment you create it. Scopes are capped at the permissions of the identity creating the key; asking for more fails with SCOPE_EXCEEDS_PERMISSIONS. This is checked when the key is created or its scopes change. If your permissions are reduced later, keys you already issued keep the scopes they were granted — revoke and re-issue them.
  • Organization control needs a signed-in person, not a key. Deleting an organization, changing a member's role, removing a member, leaving an organization and transferring ownership are refused for every API key, whatever the key is scoped to — even *. A key holding organizations:write gets USER_SESSION_REQUIRED; a key without it gets insufficient_scope first. Do these in the portal while signed in. Leaving is not in the portal yet, so ask an admin or the owner to remove you (an admin must ask the owner); an owner must transfer ownership first and then ask the new owner. Other organization changes, such as inviting members, need organizations:write on the key as well as an organization role that allows them.
  • A key reaches one organization: the one that owns its tenant. Even if you belong to several organizations, a key created in a tenant of one of them cannot read or change another. Listing organizations with the key shows only that one, and any other organization is refused with ORGANIZATION_ACCESS_DENIED. Create a key in a tenant of each organization you automate.
  • Most fm tenant commands need organizations:read. Listing tenants, switching with fm tenant use and reading default tags with fm tenant default-tags get need organizations:read. Creating or renaming a tenant, or changing its default tags, needs organizations:write, and creating one without --organization also needs organizations:read. fm tenant current needs neither. fm and the portal cannot change an existing key's scopes (the Terraform frostmoln_api_key resource can update a key it created): create a new key, switch to it with fm auth api-key set, then revoke the old one with fm account api-key revoke.
  • Quota change requests need quotas:write. Submitting or cancelling a request with fm tenant quota request needs quotas:write, and listing requests needs quotas:read. compute:write and compute:* do not grant it: an approved tenant raise lifts a spending ceiling, which is a different grant from creating resources. Existing keys did not get the new scope, so automation that submits quota requests needs a new key with quotas:write. A workload identity can hold quotas:read but never quotas:write. Reading the quotas themselves (fm tenant quota, the frostmoln_tenant_quotas data source) needs neither.

Choosing scopes ​

Start from the job the key does, not the service it talks to.

The key does thisGrant
Monitoring / inventory collectioncompute:read, storage:read, network:read — the reads of what it inspects
CI that deploys instancescompute:read, compute:write
Restart a service on a schedulecompute:read, compute:write (an action is a mutation)
Terraform managing a stackread and write for each service in the stack — a plan reads before it changes anything
Read invoices for accountingbilling:read
Request quota changesquotas:read, quotas:write

Two habits keep keys safe: give a key one job rather than reusing a broad one, and set an expiry so a forgotten key stops working by itself.

Examples ​

bash
# See what you can grant, then create the key
fm account api-key scopes
fm account api-key create --name "ci-deploy" \
  --scopes compute:read,compute:write \
  --expires 2027-01-01
hcl
data "frostmoln_api_key_scopes" "all" {}

resource "frostmoln_api_key" "ci" {
  name   = "ci-deploy"
  scopes = ["compute:read", "compute:write"]
}

The key itself is shown once, at creation. Store it in your secret manager straight away — it cannot be retrieved later, only replaced.

When scopes are not enough ​

Scopes say what kind of call a key may make, never to which resource or from where. When you need "may create instances but never delete them", "only in one region", "only from the office network", or an explicit deny, use an access policy. That is where the fine-grained service:resource:action operations apply, and a policy attaches to the same API keys.