Keys and scopes
A key says who is calling and what it may do. Give every program and every agent a key of its own, holding only what it needs.
wm
$ wm keys create agent --scope machines:read,machines:write,machines:exec
id: key_8c41d07e2b9a6f13
name: agent
scopes: cloud:machines:read, cloud:machines:write, cloud:machines:exec
projects: every project
expires: never
secret: ak_live_...
wm: store the secret now: it is shown only this once, and cannot be shown again.Python
key = client.keys.create(
name="agent",
scopes=["cloud:machines:read", "cloud:machines:write", "cloud:machines:exec"],
expires_at="2026-12-31T23:59:59Z",
)
# key.secret is in this answer only: store it nowTypeScript
const key = await client.keys.create({
name: "agent",
scopes: ["cloud:machines:read", "cloud:machines:write", "cloud:machines:exec"],
expires_at: "2026-12-31T23:59:59Z",
});
// key.secret is in this answer only: store it nowcurl
curl -X POST https://api.wheelmachine.ai/v1/keys \
-H "Authorization: Bearer $WHEELMACHINE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "agent", "scopes": ["cloud:machines:read", "cloud:machines:write", "cloud:machines:exec"]}'A key#
A key is ak_live_ and 40 letters and digits, sent with every call as
Authorization: Bearer ak_live_.... Its secret is shown once, in the answer that makes it; after that the
API shows only its first and last characters (shown), to know it by. It also has an id
(key_ and 16 letters and digits) and a name, for people. wm me (GET /v1/me)
shows the key in use: its scopes, its projects, when it ends, and whether the account can spend.
Scopes#
| Scope | In wm | What the key may do |
|---|---|---|
cloud:machines:read | machines:read | See machines, wait for them, and see what they cost (usage). |
cloud:machines:write | machines:write | Make, rename, label, start, pause, stop, archive, copy, snapshot and delete machines, and see them. |
cloud:machines:exec | machines:exec | Run commands in machines and read and write their files, and see them. A command can read its environment, so such a key can read the secrets a machine was given. |
cloud:gpus | gpus | Make machines with a GPU, once they are open to the account. |
cloud:projects:write | projects:write | Make, rename and delete projects. |
cloud:keys:write | keys:write | List, make and revoke keys. |
cloud:secrets:write | secrets:write | Set and delete the account's secrets, and give them to machines (a key of the whole account). |
cloud:* | all | All of these. |
Any key with one of these scopes can see who it is (wm me), its projects, the regions and the GPUs.
A call the key's scopes do not allow answers 403 forbidden, naming
the scope it needs. A key that holds none of these scopes is refused on every route here.
A key never makes a stronger one#
A new key is made by a key that holds cloud:keys:write, and it can hold no more than that key:
- only scopes the making key holds (
cloud:*only from a key that holdscloud:*); - only projects the making key reaches;
- an end no later than the making key's own;
- no more calls a minute than the making key may make.
Asking for more answers 403 forbidden. So a key handed to a
program, or to an agent, cannot be used to make a key that does more than it.
Keys for some projects#
$ wm keys create ci --scope machines:write,machines:exec --project researchA key limited to some projects works inside them alone:
- it sees and makes machines only in its projects; to it, a machine anywhere else does not exist
(
404 not_found); - when it reaches more than one project, it names the project of each machine it makes;
- it cannot make projects;
- it sees, makes and revokes only keys limited to its projects.
A key that names no projects reaches every project of the account.
End time and rate#
$ wm keys create nightly --scope machines:write --expires 30d --rate 120--expires is a while (30d, 12h) or a date (2026-12-31, or a
time such as 2026-12-31T18:00:00Z); in the API, expires_at is a time, at least a minute
ahead. A key past its end answers 401 key_expired, and its end
cannot be moved: make a new key.
--rate (rate_per_minute) is the calls a minute the key may make, from 1 to 6000. Left
out, the new key gets the making key's own rate, which is 600 for a key made without one. See
Calls a minute.
Revoke#
$ wm keys revoke agent
Revoke key_8c41d07e2b9a6f13? Whatever uses it stops working. [y/N] y
key_8c41d07e2b9a6f13A revoked key stops working within seconds, everywhere, and cannot be brought back; calls with it answer
401 key_revoked. A key can revoke itself. wm logout is
not the same: it only forgets the key on this computer, and the key still works until it is revoked.
wm keys ls lists the account's machine keys, with their scopes, projects, state and when each was last
used; never their secrets.
Where keys come from#
For now, an account's first key is made in Wheelmachine's console by the account's owner. After that, a key that
holds cloud:keys:write makes the others, with wm keys create, the libraries or
POST /v1/keys.
Keep keys safe#
wm loginkeeps a key in~/.config/wheelmachine/config.json(under$XDG_CONFIG_HOMEwhen that is set), in a file only you can read.WHEELMACHINE_API_KEY, when set, is used before it.- Never put a key in a web page: anyone who opens the page can read it. The TypeScript library refuses to run in a browser for this reason. Call the API from your server.
- Give each program and each agent a key of its own, with only the scopes it needs and an end time, and revoke it when the work is done.
wm,wm-mcpand the libraries never log a key and keep it out of their errors. The one secretwmprints is a new key's, once, when it is made.