nodes
Nodes are machines that joined the control plane, as their tenant sees them: a tenant sees the nodes the authorization projection holds for it. Every method except `list` is decided on `compute-node/{nodeId}`. Mutations take an optional `version`; when the node has moved past it, the call is `CONFLICT`. See [Operating nodes](/docs/guides/nodes/operating-nodes).
Nodes are machines that joined the control plane, as their tenant sees them: a tenant sees the nodes the authorization projection holds for it. Every method except list is decided on compute-node/{nodeId}. Mutations take an optional version; when the node has moved past it, the call is CONFLICT. See Operating nodes.
All 14 methods are served by this release.
| Method | Kind | Authorization | Phase | Status |
|---|---|---|---|---|
list | read | compute:nodes:read @ compute-tenant + items | v0.1 | registered |
get | read | compute:nodes:read @ compute-node | v0.1 | registered |
events | read | compute:nodes:read @ compute-node | v0.1 | registered |
cordon | mutation | compute:nodes:cordon @ compute-node | v0.1 | registered |
uncordon | mutation | compute:nodes:cordon @ compute-node | v0.1 | registered |
drain | mutation | compute:nodes:drain @ compute-node | v0.1 | registered |
label | mutation | compute:nodes:update @ compute-node | v0.1 | registered |
taint | mutation | compute:nodes:update @ compute-node | v0.1 | registered |
adopt | mutation | compute:nodes:provision @ compute-node | v0.1 | registered |
revokeIdentity | mutation | compute:nodes:revoke-identity @ compute-node | v0.1 | registered |
delete | mutation | compute:nodes:delete @ compute-node | v0.2 | registered |
issueSshCert | mutation | compute:nodes:ssh-root @ compute-node | v0.3 | registered |
revokeSshCert | mutation | compute:nodes:ssh-root @ compute-node | v0.3 | registered |
listSshCerts | read | compute:nodes:read | — | registered |
list
POST /api/compute/nodes/list
Lists the nodes visible to the tenant in id order, filtered by pool, site, cordoned or provisionState.
The call is decided once on compute-tenant/{tenantId}, then each node is checked for compute:nodes:read; nodes the caller may not read are left out, so a page can hold fewer than limit items. Pages hold up to 50 nodes.
- Kind: read
- Authorization:
compute:nodes:read@compute-tenant+ items - Module:
server - Phase: v0.1 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
poolId | string | at most 128 characters; matches ^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$ | |
siteId | string | at most 64 characters; matches ^site_[0-9a-hjkmnp-tv-z]{26,52}$ | |
cordoned | boolean | ||
provisionState | one of adopting, adopted, enrolling, inspecting, commissioning, ready, deploying, deployed, releasing, rescue, failed, retiring, retired | ||
limit | integer | 1-50; default 50 | |
cursor | string | at most 256 characters; matches ^[A-Za-z0-9._:-]{1,256}$ |
get
POST /api/compute/nodes/get
Returns one node with its labels, conditions and session (epoch, gateway and connection times).
A node the tenant cannot see is NOT_FOUND.
- Kind: read
- Authorization:
compute:nodes:read@compute-node - Module:
server - Phase: v0.1 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
nodeId | string | yes | at most 64 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
events
POST /api/compute/nodes/events
Returns a node's events from the event outbox, oldest first.
type keeps only events whose type starts with the given prefix, such as node. or security.. Pages hold up to 200 events (100 by default). A node the tenant cannot see is NOT_FOUND.
- Kind: read
- Authorization:
compute:nodes:read@compute-node - Module:
server - Phase: v0.1 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
nodeId | string | yes | at most 64 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
type | string | at most 64 characters; matches ^[a-z][a-z0-9.-]{0,63}$ | |
limit | integer | 1-200; default 100 | |
cursor | string | at most 41 characters; matches ^\d{1,20}:\d{1,20}$ |
cordon
POST /api/compute/nodes/cordon
Marks the node cordoned so placement puts no new work on it; existing allocations stay.
Cordoning a cordoned node returns it unchanged and publishes nothing. Otherwise the node's version goes up, its reconciler is queued, and node.cordoned is published.
- Kind: mutation
- Authorization:
compute:nodes:cordon@compute-node - Module:
server - Phase: v0.1 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
nodeId | string | yes | at most 64 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
version | integer | at least 1 | |
reason | string | at most 256 characters; matches ^[^\u0000-\u001f\u007f]*$ |
uncordon
POST /api/compute/nodes/uncordon
Clears cordoned and ends a drain in progress, so the node is placeable again.
Decided as compute:nodes:cordon. A node that is neither cordoned nor draining is returned unchanged. Otherwise the draining reason is removed and node.uncordoned is published.
- Kind: mutation
- Authorization:
compute:nodes:cordon@compute-node - Module:
server - Phase: v0.1 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
nodeId | string | yes | at most 64 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
version | integer | at least 1 | |
reason | string | at most 256 characters; matches ^[^\u0000-\u001f\u007f]*$ |
drain
POST /api/compute/nodes/drain
Cordons the node, adds the draining NoSchedule reason and hands the node to the Drain controller.
The Drain controller releases nothing in this release: it waits until the node holds no live allocation, then removes draining and publishes node.drained. The node stays cordoned until uncordon. Draining a node that is already draining returns it unchanged. A new drain publishes node.draining.
- Kind: mutation
- Authorization:
compute:nodes:drain@compute-node - Module:
server - Phase: v0.1 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
nodeId | string | yes | at most 64 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
version | integer | at least 1 | |
reason | string | at most 256 characters; matches ^[^\u0000-\u001f\u007f]*$ |
label
POST /api/compute/nodes/label
Sets and removes operator labels on the node and returns the node with its labels.
set takes up to 64 labels and remove up to 64 keys. The call is INVALID_INPUT when it names nothing, names a key in both set and remove, or sets a key that inventory or the system owns. remove removes only operator labels. The node's version goes up and node.labeled is published.
- Kind: mutation
- Authorization:
compute:nodes:update@compute-node - Module:
server - Phase: v0.1 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
nodeId | string | yes | at most 64 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
set | map of string | at most 64 entries; keys match ^[A-Za-z0-9](?:[A-Za-z0-9._/-]{0,126}[A-Za-z0-9])?$ | |
remove | list of string | 0-64 items; no repeats | |
version | integer | at least 1 |
taint
POST /api/compute/nodes/taint
Adds and removes operator NoSchedule taints, stored in noScheduleReasons as taint:<key>.
add and remove take up to 32 keys each. The call is INVALID_INPUT when it names nothing or names a key in both lists. Only operator taints change; conditions, unreachable and draining belong to their controllers. When the result equals the current reasons the node is returned unchanged; otherwise node.tainted is published.
- Kind: mutation
- Authorization:
compute:nodes:update@compute-node - Module:
server - Phase: v0.1 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
nodeId | string | yes | at most 64 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
add | list of string | 0-32 items; no repeats | |
remove | list of string | 0-32 items; no repeats | |
version | integer | at least 1 |
adopt
POST /api/compute/nodes/adopt
Re-runs acceptance checks on a node in adopting or adopted, moving it to adopting.
Decided as compute:nodes:provision. Any other provision state is INVALID_STATE. The node leaves placement until acceptance passes. Publishes node.adopting.
- Kind: mutation
- Authorization:
compute:nodes:provision@compute-node - Module:
server - Phase: v0.1 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
nodeId | string | yes | at most 64 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
version | integer | at least 1 | |
reason | string | at most 256 characters; matches ^[^\u0000-\u001f\u007f]*$ |
revokeIdentity
POST /api/compute/nodes/revokeIdentity
Revokes every live certificate of the node and raises its identity generation, so no certificate of the old generation can renew.
Returns the node and revokedCertificates, the number of certificates revoked; the revocation reason is reason, or identity revoked. Each call raises the generation again. The machine comes back only by joining again. Publishes security.node.identity-revoked.
- Kind: mutation
- Authorization:
compute:nodes:revoke-identity@compute-node - Module:
server - Phase: v0.1 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
nodeId | string | yes | at most 64 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
version | integer | at least 1 | |
reason | string | at most 256 characters; matches ^[^\u0000-\u001f\u007f]*$ |
delete
POST /api/compute/nodes/delete
Tombstones a node that is retired, adopting or enrolling (or failed from adopting or enrolling), or an adopted node that is cordoned, and has no open allocation. An adopted host has no retire workflow: cordon it, wait until it holds nothing, then delete it. For example, after its TPM or disk was replaced, deleting it lets the machine join again under a new identity.
Any other state, or an open allocation, is INVALID_STATE; a node that changed pools during the call is CONFLICT. The node's devices stop being bookable, its scalar resources are removed, its certificates are revoked, its hardware key and attestor bindings are freed so the machine can join again as a new node, and its pending commands are cancelled. The node leaves its pool and the projection, so nobody sees it; audit rows and past allocations keep referencing it.
A retry after the delete returns deleted: false. Publishes node.deleted.
- Kind: mutation
- Authorization:
compute:nodes:delete@compute-node - Module:
server - Phase: v0.2 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
nodeId | string | yes | at most 64 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
version | integer | at least 1 | |
reason | string | at most 256 characters; matches ^[^\u0000-\u001f\u007f]*$ |
issueSshCert
POST /api/compute/nodes/issueSshCert
Issues a short-lived break-glass OpenSSH user certificate for one node, bound to the principal <account>@<nodeId>.
Decided as compute:nodes:ssh-root. The method comes from the tunnels module (v0.3) and is UNSUPPORTED when no compute SSH CA is configured. It needs an MFA session by default (ACCESS_DENIED with reason mfa-required), and a session that ends within a minute is ACCESS_DENIED. publicKey must be an Ed25519 or ECDSA P-256 key, and principal (default root) one of the configured accounts; otherwise INVALID_INPUT.
The lifetime is ttlSeconds (60 to 3,600, default 900), never past the caller's session. Issuance is rate-limited per identity (20 per hour by default, then RATE_LIMITED). Every certificate is recorded and node.ssh-cert.issued is published.
- Kind: mutation
- Authorization:
compute:nodes:ssh-root@compute-node - Module:
tunnels - Phase: v0.3 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
nodeId | string | yes | at most 57 characters; matches ^node_[0-9a-z]{26,52}$ |
publicKey | string | yes | at most 4096 characters |
principal | string | matches ^[a-z_][a-z0-9_-]{0,31}$; default "root" | |
ttlSeconds | integer | 60-3600; default 900 |
revokeSshCert
POST /api/compute/nodes/revokeSshCert
Revokes a break-glass SSH certificate before it expires; the node's revocation list carries it from the next NodeSpec on.
Decided as compute:nodes:ssh-root and hosted by the tunnels module (v0.3). Revoking again returns the same revocation. A serial not issued for this node is NOT_FOUND. Publishes node.ssh-cert.revoked.
- Kind: mutation
- Authorization:
compute:nodes:ssh-root@compute-node - Module:
tunnels - Phase: v0.3 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
nodeId | string | yes | at most 57 characters; matches ^node_[0-9a-z]{26,52}$ |
serial | string | yes | matches ^[0-9]{1,20}$ |
reason | string | at most 500 characters |
listSshCerts
POST /api/compute/nodes/listSshCerts
Lists the break-glass SSH certificates the tenant issued for one node, newest first, with each one's principal, issuer, lifetime and state; never the certificate itself.
Decided as compute:nodes:read on compute-node/{nodeId} and hosted by the tunnels module. state is active, expired or revoked; a revoked certificate also carries revokedAt, revokedBy (an identity, or iam when re-authorization revoked it) and revokeReason when one was given.
Only unexpired certificates are listed unless includeExpired is true; expired certificates are kept for one day, then removed by an hourly clean-up. Pages hold 1 to 500 certificates (100 by default), and nextCursor is set while more remain. A node the tenant cannot see is NOT_FOUND, and a malformed cursor is INVALID_INPUT.
- Kind: read
- Authorization:
compute:nodes:read - Module:
tunnels - Phase: not in PLAN.md §9.2 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
nodeId | string | yes | at most 57 characters; matches ^node_[0-9a-z]{26,52}$ |
includeExpired | boolean | default false | |
limit | integer | 1-500; default 100 | |
cursor | string | at most 128 characters |
Better Compute is created by Sean Filimon
Last updated
metal
The metal group enrolls machines by their BMCs and moves them through the provision state machine: inspect, commission, deploy, rescue, release and retire. Each verb checks the move against the provision state table, records it in one transaction and queues the metal reconciler, which runs the workflow of the new state; no verb touches the machine itself. Every method returns the node's metal view, which reports whether BMC credentials are set but never returns them. See [Bare metal](/docs/guides/bare-metal) and [BMC drivers and credentials](/docs/guides/bare-metal/bmc).
pools
A pool groups the nodes of one site under one GPU model, one sharing mode and one policy, and every reservation and quota belongs to exactly one pool. The tenant that creates a pool owns it; other tenants see it only once the owner offers it to them with a quota. Only the owner can offer, unoffer, update or archive a pool; anyone else gets `NOT_FOUND`. See [Pools](/docs/guides/capacity/pools).