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).
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 and BMC drivers and credentials.
All 15 methods are served by this release.
| Method | Kind | Authorization | Phase | Status |
|---|---|---|---|---|
enroll | mutation | compute:nodes:provision @ compute-pool | v0.2 | registered |
manage | mutation | compute:nodes:provision @ compute-node | v0.2 | registered |
inspect | mutation | compute:nodes:provision @ compute-node | v0.2 | registered |
provide | mutation | compute:nodes:provision @ compute-node | v0.2 | registered |
deploy | mutation | compute:nodes:provision @ compute-node | v0.2 | registered |
undeploy | mutation | compute:nodes:provision @ compute-node | v0.2 | registered |
rescue | mutation | compute:nodes:provision @ compute-node | v0.2 | registered |
unrescue | mutation | compute:nodes:provision @ compute-node | v0.2 | registered |
abort | mutation | compute:nodes:provision @ compute-node | v0.2 | registered |
recover | mutation | compute:nodes:provision @ compute-node | v0.2 | registered |
retire | mutation | compute:nodes:provision @ compute-node | v0.2 | registered |
power | mutation | compute:nodes:power @ compute-node | v0.2 | registered |
bulk | mutation | compute:nodes:provision @ compute-pool | v1.0 | registered |
confirmMac | mutation | compute:nodes:provision @ compute-node | v1.0 | registered |
discovered | read | compute:sites:read @ compute-site | v1.0 | registered |
enroll
POST /api/compute/metal/enroll
Creates a metal node from a BMC address, driver and credentials in the given pool, in provision state enrolling, and queues the reconciler to verify BMC access and inspect the machine out of band.
- Decided as
compute:nodes:provisionon the pool, since the node does not exist yet. bmc.credentialsis asecret://reference or an inline username and password. Inline credentials go to the secret broker before anything is written, and only the reference is stored; without a secret broker they areINVALID_CONFIG.- The node's driver bindings come from the BMC (power, management, virtual media), then the hardware profile (
hardwareProfile, else the pool'spolicy.hardwareProfile). An unknown hardware profile isINVALID_INPUT. bmc.driverOptionsis a JSON object of at most 1 KiB for the BMC driver (e.g.{ "cipherSuite": 3 }for older IPMI BMCs), sent on every BMC request and validated by the driver. Keys that look like credentials areINVALID_INPUT: credentials travel only asbmc.credentials.addressesare the machine's own IP literals (at most 16). In an agentless pool the site's agents can reach the machine through them at once; out-of-band inspection replaces them only when it reports some (IPMI reports none).- A driver that cannot report MACs (IPMI) needs
macs, orproductUuid(the SMBIOS UUID its BMC reports). With the UUID alone, the first boot PXE-boots the machine once with no boot entry, and PXE discovery proposes the MAC its site agent then sees (seediscoveredandconfirmMac). A firmware placeholder UUID (all zeros, all ones,03000200-0400-0500-0006-000700080009) cannot stand in for the MACs. NOT_FOUNDwhen the pool does not exist or the tenant does not own it;INVALID_INPUTwhen the pool is not insiteId.
Each call creates a new node; there is no idempotency key. Publishes node.enrolled.
- Kind: mutation
- Authorization:
compute:nodes:provision@compute-pool - Module:
metal - Phase: v0.2 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
siteId | string | yes | 1-128 characters |
poolId | string | yes | 1-128 characters |
bmc | object | yes | |
bmc.address | string | yes | 1-512 characters |
bmc.driver | string | yes | matches ^[a-z][a-z0-9-]{0,63}$ |
bmc.credentials | string,object | yes | at most 2048 characters; matches ^(secret|vault|env)://\S+$ |
bmc.tlsSpkiSha256 | string | matches ^[0-9A-Fa-f]{64}$ | |
bmc.driverOptions | object | ||
macs | list of any | 0-16 items | |
addresses | list of string | 0-16 items | |
productUuid | string | 1-64 characters | |
arch | one of x86_64, arm64 | default "x86_64" | |
uefi | boolean | default true | |
hardwareProfile | string | 1-64 characters |
manage
POST /api/compute/metal/manage
Moves a ready node back to enrolling to re-verify BMC access; the reconciler then chains re-inspection and re-commissioning back to ready.
INVALID_STATE from any state but ready, NOT_FOUND when the tenant has no such metal node, and CONFLICT when the node changed concurrently. Publishes node.provision.enrolling.
- Kind: mutation
- Authorization:
compute:nodes:provision@compute-node - Module:
metal - 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 57 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
inspect
POST /api/compute/metal/inspect
Moves a ready node to inspecting, where the machine boots the ramdisk and reports its inventory; commissioning follows, and the node returns to ready.
INVALID_STATE from any state but ready; CONFLICT when the node changed concurrently. Publishes node.provision.inspecting.
- Kind: mutation
- Authorization:
compute:nodes:provision@compute-node - Module:
metal - 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 57 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
provide
POST /api/compute/metal/provide
Moves a ready node to commissioning, which runs DCGM diagnostics, a quick wipe and a power-off before the node returns to ready.
INVALID_STATE from any state but ready; CONFLICT when the node changed concurrently. Publishes node.provision.commissioning.
- Kind: mutation
- Authorization:
compute:nodes:provision@compute-node - Module:
metal - 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 57 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
deploy
POST /api/compute/metal/deploy
Moves a ready node to deploying with the given profile version recorded on it; the metal reconciler then starts a workflow of that profile.
The call records profileId and profileVersion without running the profile; run profiles.validate first to catch problems before the workflow starts. When the workflow succeeds the node becomes deployed and takes the profile's role. INVALID_STATE from any state but ready; CONFLICT when the node changed concurrently. Publishes node.provision.deploying.
- Kind: mutation
- Authorization:
compute:nodes:provision@compute-node - Module:
metal - 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 57 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
profileId | string | yes | 1-128 characters |
profileVersion | integer | yes | at least 1 |
undeploy
POST /api/compute/metal/undeploy
Moves a deployed or rescue node to releasing, which wipes its disks with the chosen mode and powers it off before it returns to ready.
wipe is none, quick (the default), secure-erase or crypto-erase. Unless the mode is none, the node reaches ready only when the wipe result is attested; otherwise it fails with UNVERIFIED_WIPE. INVALID_STATE from other states. Publishes node.provision.releasing.
- Kind: mutation
- Authorization:
compute:nodes:provision@compute-node - Module:
metal - 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 57 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
wipe | one of none, quick, secure-erase, crypto-erase | default "quick" |
rescue
POST /api/compute/metal/rescue
Moves a deployed node to rescue, whose workflow boots the rescue ramdisk for repairs from outside the installed OS.
INVALID_STATE from any state but deployed. On a pool with agent: 'none' the node fails with UNSUPPORTED, since such a machine cannot boot the ramdisk. Publishes node.provision.rescue.
- Kind: mutation
- Authorization:
compute:nodes:provision@compute-node - Module:
metal - 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 57 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
unrescue
POST /api/compute/metal/unrescue
Starts the metal-unrescue workflow, which boots a rescued node back into its installed OS; the node stays in rescue until that workflow succeeds, then becomes deployed.
INVALID_STATEoutsiderescue.- A second call while the unrescue workflow runs returns the node unchanged.
CONFLICTwhile the rescue ramdisk is still booting.INVALID_CONFIGwhen the provision module is not registered.
Publishes node.provision.unrescuing with the workflowId. If the workflow fails, the node goes to failed, and recover returns it to deployed.
- Kind: mutation
- Authorization:
compute:nodes:provision@compute-node - Module:
metal - 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 57 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
abort
POST /api/compute/metal/abort
Stops the node's running workflow and moves the node to failed, recording where it failed, an ABORTED error with the optional reason, and maintenance on.
Legal from enrolling, inspecting, commissioning, deploying and releasing; INVALID_STATE otherwise. The aborted workflow still runs its cleanup steps. Publishes node.provision.failed.
- Kind: mutation
- Authorization:
compute:nodes:provision@compute-node - Module:
metal - 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 57 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
reason | string | 1-512 characters |
recover
POST /api/compute/metal/recover
Moves a failed node to the last stable point for the state it failed in: enrolling after enrolling, inspecting or commissioning, releasing after deploying or releasing, deployed after rescue, and retiring after retiring.
It clears failedFrom and lastError but leaves maintenance on. INVALID_STATE when the node is not failed. Publishes node.provision.enrolling, node.provision.releasing, node.provision.deployed or node.provision.retiring.
- Kind: mutation
- Authorization:
compute:nodes:provision@compute-node - Module:
metal - 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 57 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
retire
POST /api/compute/metal/retire
Moves a node to retiring, whose workflow crypto-erases the disks (on pools whose machines run bcd), powers the machine off and revokes its identity before the node becomes retired, which is terminal.
Legal from adopting, adopted, enrolling, ready and failed; INVALID_STATE otherwise, so undeploy a deployed machine first. Publishes node.provision.retiring.
- Kind: mutation
- Authorization:
compute:nodes:provision@compute-node - Module:
metal - 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 57 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
power
POST /api/compute/metal/power
Queues a bmc.power command to the site agent of the node's site and returns its commandId; the provision state does not change.
- Decided as
compute:nodes:poweron the node. actionison,off,graceful-off,reset,graceful-restart,cycleornmi.INVALID_STATEwhen the node has no BMC credentials, while it isinspecting,commissioning,deployingorreleasing(a workflow controls its power then), or when no site agent serves its site.
The command expires five minutes after it is queued. Its idempotency key derives from the request id, so a retry with the same X-Request-Id header returns the same command.
- Kind: mutation
- Authorization:
compute:nodes:power@compute-node - Module:
metal - 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 57 characters; matches ^node_[0-9a-hjkmnp-tv-z]{26,52}$ |
action | one of on, off, graceful-off, reset, graceful-restart, cycle, nmi | yes |
bulk
POST /api/compute/metal/bulk
Runs one lifecycle verb on many nodes of a pool at once (manage, inspect, provide, undeploy, rescue or retire), as one batch; returns every node and, when the verb sends destructive commands, the batch.
- Each node is decided on its own, exactly as if the verb were called for it (
metal.<verb>); one refusal refuses the whole call before anything changes. Every node must be inpoolId(INVALID_INPUTotherwise), listed once, 1 to 10,000 of them. - When the verb's steps send destructive commands, a quorum batch (
commandBatches) is created over them withreason, and each node's commands carry it: with approver keys they wait for its approval; without, it is recorded for audit. wipeapplies toundeployonly.
Decided as compute:nodes:provision on the pool, then per node.
- Kind: mutation
- Authorization:
compute:nodes:provision@compute-pool - Module:
metal - Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
poolId | string | yes | 1-128 characters |
verb | one of manage, inspect, provide, undeploy, rescue, retire | yes | |
nodeIds | list of string | yes | 1-10000 items; no repeats |
wipe | one of none, quick, secure-erase, crypto-erase | default "quick" | |
reason | string | yes | 1-512 characters |
confirmMac
POST /api/compute/metal/confirmMac
Binds the PXE MAC of a machine enrolled without MACs, usually the candidate PXE discovery proposed (its discoveredMac), or any MAC the operator knows to be the machine's. The waiting boot then goes on from that MAC.
-
Decided as
compute:nodes:provisionon the node. Publishesnode.pxe-mac.confirmedwith the MAC and whether it was the discovered candidate. -
INVALID_STATEwhen the node already has MACs or is retired.CONFLICTwhen another machine of the site has the MAC. -
Until a MAC is confirmed, a machine known only by its UUID never gets a boot entry or a discovery token. A pool whose
policy.trustProvisioningNetworkistruebinds the candidate at once instead. Use it only on a provisioning network no untrusted host can reach. Its ramdisk's reported UUID and NICs are then checked against the machine before anything is written, and a mismatch fails the workflow. -
Kind: mutation
-
Authorization:
compute:nodes:provision@compute-node -
Module:
metal -
Phase: v1.0 (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-hjkmnp-tv-z]{26,52}$ |
mac | string | yes | matches ^[0-9A-Fa-f]{2}([:-][0-9A-Fa-f]{2}){5}$ |
discovered
POST /api/compute/metal/discovered
Lists the machines the site's agents saw PXE-booting from a MAC with no boot entry in the last 24 hours, newest first. The agents never answer or enroll them.
-
Decided as
compute:sites:readon the site, and only the tenant that owns the site sees it (NOT_FOUNDotherwise): the list describes the site's network. -
Each machine has its
mac, theproductUuidits DHCP request carried (as sent: SMBIOS stores the first three fields byte-swapped), itsarch, when it was first and last seen, a requestcountsummed over agents, the DHCPrelaysit came through, and theagentNodeIdsthat saw it. -
nodeIdnames the enrolled machine with that MAC.candidateFornames the machine enrolled without MACs whose candidate it is, awaitingconfirmMac. -
The list alone binds nothing: any host on the network can claim any MAC and UUID. A MAC becomes a machine's candidate only when metal has just power-cycled that machine into PXE with its BMC. The MAC must be first seen within 10 minutes after that cycle, and be the only MAC its product UUID (in any byte order) was seen from in 24 hours. No other machine of the site may share the UUID, and the MAC must not be enrolled already. This publishes
node.pxe-mac.discovered. -
At most 256 machines per agent, as each agent's last status report lists them.
-
Kind: read
-
Authorization:
compute:sites:read@compute-site -
Module:
metal -
Phase: v1.0 (registered)
Input
| Field | Type | Required | Constraints |
|---|---|---|---|
tenantId | string | yes | at most 128 characters; matches ^[A-Za-z0-9._:-]{1,128}$ |
siteId | string | yes | 1-128 characters |
Better Compute is created by Sean Filimon
Last updated
joinTokens
Join tokens are single-purpose credentials that let a limited number of daemons join one pool for a limited time. The bearer form `bcjt_<id>.<secret>` is returned once, when the token is created; the control plane stores only the SHA-256 of the secret. Every method is decided on `compute-pool/{poolId}`. See [Joining nodes](/docs/guides/nodes/joining).
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).