better/compute

CLI

The better-compute command line 0.1.1: 189 commands.

Run better-compute help <command> for the same text in a terminal, or better-compute help --json for this manifest.

Setup

init

Write a starter configuration file

better-compute init [--config better-compute.config.ts]

init writes a starter better-compute.config.ts (or the JavaScript form when --config names a .mjs or .js file): a configuration factory that uses PGlite in a local .better-compute-pglite directory for development and PostgreSQL when BETTER_COMPUTE_DATABASE_URL is set, reads the secret and base URL from the environment, shows where better-iam plugs in, and exports an empty list of project commands. It never overwrites an existing file.

FlagDescriptionDefaultEnvironment
--config PATHWhere to write the configuration (.ts, .mts, .js or .mjs)better-compute.config.ts
better-compute init
better-compute init --config better-compute.config.mjs

migrate

Create or upgrade the database schema and apply module migrations

better-compute migrate [--config PATH]

migrate runs compute.initialize(): it checks every method's IAM action against the merged catalog and the normative method table (INVALID_CONFIG on a mismatch), then creates or upgrades the compute schema and applies every module's and plugin's migrations. Run it on every deploy before the application starts; it is idempotent.

FlagDescriptionDefaultEnvironment
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
better-compute migrate
BETTER_COMPUTE_DATABASE_URL=pglite:./data better-compute migrate

doctor

Check the database, schema, clock, method registry and IAM wiring

better-compute doctor [--strict] [--pki PATH] [--config PATH]

doctor runs compute.selfCheck() (applied migrations, btree_gist, compute_now() against the database clock, the method → action registry, and each module's own checks) and adds the CLI's findings, such as a deployment with no IAM configured, the PKI bundle, and the configuration's host export (whether the gateway role can authenticate daemons by node certificate, reach the api's pokes, tunnels and revocations, and whether host routes fit beside the API). It only reads. --strict exits non-zero (DOCTOR_FINDINGS) when any error or warning is found.

FlagDescriptionDefaultEnvironment
--strictExit non-zero when any error or warning is found
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute doctor
better-compute doctor --strict --query "findings[].check"

version

Print the better-compute version

better-compute version

version prints the version of this command line (also --version or -v).

completion

Print a shell completion script (bash, zsh or fish)

better-compute completion SHELL

completion prints a script that completes commands, their flags and the values of flags with choices, generated from this program's commands (a configuration's project commands included). Load it in your shell's startup file.

source <(better-compute completion bash)
source <(better-compute completion zsh)
better-compute completion fish | source

serve

Run the API and the workers in this process

better-compute serve [--host HOST] [--port PORT] [--roles ROLES] [--no-migrate] [--config PATH]

serve loads the configuration, migrates (skip with --no-migrate once a deploy step runs better-compute migrate), starts the worker roles (controllers: reconcilers and the command sweeper; scheduler; sinks: outbox dispatch; iam: IAM jobs) under their leases, and serves the API with compute.nodeHandler on --host:--port. Run several processes against one database: leases and shard ownership keep each job single-writer. --roles api serves the API only; --roles controllers,sinks runs a worker with no listener; --roles gateway runs the daemon gateway over mTLS from the configuration's host.gateway (all includes it when configured), with the report hooks of the built-in modules and tunnels when the deployment issues exec tickets. Beside the API the listener answers /healthz, /readyz, /.well-known/better-compute (the server's document, with JoinService's public keys when pki.join publishes them: the fallback bcd checks against its pins) and the host export's own routes (for example GET /watch and GET /metrics). SIGINT or SIGTERM drains the gateway first, then stops the listener and the workers, releasing their leases.

FlagDescriptionDefaultEnvironment
--host HOSTThe address to listen on0.0.0.0HOST
--port PORTThe port to listen on (0 picks a free one)3000PORT
--roles ROLESall, or a comma list of api, gateway and controllers, scheduler, sinks, iamallBETTER_COMPUTE_ROLES
--no-migrateDo not run migrations first
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
better-compute serve
better-compute serve --roles api --port 8080
better-compute serve --roles controllers,sinks,iam --no-migrate
better-compute serve --roles gateway,controllers

dev

Run a local development server with no setup

better-compute dev [--host HOST] [--port PORT] [--data DIR] [--memory] [--sim N] [--sim-variant VARIANT] [--config PATH]

dev runs the API and every worker role on 127.0.0.1. With a configuration file (or BETTER_COMPUTE_DATABASE_URL) it uses that deployment; without one it keeps PGlite in --data (default .better-compute/dev-db; --memory for a throwaway database) and a generated deployment secret in .better-compute/dev-secret (owner-only), so repeated runs keep their data. It always migrates first. Without IAM configured only public methods answer; see better-compute iam setup. --sim N adds a gateway on loopback cleartext HTTP/2 (sessions authenticate by a header; local development only) and N simulated machines that join the way bcd does: a join token sealed to throwaway dev JoinService keys, the Join RPC, a Session stream, diag.nvml acceptance and adoption by the real runtime. Without configured IAM, --sim starts an embedded in-memory better-iam and prints its root token; with configured IAM it acts as BETTER_COMPUTE_TOKEN, which must be a root administrator.

FlagDescriptionDefaultEnvironment
--host HOSTThe address to listen on127.0.0.1
--port PORTThe port to listen on (0 picks a free one)3000PORT
--data DIRWhere PGlite keeps the development database (no configuration only).better-compute/dev-db
--memoryUse a throwaway in-memory database
--config PATHThe configuration file (default: discovered)BETTER_COMPUTE_CONFIG
--sim NJoin N simulated machines (FakeDaemons) through a loopback dev gateway (needs @better-compute/testing)
--sim-variant VARIANTHardware of the simulated machines: hgx-h100, hgx-h100-gpu-missing, l40s, cpuhgx-h100
better-compute dev
better-compute dev --memory --port 0
better-compute dev --memory --sim 8
better-compute dev --sim 4 --sim-variant l40s

sim

Run the deterministic fleet simulator and check its invariants

better-compute sim [--seed SEED] [--nodes N] [--duration DURATION] [--settle DURATION] [--chaos LEVEL] [--log PATH]

sim runs --nodes FakeDaemons on fake hardware against the in-memory gateway stub for --duration of simulated time, with a steady workload (diagnostics, inventory refreshes, log collection, reboots, GPU bookings) and --chaos faults (none, light, heavy: crashes, reboots, journal loss, partitions, half-open links, resets, stalls, gateway kills), then quiesces for --settle and checks invariants I1 (no double booking), I2 (at-most-once side effects), I5 (no allocation released only because its node disconnected) and I8 (usage). Nothing reads the wall clock: the same --seed prints the same digest. It exits SIM_INVARIANT_VIOLATED when an invariant fails. Needs @better-compute/testing.

FlagDescriptionDefaultEnvironment
--seed SEEDThe seedsim
--nodes NSimulated nodes50
--duration DURATIONSimulated time under workload10m
--settle DURATIONQuiet time afterwards (sweeper and replays settle)16m
--chaos LEVELnone, light or heavylight
--log PATHWrite the event log to a new file
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sim
better-compute sim --seed nightly-42 --nodes 500 --duration 1h --chaos heavy

support bundle

Write a redacted diagnostics bundle for a support request

better-compute support bundle [--out PATH] [--pki PATH] [--config PATH]

support bundle collects what a support request about the control plane needs: the CLI, Node and platform versions; the configuration's shape (database kind, modules, whether IAM, PKI, tunnels and a notifier are configured; never URLs or values); doctor's findings; applied migrations; gateway and worker heartbeats; work-queue, command and IAM-inbox backlogs; and node counts by state. Environment variables appear by name only. Before anything is written, the bundle is scanned for join tokens (bcjt*), IAM tokens (biam*), JWTs, private keys, URL passwords and the deployment's own secrets, and each is replaced; a bundle in which a secret survives is refused (SUPPORT_BUNDLE_LEAK). The file is written owner-only. It only reads.

FlagDescriptionDefaultEnvironment
--out PATHWhere to write it (default better-compute-support-<time>.json; - prints it)
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute support bundle
better-compute support bundle --out - > bundle.json

API

api

Call any compute API method as the current token

better-compute api GROUP.METHOD [key=value ...] [--data JSON|@FILE|-] [--list] [--config PATH] [--url URL]

api calls any method of the HTTP API (the same routes, envelope and permission checks as POST {basePath}/{group}/{method}) as BETTER_COMPUTE_TOKEN, in process through the configuration or against --url, and prints the result. Public methods (system.version, system.whoami) need no token; system.whoami reports the token when one is set. Build the input with --data (JSON, @file.json, or - for standard input) and key=value items: name=gpu-a sets a string, limit:=10 or labels:='["a","b"]' sets JSON, spec:=@spec.json reads a JSON file, notes=@notes.md reads a text file, resource.type=pool nests, and ids[]=a appends. Plugin methods are plugins.ID.GROUP.METHOD. api --list [GROUP] lists every route with whether it needs a token and the IAM action it is decided on.

| Flag | Description | Default | Environment | | ----------------- | ------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------- | --- | --- | | --data JSON | @FILE | - | Request body as JSON, a JSON file, or standard input | | | | --list | List routes (optionally of one group) instead of calling one | | | | --config PATH | Configuration module; default: the nearest better-compute.config.{mjs,js,ts,mts} | | BETTER_COMPUTE_CONFIG | | --url URL | Call a running compute server (https://host or https://host/api/compute) instead of loading the configuration | | BETTER_COMPUTE_URL | | --format FORMAT | Output as json (indented, default), compact (one line), or table | | | | --query PATH | Print only part of the result, e.g. protocol.max or findings[].check (strings print raw) | | |

better-compute api --list
better-compute api system.version
better-compute api system.selfCheck --query "findings[].check"
better-compute api system.whoami --url https://compute.example.com
better-compute api inventory.get tenantId=tenant-acme nodeId=node_01jz0000000000000000000000
echo '{"tenantId":"tenant-acme","widgetId":"w1"}' | better-compute api plugins.acme.widgets.get --data -

Access

auth login

Save a token for later commands (checked with system.whoami)

better-compute auth login [--profile NAME] [--no-verify] [--config PATH] [--url URL]

auth login reads an IAM session token, API key or session JWT from standard input (or BETTER_COMPUTE_TOKEN), checks it with system.whoami against the deployment (--url, or the configuration in process; --no-verify skips the check), and saves it under --profile in better-compute's credentials file (BETTER_COMPUTE_CREDENTIALS, else %APPDATA%\better-compute or ~/.config/better-compute), written owner-only. Token commands then use it when BETTER_COMPUTE_TOKEN is unset and no better-iam session is saved. A session that better-iam's own CLI saved (better-iam login) is used without this step.

FlagDescriptionDefaultEnvironment
--profile NAMEThe saved profile (default: the current one, else default)BETTER_COMPUTE_PROFILE
--no-verifySave without calling system.whoami
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute auth login --url https://compute.example.com < token.txt
BETTER_COMPUTE_TOKEN=biam_key_… better-compute auth login --profile ci --no-verify

auth status

Show which credential token commands use (never the token)

better-compute auth status [--profile NAME]

auth status says where token commands get their credential, in PLAN §9.3's order: BETTER_COMPUTE_TOKEN, else better-iam's saved session (better-iam login, when @better-iam/cli is installed), else better-compute's saved profile. It lists the saved profiles without their tokens.

FlagDescriptionDefaultEnvironment
--profile NAMEThe saved profile (default: the current one, else default)BETTER_COMPUTE_PROFILE
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute auth status

auth logout

Forget a saved profile's token

better-compute auth logout [--profile NAME]

auth logout removes a profile from better-compute's credentials file (the current one by default). It does not revoke the session in better-iam: run better-iam logout for that.

FlagDescriptionDefaultEnvironment
--profile NAMEThe saved profile (default: the current one, else default)BETTER_COMPUTE_PROFILE
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute auth logout
better-compute auth logout --profile ci

ssh-cert

Get a break-glass SSH certificate for one node, next to your key

better-compute ssh-cert [COMMAND...] --tenant ID --node ID [--key PATH] [--account NAME] [--minutes N] [--connect HOST] [--ssh PROGRAM] [--config PATH] [--url URL]

ssh-cert asks nodes.issueSshCert for a short-lived OpenSSH user certificate for your own public key (--key, default ~/.ssh/id_ed25519.pub, else id_ecdsa.pub) and writes it beside the key as <key>-cert.pub, where ssh -i <key> finds it. The certificate is bound to the node (principal <account>@<nodeId>): it opens that node only, as --account (default root), for --minutes (default 15, at most 60, and never past your session). It needs compute:nodes:ssh-root and a session verified with MFA. The private key never leaves your machine. Revoke it early with api nodes.revokeSshCert.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--node IDThe node
--key PATHYour OpenSSH public key (.pub)
--account NAMEThe Unix account on the noderoot
--minutes NValidity in minutes15
--connect HOSTThen run ssh to the node's address with the new certificate (the command after --, or a shell)
--ssh PROGRAMThe OpenSSH client for --connectssh
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute ssh-cert --tenant org_acme --node node_01k5w… && ssh -i ~/.ssh/id_ed25519 root@10.0.0.5
better-compute ssh-cert --tenant org_acme --node node_01k5w… --key ./ops.pub --minutes 5
better-compute ssh-cert --node node_01k5w… --connect 10.0.0.5 -- journalctl -u bcd -n 50

console

Open an interactive console on a node, or an instance’s serial console

better-compute console --tenant ID [--node ID] [--instance ID] [--config PATH] [--url URL]

console opens a root console on one node (--node, compute:nodes:console on the node), or the serial console of one running instance (--instance, compute:instances:exec on the instance), through tunnels.open. The session runs over the server’s tunnel WebSocket, so it needs --url (or BETTER_COMPUTE_URL). The terminal is in raw mode until the remote program exits: every key, Ctrl-C included, goes to the remote, and Ctrl-] detaches. The command exits with the remote program’s status (255 when the tunnel breaks), like ssh. Tickets are single-use and the gateway and the node verify them again; revoking your session or the delegation closes the tunnel.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--node IDThe node (a root shell on it)
--instance IDThe instance (its serial console)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
better-compute console --url https://compute.example.com --tenant org_acme --node node_01k5w…
better-compute console --url https://compute.example.com --tenant org_acme --instance inst_01k5w…

exec

Open an interactive shell in a running instance

better-compute exec --tenant ID --instance ID [--config PATH] [--url URL]

exec opens a shell in one running instance through tunnels.open (compute:instances:exec on the instance); its node's agent runs it. The session runs over the server’s tunnel WebSocket, so it needs --url (or BETTER_COMPUTE_URL). The terminal is in raw mode until the remote program exits: every key, Ctrl-C included, goes to the remote, and Ctrl-] detaches. The command exits with the remote program’s status (255 when the tunnel breaks), like ssh. Tickets are single-use and the gateway and the node verify them again; revoking your session or the delegation closes the tunnel.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--instance IDThe instance
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
better-compute exec --url https://compute.example.com --tenant org_acme --instance inst_01k5w…

forward

Forward a local port to a port of a running instance

better-compute forward --tenant ID --instance ID --port PORT [--local-port PORT] [--bind HOST] [--config PATH] [--url URL]

forward listens on --bind (default 127.0.0.1) at --local-port (default: the same as --port; 0 picks a free one) and forwards each connection to --port on the instance through its own tcp tunnel (tunnels.open, compute:instances:exec on the instance), like ssh -L. It runs until Ctrl-C. The tunnels run over the server’s WebSocket, so it needs --url (or BETTER_COMPUTE_URL). A connection is re-authorized like any tunnel: revoking your session or the delegation closes it. Binding anything but loopback lets whoever reaches that address use your access; even on loopback, other users of this machine and web pages open in your browser can reach the port, as with ssh -L. At most 16 connections are forwarded at once, and new ones are refused while the server rate-limits your tunnels.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--instance IDThe instance
--port PORTThe port on the instance
--local-port PORTThe local port (default: --port; 0 picks a free one)
--bind HOSTThe local address to listen on127.0.0.1
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
better-compute forward --url https://compute.example.com --tenant org_acme --instance inst_01k5w… --port 5432
better-compute forward --instance inst_01k5w… --port 8888 --local-port 0

Capacity

reserve

Reserve GPUs from a pool (reservations.create)

better-compute reserve --tenant ID --pool ID --gpus N --for DURATION [--selector EXPR] [--model MODEL] [--product PRODUCT] [--nvlink] [--project ID] [--start TIME] [--lease SECONDS] [--key KEY] [--wait SECONDS] [--cpus N] [--memory GIB] [--kubernetes] [--config PATH] [--url URL]

reserve asks for --gpus whole GPUs from --pool for --for, decided by better-iam as compute:reservations:create on the reservation draft and placed by the pool's placer (PLAN.md §3.4, §10.3). --selector takes the shorthand gpuProduct in (H100-SXM5-80GB), vramGiB >= 80; --model and --product add a gpuModel or gpuProduct match. It waits up to --wait seconds for the placement and prints the reservation, whose binding lists the booked GPU UUIDs once active. Every run sends a new idempotency key unless --key is given: repeat the command with the same --key to retry safely.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool to reserve from (pool_…)
--gpus NWhole GPUs to reserve
--for DURATIONHow long: 30m, 8h, 7d
--selector EXPRDevice selector shorthand, e.g. "vramGiB >= 80"
--model MODELGPU model family, e.g. H100
--product PRODUCTGPU product, e.g. H100-SXM5-80GB
--nvlinkAll GPUs must share one NVLink domain
--project IDProject to account the reservation to (prj_…)
--start TIMEStart later: ISO 8601 time or +DURATION (default now)
--lease SECONDSRenewable lease: released unless renewed within this many seconds
--key KEYIdempotency key, to retry an earlier request
--wait SECONDSHow long to wait for the placement20
--cpus NHost CPUs to book with the GPUs (default: the pool's per-GPU share)
--memory GIBHost memory in GiB to book with the GPUs (default: the pool's per-GPU share)
--kubernetesGive the GPUs to the node's kubelet (pools that join a Kubernetes cluster)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute reserve --pool pool_01k… --gpus 4 --for 8h
better-compute reserve --pool pool_01k… --gpus 2 --for 8h --cpus 32 --memory 256 --kubernetes
better-compute reserve --pool pool_01k… --gpus 8 --for 3d --model H100 --nvlink --selector "vramGiB >= 80"
better-compute reserve --pool pool_01k… --gpus 2 --for 1h --start +2h --key nightly-2026-09-24

release

Release a reservation early (reservations.release)

better-compute release RESERVATION --tenant ID [--cancel] [--config PATH] [--url URL]

release ends an active or placed reservation now: its bookings are truncated, the GPUs return to the pool and billing stops at the release time. A reservation that has not started yet can be withdrawn with --cancel instead (reservations.cancel).

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--cancelWithdraw a reservation that has not started
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute release rsv_01k…
better-compute release rsv_01k… --cancel

extend

Extend a reservation (reservations.extend)

better-compute extend RESERVATION --tenant ID [--by DURATION] [--until TIME] [--renew] [--key KEY] [--config PATH] [--url URL]

extend moves the end of a reservation later, by --by or to --until. It is admitted like a new reservation: better-iam decides compute:reservations:extend on the total duration after the change, so policy caps also bound extensions, and the booking must still be free. With --renew it instead renews a leased reservation (reservations.renew).

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--by DURATIONExtend by this much: 30m, 4h, 1d
--until TIMEExtend to this time: ISO 8601 or +DURATION
--renewRenew the lease of a renewable reservation instead
--key KEYIdempotency key, to retry an earlier request
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute extend rsv_01k… --by 4h
better-compute extend rsv_01k… --renew

reservations list

List the tenant’s reservations (reservations.list)

better-compute reservations list --tenant ID [--state STATE] [--pool ID] [--limit N] [--cursor ID] [--config PATH] [--url URL]

List the tenant’s reservations (reservations.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--state STATEOnly this state: active, placed, pending, …
--pool IDOnly this pool
--limit NPage size
--cursor IDContinue after this reservation
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute reservations list --state active
better-compute reservations list --format json

reservations get

Show one reservation, with its binding (reservations.get)

better-compute reservations get RESERVATION --tenant ID [--config PATH] [--url URL]

Show one reservation, with its binding (reservations.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute reservations get rsv_01k… --query binding

pools list

List the pools the tenant owns or is offered (pools.list)

better-compute pools list --tenant ID [--config PATH] [--url URL]

List the pools the tenant owns or is offered (pools.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pools list

capacity

Show free and allocated capacity: a summary, or one pool (capacity.summary, capacity.byPool)

better-compute capacity --tenant ID [--pool ID] [--preview] [--gpus N] [--for DURATION] [--selector EXPR] [--cpus N] [--memory GIB] [--kubernetes] [--config PATH] [--url URL]

capacity prints what the tenant can see: per pool and per GPU model, total, allocatable, allocated, reserved and available. With --pool it prints that pool's rollup. With --preview and a --gpus request it asks where such a reservation would fit right now (capacity.preview) without booking anything.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDOne pool
--previewPreview a placement (needs --pool, --gpus and --for)
--gpus NGPUs for --preview
--for DURATIONDuration for --preview
--selector EXPRSelector shorthand for --preview
--cpus NHost CPUs to book with the GPUs (default: the pool's per-GPU share)
--memory GIBHost memory in GiB to book with the GPUs (default: the pool's per-GPU share)
--kubernetesGive the GPUs to the node's kubelet (pools that join a Kubernetes cluster)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute capacity
better-compute capacity --pool pool_01k…
better-compute capacity --preview --pool pool_01k… --gpus 8 --for 1d --selector "vramGiB >= 80"

Sandboxes

sandbox create

Create a sandbox from a template (sandboxes.create)

better-compute sandbox create --tenant ID --template ID [--ttl DURATION] [--from-snapshot ID] [--wait SECONDS] [--key KEY] [--config PATH] [--url URL]

sandbox create starts a sandbox from --template (sbxt_…), claimed from the template's warm pool when it keeps one, decided by better-iam as compute:sandboxes:create. It lives --ttl (default: the template's default, at most its maximum) and is deleted then unless extended. --from-snapshot restores a snapshot of yours instead of booting the image. It waits up to --wait seconds (default 30) for the sandbox to leave starting and prints it. Every run sends a new idempotency key unless --key is given: repeat with the same --key to retry safely.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--template IDThe template (sbxt_…)
--ttl DURATIONHow long it lives: 15m, 2h
--from-snapshot IDRestore this snapshot (snap_…)
--wait SECONDSWait this long for it to start30
--key KEYIdempotency key, to retry the same request
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox create --template sbxt_01k5w… --ttl 30m
better-compute sandbox create --template sbxt_01k5w… --from-snapshot snap_01k5w…

sandbox list

List sandboxes: yours, or the tenant’s with --all (sandboxes.list)

better-compute sandbox list --tenant ID [--all] [--deleted] [--template ID] [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List sandboxes: yours, or the tenant’s with --all (sandboxes.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--allEvery sandbox of the tenant, not only yours
--deletedInclude deleted sandboxes
--template IDOnly this template’s (sbxt_…)
--limit NPage size (at most 200)
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox list
better-compute sandbox list --all --query "items[*].id"

sandbox get

Show a sandbox, its expiry and its snapshots (sandboxes.get)

better-compute sandbox get SANDBOX --tenant ID [--config PATH] [--url URL]

Show a sandbox, its expiry and its snapshots (sandboxes.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox get sbx_01k5w…

sandbox extend

Move a sandbox’s expiry (sandboxes.extend)

better-compute sandbox extend SANDBOX --tenant ID [--by DURATION] [--until TIME] [--config PATH] [--url URL]

sandbox extend adds --by to the current expiry, or sets it to --until (ISO 8601, epoch ms or +DURATION from now), within the template's maximum lifetime. Decided as compute:sandboxes:update on the sandbox.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--by DURATIONAdd this to the expiry: 30m, 2h
--until TIMEThe new expiry
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox extend sbx_01k5w… --by 30m
better-compute sandbox extend sbx_01k5w… --until +2h

sandbox snapshot

Snapshot a sandbox, memory included unless --no-memory (sandboxes.snapshot)

better-compute sandbox snapshot SANDBOX --tenant ID [--no-memory] [--keep] [--key KEY] [--config PATH] [--url URL]

sandbox snapshot asks the sandbox's host for its instance's state; the snapshot is pending until the host reports it. Snapshots go with the sandbox unless --keep. Restore one with sandbox create --from-snapshot.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--no-memoryDisk only (not a running state)
--keepKeep it after the sandbox is deleted
--key KEYIdempotency key, to retry the same request
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox snapshot sbx_01k5w… --keep

sandbox delete

Delete a sandbox now (sandboxes.delete)

better-compute sandbox delete SANDBOX --tenant ID [--config PATH] [--url URL]

Delete a sandbox now (sandboxes.delete)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox delete sbx_01k5w…

sandbox exec

Run a command (or a shell) in a sandbox over SSH through a tunnel (sandboxes.exec)

better-compute sandbox exec SANDBOX [COMMAND...] --tenant ID [--key PATH] [--minutes N] [--ssh PROGRAM] [--accept-host-key] [--config PATH] [--url URL]

sandbox exec asks sandboxes.exec for a short-lived SSH certificate for your own key (--key, default ~/.ssh/id_ed25519; its .pub is sent, the private key never leaves this machine) and a single-use tunnel ticket to the sandbox's instance, then runs your OpenSSH client (--ssh, default ssh) through that tunnel: the command after --, or a login shell. There is no agent in the guest; its sshd trusts the sandbox CA for <account>@<instance>. The certificate and a known_hosts file for this one connection live in a temporary directory that is removed afterwards. It exits with ssh's status. Needs --url (tunnels run over the server's WebSocket) and a signed-in session.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--key PATHYour private key (its .pub beside it is sent)
--minutes NCertificate lifetime in minutes10
--ssh PROGRAMThe OpenSSH client to runssh
--accept-host-keyConnect although the guest's host key is not certified (older guests, or an expired host certificate): accepted on first use
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
better-compute sandbox exec sbx_01k5w… -- uname -a
better-compute sandbox exec sbx_01k5w…

sandbox-template create

Create a sandbox template for the tenant (sandboxTemplates.create)

better-compute sandbox-template create --tenant ID --name NAME --pool ID [--runtime ID] [--flavor NAME] [--image ID] [--user-data FILE] [--default-ttl DURATION] [--max-ttl DURATION] [--warm-min N] [--warm-max N] [--env NAME=REF,…] [--network MODE] [--volumes NAME:/PATH[:ro],…] [--config PATH] [--url URL]

sandbox-template create defines what sandbox create --template starts: --pool, --flavor and --image (and the pool's --runtime), the default and longest lifetimes, a warm pool kept ready (--warm-min/--warm-max), user data from a file, environment variables from secret references and the network mode. Decided as compute:sandboxes:manage on the tenant.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--name NAMEThe template name
--pool IDThe pool its instances run in (pool_…)
--runtime IDThe pool's runtime driver (checked against it)
--flavor NAMEThe instance flavor (the pool offers it)
--image IDThe image
--user-data FILEcloud-init user data for every sandbox, from a file
--default-ttl DURATIONDefault lifetime: 30m, 2h
--max-ttl DURATIONLongest lifetime a sandbox may be given or extended to
--warm-min NWarm instances kept ready
--warm-max NAt most this many warm instances
--env NAME=REF,…Environment from secret references: NAME=secret://<tenant>/<id>, comma-separated
--network MODEWhat sandboxes may reach (default egress)
--volumes NAME:/PATH[:ro],…Persistent volumes (agent memory) every sandbox mounts, in order; "" removes them
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox-template create --name python --pool pool_01k5w… --flavor small --image img_01k5w… --warm-min 2 --max-ttl 4h

sandbox-template update

Change a sandbox template (sandboxTemplates.update)

better-compute sandbox-template update TEMPLATE --tenant ID [--flavor NAME] [--image ID] [--user-data FILE] [--default-ttl DURATION] [--max-ttl DURATION] [--warm-min N] [--warm-max N] [--env NAME=REF,…] [--network MODE] [--volumes NAME:/PATH[:ro],…] [--no-user-data] [--config PATH] [--url URL]

sandbox-template update changes only the fields given; --no-user-data removes the user data. Sandboxes already running keep what they started with; the warm pool is refilled from the new definition.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--flavor NAMEThe instance flavor (the pool offers it)
--image IDThe image
--user-data FILEcloud-init user data for every sandbox, from a file
--default-ttl DURATIONDefault lifetime: 30m, 2h
--max-ttl DURATIONLongest lifetime a sandbox may be given or extended to
--warm-min NWarm instances kept ready
--warm-max NAt most this many warm instances
--env NAME=REF,…Environment from secret references: NAME=secret://<tenant>/<id>, comma-separated
--network MODEWhat sandboxes may reach (default egress)
--volumes NAME:/PATH[:ro],…Persistent volumes (agent memory) every sandbox mounts, in order; "" removes them
--no-user-dataRemove the user data
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox-template update sbxt_01k5w… --warm-min 4 --image img_01k5w…

sandbox-template delete

Delete a sandbox template (sandboxTemplates.delete)

better-compute sandbox-template delete TEMPLATE --tenant ID [--config PATH] [--url URL]

Delete a sandbox template (sandboxTemplates.delete)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox-template delete sbxt_01k5w…

sandbox templates

List the tenant’s sandbox templates and their warm pools (sandboxTemplates.list)

better-compute sandbox templates --tenant ID [--config PATH] [--url URL]

List the tenant’s sandbox templates and their warm pools (sandboxTemplates.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox templates --format table

sandbox-volume create

Create a persistent volume (agent memory) on a pool (sandboxVolumes.create)

better-compute sandbox-volume create --tenant ID --pool ID --name NAME --size GIB [--key KEY] [--config PATH] [--url URL]

sandbox-volume create books --size GiB of disk on one host of --pool; sandboxes of templates that mount it (sandbox-template --volumes) run on that host and keep its data across runs. It is billed until deleted. Repeat with the same --key to retry safely.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool (pool_…)
--name NAMEA name unique in the tenant
--size GIBSize in GiB
--key KEYIdempotency key, to retry the same request
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox-volume create --pool pool_01k5w… --name agent-notes --size 20

sandbox-volume list

List the tenant’s persistent volumes (sandboxVolumes.list)

better-compute sandbox-volume list --tenant ID [--pool ID] [--deleted] [--all] [--config PATH] [--url URL]

List the tenant’s persistent volumes (sandboxVolumes.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDOnly this pool’s (pool_…)
--deletedInclude deleted volumes
--allEvery volume of the tenant you may see, not only yours
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox-volume list --format table

sandbox-volume get

Show a persistent volume (sandboxVolumes.get)

better-compute sandbox-volume get VOLUME --tenant ID [--config PATH] [--url URL]

Show a persistent volume (sandboxVolumes.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox-volume get vol_01k5w…

sandbox-volume resize

Grow a persistent volume (sandboxVolumes.resize)

better-compute sandbox-volume resize VOLUME --tenant ID --size GIB [--config PATH] [--url URL]

sandbox-volume resize grows the volume to --size GiB; volumes never shrink.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--size GIBThe new size in GiB
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox-volume resize vol_01k5w… --size 40

sandbox-volume delete

Delete a persistent volume and wipe its data (sandboxVolumes.delete)

better-compute sandbox-volume delete VOLUME --tenant ID [--config PATH] [--url URL]

Delete a persistent volume and wipe its data (sandboxVolumes.delete)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox-volume delete vol_01k5w…

sandbox-snapshot list

List sandbox snapshots, kept ones of deleted sandboxes too (sandboxSnapshots.list)

better-compute sandbox-snapshot list --tenant ID [--all] [--sandbox ID] [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List sandbox snapshots, kept ones of deleted sandboxes too (sandboxSnapshots.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--allEvery snapshot of the tenant, not only yours
--sandbox IDOnly this sandbox’s (sbx_…)
--limit NPage size (at most 200)
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox-snapshot list --sandbox sbx_01k5w…

sandbox-snapshot get

Show a sandbox snapshot and its state (sandboxSnapshots.get)

better-compute sandbox-snapshot get SNAPSHOT --tenant ID [--config PATH] [--url URL]

Show a sandbox snapshot and its state (sandboxSnapshots.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox-snapshot get snap_01k5w…

sandbox-snapshot delete

Delete a sandbox snapshot (sandboxSnapshots.delete)

better-compute sandbox-snapshot delete SNAPSHOT --tenant ID [--config PATH] [--url URL]

Delete a sandbox snapshot (sandboxSnapshots.delete)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sandbox-snapshot delete snap_01k5w…

Nodes

nodes list

List the tenant’s nodes (nodes.list)

better-compute nodes list --tenant ID [--pool ID] [--site ID] [--cordoned] [--state STATE] [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List the tenant’s nodes (nodes.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDOnly this pool’s
--site IDOnly this site’s (site_…)
--cordonedOnly cordoned nodes
--state STATEOnly nodes in this provisioning state
--limit NPage size (at most 50)
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute nodes list --pool h100-eu
better-compute nodes list --cordoned --format json

nodes get

Show one node: its state, labels, conditions and session (nodes.get)

better-compute nodes get NODE --tenant ID [--config PATH] [--url URL]

Show one node: its state, labels, conditions and session (nodes.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute nodes get node_01k5w…
better-compute nodes get node_01k5w… --query conditions

nodes events

List a node’s events, newest first (nodes.events)

better-compute nodes events NODE --tenant ID [--type TYPE] [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List a node’s events, newest first (nodes.events)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--type TYPEOnly events of this type (node.cordoned, gpu.xid, …)
--limit NPage size (at most 200)
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute nodes events node_01k5w… --type gpu.xid

nodes cordon

Stop placing new work on a node (nodes.cordon)

better-compute nodes cordon NODE --tenant ID [--reason TEXT] [--if-version N] [--config PATH] [--url URL]

nodes cordon keeps the scheduler from placing anything new on the node; what runs there keeps running. Decided as compute:nodes:cordon. --if-version makes it conditional on the version nodes get printed.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--reason TEXTWhy (recorded in the node’s events)
--if-version NOnly if the node is still at this version (nodes.get’s version)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute nodes cordon node_01k5w… --reason "disk replacement"

nodes uncordon

Let the scheduler place work on a node again (nodes.uncordon)

better-compute nodes uncordon NODE --tenant ID [--reason TEXT] [--if-version N] [--config PATH] [--url URL]

nodes uncordon undoes nodes cordon. Decided as compute:nodes:cordon.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--reason TEXTWhy (recorded in the node’s events)
--if-version NOnly if the node is still at this version (nodes.get’s version)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute nodes uncordon node_01k5w… --reason "disk replacement"

nodes drain

Cordon a node and move its work off (nodes.drain)

better-compute nodes drain NODE --tenant ID [--reason TEXT] [--if-version N] [--config PATH] [--url URL]

nodes drain cordons the node and hands its allocations to the Drain controller, which releases them within the pool’s disruption budget. Decided as compute:nodes:drain; follow it with nodes events (node.draining, then the releases).

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--reason TEXTWhy (recorded in the node’s events)
--if-version NOnly if the node is still at this version (nodes.get’s version)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute nodes drain node_01k5w… --reason "disk replacement"

nodes label

Set or remove a node’s labels (nodes.label)

better-compute nodes label NODE --tenant ID [--set KEY=VALUE,…] [--remove KEY,…] [--if-version N] [--config PATH] [--url URL]

nodes label sets labels from --set KEY=VALUE,… and removes the keys in --remove KEY,…; selectors (reservations, pools) match on them. Decided as compute:nodes:update.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--set KEY=VALUE,…Labels to set
--remove KEY,…Label keys to remove
--if-version NOnly if the node is still at this version (nodes.get’s version)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute nodes label node_01k5w… --set rack=r12,fabric=ib-a --remove old

nodes taint

Add or remove a node’s taints (nodes.taint)

better-compute nodes taint NODE --tenant ID [--add TAINT,…] [--remove TAINT,…] [--if-version N] [--config PATH] [--url URL]

nodes taint adds the taints in --add and removes those in --remove (comma-separated). A taint joins the node’s NoSchedule reasons as taint:<taint>, so the scheduler places nothing new there until it is removed. Decided as compute:nodes:update.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--add TAINT,…Taints to add
--remove TAINT,…Taints to remove
--if-version NOnly if the node is still at this version (nodes.get’s version)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute nodes taint node_01k5w… --add gpu=degraded:NoSchedule

nodes delete

Remove a retired or cordoned adopted node for good (nodes.delete)

better-compute nodes delete NODE --tenant ID [--reason TEXT] [--if-version N] [--yes] [--config PATH] [--url URL]

nodes delete tombstones a node that is retired, a cordoned adopted node (cordon it first), or one that never got past adoption or enrollment, once it holds no allocation. Its certificates are revoked and its hardware key and attestor bindings (e.g. a TPM) are freed, so the machine can join again as a new node: after a TPM swap, cordon, delete, then run bcd join --reset --yes and bcd join on the machine. It cannot be undone, so it needs --yes. Decided as compute:nodes:delete.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--reason TEXTWhy (recorded in the node’s events)
--if-version NOnly if the node is still at this version (nodes.get’s version)
--yesConfirm: the node is removed for good
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute nodes delete node_01k5w… --reason "TPM replaced" --yes

join-tokens create

Create a join token for a pool: what bcd join presents (joinTokens.create)

better-compute join-tokens create --tenant ID --pool ID --ttl DURATION [--max-uses N] [--allow-reclaim] [--hosted WHERE] [--expect-product-uuid UUID] [--expect-serial SERIAL] [--expect-mac MAC] [--expect-gpu-uuid GPU-…] [--out PATH] [--key KEY] [--config PATH] [--url URL]

join-tokens create mints a token that lets up to --max-uses machines (default 1) join --pool for --ttl (60s to 24h). --expect-* bind it to one machine's identity (product UUID, serial, MAC or GPU UUID). --hosted kubernetes marks it for bcd as a DaemonSet (deploy/kubernetes): the cluster's admins have root on its machines, so they join tenant-controlled unless the pool's policy.kubernetes.platformOperated is true. The token is printed once and never again: with --out it goes to a new file (0600, never overwritten) instead of the output, to hand to the server out of band (the install script reads BCD_JOIN_TOKEN or stdin). Decided as compute:join-tokens:create.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool machines join
--ttl DURATIONHow long it is valid: 30m, 2h (at most 24h)
--max-uses NMachines it admits (default 1)
--allow-reclaimMay re-admit a machine the pool already knew
--hosted WHEREFor bcd as a DaemonSet on a Kubernetes cluster: its nodes join tenant-controlled unless the pool says the platform operates the cluster
--expect-product-uuid UUIDOnly the machine with this SMBIOS product UUID
--expect-serial SERIALOnly the machine with this serial number
--expect-mac MACOnly the machine with this MAC address
--expect-gpu-uuid GPU-…Only the machine with this GPU
--out PATHWrite the token to this new file instead of printing it
--key KEYIdempotency key, to retry the same request
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute join-tokens create --pool h100-eu --ttl 2h --max-uses 8 --out ./join-token
better-compute join-tokens create --pool h100-eu --ttl 30m --expect-serial SN12345
better-compute join-tokens create --pool k8s-gpu --ttl 2h --max-uses 16 --hosted kubernetes --out ./join-token

join-tokens list

List a pool’s join tokens, never their secrets (joinTokens.list)

better-compute join-tokens list --tenant ID --pool ID [--all] [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List a pool’s join tokens, never their secrets (joinTokens.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool
--allAlso revoked, expired and used-up tokens
--limit NPage size (at most 200)
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute join-tokens list --pool h100-eu

join-tokens revoke

Revoke a join token before it expires (joinTokens.revoke)

better-compute join-tokens revoke TOKEN --tenant ID --pool ID [--config PATH] [--url URL]

Revoke a join token before it expires (joinTokens.revoke)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute join-tokens revoke jt_01k5w… --pool h100-eu

join-requests list

List a pool’s join requests, pending by default (joinRequests.list)

better-compute join-requests list --tenant ID --pool ID [--state STATE] [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List a pool’s join requests, pending by default (joinRequests.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool
--state STATEWhich requests (default pending)
--limit NPage size (at most 200)
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute join-requests list --pool h100-eu

join-requests approve

Let a machine that asked join the pool (joinRequests.approve)

better-compute join-requests approve REQUEST --tenant ID --pool ID [--reason TEXT] [--ttl SECONDS] [--config PATH] [--url URL]

join-requests approve admits the machine behind the request into --pool; the approval waits --ttl seconds (default 3600) for the machine to retry. Check its claims (join-requests list) first: approving is what makes an unknown machine a node. Decided as compute:join-tokens:create.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool
--reason TEXTRecorded on the request
--ttl SECONDSHow long it may join
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute join-requests approve jreq_01k5w… --pool h100-eu

join-requests reject

Turn a machine’s join request down (joinRequests.reject)

better-compute join-requests reject REQUEST --tenant ID --pool ID [--reason TEXT] [--config PATH] [--url URL]

Turn a machine’s join request down (joinRequests.reject)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool
--reason TEXTRecorded on the request
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute join-requests reject jreq_01k5w… --pool h100-eu

inventory get

Show a node’s current hardware inventory (inventory.get)

better-compute inventory get NODE --tenant ID [--config PATH] [--url URL]

Show a node’s current hardware inventory (inventory.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute inventory get node_01k5w… --query gpus

inventory history

List a node’s inventory snapshots, newest first (inventory.history)

better-compute inventory history NODE --tenant ID [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List a node’s inventory snapshots, newest first (inventory.history)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--limit NPage size (at most 100)
--cursor CURSORThe cursor of the previous page (inv_…)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute inventory history node_01k5w…

inventory diff

Show what changed in a node’s hardware between two snapshots (inventory.diff)

better-compute inventory diff NODE --tenant ID [--from SNAPSHOT] [--to SNAPSHOT] [--config PATH] [--url URL]

inventory diff compares --from and --to (snapshot ids from inventory history, or inventory digests). Without --to it is the latest snapshot, without --from the one before --to.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--from SNAPSHOTThe older snapshot (inv_…) or digest
--to SNAPSHOTThe newer snapshot (inv_…) or digest
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute inventory diff node_01k5w…

site-tokens create

Create a token a site agent joins a site with (siteTokens.create)

better-compute site-tokens create --tenant ID --site ID --ttl SECONDS [--max-uses N] [--out PATH] [--key KEY] [--config PATH] [--url URL]

site-tokens create mints a token for up to --max-uses site agents (default 1) to join --site within --ttl seconds. It is printed once: with --out it goes to a new 0600 file instead. Decided as compute:sites:manage.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--site IDThe site (site_…)
--ttl SECONDSHow long it is valid
--max-uses NSite agents it admits (default 1)
--out PATHWrite the token to this new file instead of printing it
--key KEYIdempotency key, to retry the same request
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute site-tokens create --site site_01k5w… --ttl 3600 --out ./site-token

site-tokens revoke

Revoke a site token (siteTokens.revoke)

better-compute site-tokens revoke TOKEN --tenant ID --site ID [--config PATH] [--url URL]

Revoke a site token (siteTokens.revoke)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--site IDThe site
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute site-tokens revoke jt_01k5w… --site site_01k5w…

Instances

instances list

List instances: yours, or the tenant’s with --all (instances.list)

better-compute instances list --tenant ID [--all] [--state STATE] [--pool ID] [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List instances: yours, or the tenant’s with --all (instances.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--allEvery instance of the tenant, not only yours
--state STATEOnly instances in this state
--pool IDOnly this pool’s
--limit NPage size (at most 200)
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute instances list
better-compute instances list --all --state running

instances get

Show one instance (instances.get)

better-compute instances get INSTANCE --tenant ID [--config PATH] [--url URL]

Show one instance (instances.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute instances get inst_01k5w…

instances create

Create an instance in a pool (instances.create)

better-compute instances create --tenant ID --pool ID --flavor NAME --image ID [--project ID] [--ssh-key PATH,…] [--user-data FILE|secret://…] [--stopped] [--wait SECONDS] [--key KEY] [--config PATH] [--url URL]

instances create boots --image with --flavor in --pool, decided as compute:instances:create. --ssh-key reads OpenSSH public keys from files (comma-separated paths; every non-empty line is a key), --user-data cloud-init from a file, or a secret://<tenant>/<id> reference. --stopped creates it without starting it. It waits up to --wait seconds (default 30) for the instance to settle. Every run sends a new idempotency key unless --key is given: repeat with the same --key to retry safely.

| Flag | Description | Default | Environment | | ------------------ | ------------------------------------------------------------------------------------------------------------- | ------------------------------------- | ----------------------- | --- | | --tenant ID | The tenant acting (the caller’s organization) | | BETTER_COMPUTE_TENANT | | --pool ID | The pool | | | | --flavor NAME | The flavor (the pool offers it) | | | | --image ID | The image to boot | | | | --project ID | The project it belongs to | | | | --ssh-key PATH,… | Files of OpenSSH public keys to authorize | | | | --user-data FILE | secret://… | cloud-init user data (at most 32 KiB) | | | | --stopped | Create it stopped | | | | --wait SECONDS | Wait this long for it to settle | 30 | | | --key KEY | Idempotency key, to retry the same request | | | | --config PATH | Configuration module; default: the nearest better-compute.config.{mjs,js,ts,mts} | | BETTER_COMPUTE_CONFIG | | --url URL | Call a running compute server (https://host or https://host/api/compute) instead of loading the configuration | | BETTER_COMPUTE_URL | | --format FORMAT | Output as json (indented, default), compact (one line), or table | | | | --query PATH | Print only part of the result, e.g. protocol.max or findings[].check (strings print raw) | | |

better-compute instances create --pool vps-eu --flavor g1.small --image img_01k5w… --ssh-key ~/.ssh/id_ed25519.pub

instances start

Start a stopped instance (instances.start)

better-compute instances start INSTANCE --tenant ID [--config PATH] [--url URL]

Start a stopped instance (instances.start)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute instances start inst_01k5w…

instances stop

Stop a running instance (instances.stop)

better-compute instances stop INSTANCE --tenant ID [--config PATH] [--url URL]

Stop a running instance (instances.stop)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute instances stop inst_01k5w…

instances delete

Delete an instance and its disks (instances.delete)

better-compute instances delete INSTANCE --tenant ID [--config PATH] [--url URL]

Delete an instance and its disks (instances.delete)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute instances delete inst_01k5w…

instances resize

Move an instance to another flavor (instances.resize)

better-compute instances resize INSTANCE --tenant ID --flavor NAME [--config PATH] [--url URL]

Move an instance to another flavor (instances.resize)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--flavor NAMEThe new flavor
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute instances resize inst_01k5w… --flavor g1.large

Events

events list

List the tenant’s events, newest first (events.list)

better-compute events list --tenant ID [--type TYPE] [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List the tenant’s events, newest first (events.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--type TYPEOnly events of this type (node.cordoned, reservation.placed, …)
--limit NPage size (at most 500)
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute events list --type node.draining
better-compute events list --query "items[*].type"

events watch

Follow the tenant’s events as they happen, one JSON line each (GET /watch)

better-compute events watch --tenant ID [--types PATTERN,…] [--since CURSOR] [--count N] [--path PATH] [--config PATH] [--url URL]

events watch follows the host's server-sent events route (--path, default /watch; the console app serves /api/compute/watch) as BETTER_COMPUTE_TOKEN, so it needs --url. Each event is printed as one line of JSON with its sequence, the cursor: pass the last one to --since to resume where a previous run stopped. It reconnects after a broken connection, resuming from the last event, until Ctrl-C, --count events, or a refusal: a revoked session or a withdrawn compute:events:read ends it.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--types PATTERN,…Event type patterns, comma-separated (node.*, reservation.placed); default all
--since CURSORResume after this event (a printed sequence)
--count NStop after N events
--path PATHThe watch route on the server/watch
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
better-compute events watch --url https://compute.example.com --types "node.*,reservation.*"
better-compute events watch --url https://compute.example.com --since 184467.12 --count 100

Operations

releases register

Register a signed bcd release manifest for rollouts (releases.register)

better-compute releases register MANIFEST --tenant ID [--config PATH] [--url URL]

releases register sends a published manifest.json ({ manifest, signatures }) as it is; the server checks the signatures against its release keys. Decided as compute:fleet:admin.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute releases register ./dist/manifest.json

releases list

List the registered bcd releases (releases.list)

better-compute releases list --tenant ID [--config PATH] [--url URL]

List the registered bcd releases (releases.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute releases list

rollouts create

Roll a registered bcd release out in waves (rollouts.create)

better-compute rollouts create --tenant ID --version SEMVER [--pools ID,…] [--waves PCT,…] [--max-failure-ratio RATIO] [--soak DURATION] [--upgrade-timeout DURATION] [--key KEY] [--config PATH] [--url URL]

rollouts create upgrades the nodes of --pools (default: every pool the tenant owns) to --version in cumulative --waves of percentages (default 1,10,50,100). Each wave soaks --soak (default 10m) before the next, a node has --upgrade-timeout (default 15m) to come back on the new version, and more than --max-failure-ratio failed nodes (default 0.05) pause the rollout. Decided as compute:fleet:admin.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--version SEMVERThe registered release
--pools ID,…The pools to upgrade (pool_…)
--waves PCT,…Cumulative percentages ending at 100
--max-failure-ratio RATIOThe failed share that pauses it (0–1)
--soak DURATIONHow long each wave soaks
--upgrade-timeout DURATIONHow long a node has to come back
--key KEYIdempotency key, to retry the same request
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute rollouts create --version 0.9.1 --waves 5,25,100 --soak 30m

rollouts list

List rollouts (rollouts.list)

better-compute rollouts list --tenant ID [--config PATH] [--url URL]

List rollouts (rollouts.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute rollouts list

rollouts get

Show a rollout: its waves and per-node progress (rollouts.get)

better-compute rollouts get ROLLOUT --tenant ID [--config PATH] [--url URL]

Show a rollout: its waves and per-node progress (rollouts.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute rollouts get rollout_01k5w…

rollouts pause

Pause a rollout before its next wave (rollouts.pause)

better-compute rollouts pause ROLLOUT --tenant ID [--reason TEXT] [--config PATH] [--url URL]

Pause a rollout before its next wave (rollouts.pause)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--reason TEXTRecorded on the rollout
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute rollouts pause rollout_01k5w… --reason "XIDs after wave 2"

rollouts resume

Resume a paused rollout (rollouts.resume)

better-compute rollouts resume ROLLOUT --tenant ID [--config PATH] [--url URL]

Resume a paused rollout (rollouts.resume)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute rollouts resume rollout_01k5w…

rollouts abort

Stop a rollout for good, optionally rolling nodes back (rollouts.abort)

better-compute rollouts abort ROLLOUT --tenant ID [--reason TEXT] [--rollback] [--config PATH] [--url URL]

rollouts abort ends the rollout: no further node is upgraded. --rollback also swaps back the nodes whose upgrade is not confirmed yet. A node that confirmed the new release keeps it (it never goes below its release floor), so a bad release is undone by rolling a newer one forward.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--reason TEXTRecorded on the rollout
--rollbackAlso swap back upgrades not confirmed yet
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute rollouts abort rollout_01k5w… --rollback --reason "boot loop on H100 nodes"

workflows list

List a node’s workflows, newest first (workflows.list)

better-compute workflows list --tenant ID --node ID [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List a node’s workflows, newest first (workflows.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--node IDThe node (node_…)
--limit NPage size (at most 100)
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute workflows list --node node_01k5w…

workflows get

Show a workflow and its steps (workflows.get)

better-compute workflows get WORKFLOW --tenant ID --node ID [--config PATH] [--url URL]

Show a workflow and its steps (workflows.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--node IDThe node (node_…)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute workflows get wf_01k5w… --node node_01k5w… --query steps

workflows abort

Abort a workflow: pending steps are cancelled, its cleanup still runs (workflows.abort)

better-compute workflows abort WORKFLOW --tenant ID --node ID [--reason TEXT] [--config PATH] [--url URL]

Abort a workflow: pending steps are cancelled, its cleanup still runs (workflows.abort)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--node IDThe node (node_…)
--reason TEXTRecorded on the workflow
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute workflows abort wf_01k5w… --node node_01k5w…

workflows pause

Pause a workflow: the step in flight finishes, no further one starts (workflows.pause)

better-compute workflows pause WORKFLOW --tenant ID --node ID [--config PATH] [--url URL]

Pause a workflow: the step in flight finishes, no further one starts (workflows.pause)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--node IDThe node (node_…)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute workflows pause wf_01k5w… --node node_01k5w…

workflows resume

Resume a paused workflow (workflows.resume)

better-compute workflows resume WORKFLOW --tenant ID --node ID [--config PATH] [--url URL]

Resume a paused workflow (workflows.resume)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--node IDThe node (node_…)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute workflows resume wf_01k5w… --node node_01k5w…

workflows retry

Retry a failed workflow, from a step (workflows.retry)

better-compute workflows retry WORKFLOW --tenant ID --node ID [--from-step STEP] [--config PATH] [--url URL]

Retry a failed workflow, from a step (workflows.retry)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--node IDThe node (node_…)
--from-step STEPStart here (default: the first main step that did not succeed)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute workflows retry wf_01k5w… --node node_01k5w…

commands list

List the commands queued for a node, newest first (commands.list)

better-compute commands list --tenant ID --node ID [--state STATE] [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List the commands queued for a node, newest first (commands.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--node IDThe node (node_…)
--state STATEOnly commands in this state
--limit NPage size (at most 200)
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute commands list --node node_01k5w… --state failed

commands get

Show one command and its result (commands.get)

better-compute commands get COMMAND --tenant ID --node ID [--config PATH] [--url URL]

Show one command and its result (commands.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--node IDThe node (node_…)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute commands get cmd_01k5w… --node node_01k5w…

commands cancel

Cancel a command: a pending one never runs, a running one is asked to stop (commands.cancel)

better-compute commands cancel COMMAND --tenant ID --node ID [--config PATH] [--url URL]

Cancel a command: a pending one never runs, a running one is asked to stop (commands.cancel)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--node IDThe node (node_…)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute commands cancel cmd_01k5w… --node node_01k5w…

Catalog

sites list

List sites (sites.list)

better-compute sites list --tenant ID [--kind KIND] [--region REGION] [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List sites (sites.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--kind KINDOnly this kind
--region REGIONOnly this region
--limit NPage size
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sites list --region eu-west

sites get

Show a site (sites.get)

better-compute sites get SITE --tenant ID [--config PATH] [--url URL]

Show a site (sites.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sites get site_01k5w…

sites create

Create a site (sites.create)

better-compute sites create --tenant ID --kind KIND --region REGION [--iam-region REGION] [--key KEY] [--config PATH] [--url URL]

Create a site (sites.create)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--kind KINDThe site kind
--region REGIONIts region, e.g. eu-west
--iam-region REGIONThe IAM region it answers to
--key KEYIdempotency key, to retry the same request
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sites create --kind dedicated --region eu-west

sites update

Change a site’s region or IAM region (sites.update)

better-compute sites update SITE --tenant ID [--region REGION] [--iam-region REGION] [--clear-iam-region] [--if-version N] [--config PATH] [--url URL]

Change a site’s region or IAM region (sites.update)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--region REGIONThe new region
--iam-region REGIONThe new IAM region
--clear-iam-regionRemove the IAM region
--if-version NOnly if the site is still at this version
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sites update site_01k5w… --iam-region eu

sites delete

Delete a site (sites.delete)

better-compute sites delete SITE --tenant ID [--if-version N] [--config PATH] [--url URL]

Delete a site (sites.delete)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--if-version NOnly if the site is still at this version
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute sites delete site_01k5w…

projects list

List the tenant’s projects (projects.list)

better-compute projects list --tenant ID [--state STATE] [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List the tenant’s projects (projects.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--state STATEOnly this state
--limit NPage size
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute projects list

projects get

Show a project (projects.get)

better-compute projects get PROJECT --tenant ID [--config PATH] [--url URL]

Show a project (projects.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute projects get prj_01k5w…

projects create

Create a project (projects.create)

better-compute projects create --tenant ID --name NAME [--key KEY] [--config PATH] [--url URL]

Create a project (projects.create)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--name NAMELowercase letters, digits and dashes
--key KEYIdempotency key, to retry the same request
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute projects create --name training

projects archive

Archive a project (projects.archive)

better-compute projects archive PROJECT --tenant ID [--config PATH] [--url URL]

Archive a project (projects.archive)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute projects archive prj_01k5w…

images list

List catalog images (images.list)

better-compute images list --tenant ID [--kind KIND] [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List catalog images (images.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--kind KINDOnly this kind
--limit NPage size
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute images list --kind os

images get

Show a catalog image (images.get)

better-compute images get IMAGE --tenant ID [--config PATH] [--url URL]

Show a catalog image (images.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute images get img_01k5w…

images register

Register an image in the catalog by digest (images.register)

better-compute images register --tenant ID --ref NAME@VERSION --source URL --disk-format FORMAT --sha256 HEX [--kind KIND] [--compression ALG] [--size BYTES] [--signature BASE64] [--signer ID] [--config PATH] [--url URL]

images register records name@version (--ref), where it is fetched from (--source, https:// or oci://), its format and --sha256, and optionally a signature over its identity checked with a registered signer. Nodes verify the digest of what they fetch. Decided as compute:images:publish.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--ref NAME@VERSIONe.g. ubuntu-24.04@2026.09
--source URLWhere nodes fetch it: https:// or oci://
--disk-format FORMATThe disk format
--sha256 HEXThe image digest, 64 hex digits
--kind KINDWhat it boots as (default os)
--compression ALGDefault none
--size BYTESIts size in bytes
--signature BASE64A signature over its identity
--signer IDThe registered signer that checks --signature
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute images register --ref ubuntu-24.04@2026.09 --source https://images.example.com/u2404.qcow2 --disk-format qcow2 --sha256 3f…

images promote

Promote a catalog image (images.promote)

better-compute images promote IMAGE --tenant ID [--config PATH] [--url URL]

Promote a catalog image (images.promote)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute images promote img_01k5w…

flavors list

List a pool’s instance flavors (flavors.list)

better-compute flavors list --tenant ID --pool ID [--config PATH] [--url URL]

List a pool’s instance flavors (flavors.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool (pool_…)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute flavors list --pool pool_01k5w…

flavors create

Define an instance flavor in a pool (flavors.create)

better-compute flavors create --tenant ID --pool ID --name NAME --vcpu N --memory-gib GIB --disk-gib GIB [--gpus N] [--offer ID] [--config PATH] [--url URL]

Define an instance flavor in a pool (flavors.create)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool (pool_…)
--name NAMEThe flavor name, e.g. g1.small
--vcpu NVirtual CPUs
--memory-gib GIBMemory in GiB
--disk-gib GIBRoot disk in GiB
--gpus NGPUs passed through (default 0)
--offer IDProvider pools: the provider offer it creates
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute flavors create --pool pool_01k5w… --name g1.small --vcpu 4 --memory-gib 16 --disk-gib 80

flavors update

Redefine an instance flavor; running instances keep theirs (flavors.update)

better-compute flavors update --tenant ID --pool ID --name NAME --vcpu N --memory-gib GIB --disk-gib GIB [--gpus N] [--offer ID] [--config PATH] [--url URL]

Redefine an instance flavor; running instances keep theirs (flavors.update)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool (pool_…)
--name NAMEThe flavor name, e.g. g1.small
--vcpu NVirtual CPUs
--memory-gib GIBMemory in GiB
--disk-gib GIBRoot disk in GiB
--gpus NGPUs passed through (default 0)
--offer IDProvider pools: the provider offer it creates
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute flavors update --pool pool_01k5w… --name g1.small --vcpu 4 --memory-gib 32 --disk-gib 80

flavors delete

Remove an instance flavor from a pool (flavors.delete)

better-compute flavors delete --tenant ID --pool ID --name NAME [--config PATH] [--url URL]

Remove an instance flavor from a pool (flavors.delete)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool (pool_…)
--name NAMEThe flavor name, e.g. g1.small
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute flavors delete --pool pool_01k5w… --name g1.small

quotas get

Show the tenant’s quotas and what is used, per pool (quotas.get)

better-compute quotas get --tenant ID [--pool ID] [--config PATH] [--url URL]

Show the tenant’s quotas and what is used, per pool (quotas.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDOnly this pool (pool_…)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute quotas get --pool pool_01k5w…

quotas set

Set one quota of the tenant in a pool (quotas.set)

better-compute quotas set --tenant ID --pool ID --resource NAME --max N [--config PATH] [--url URL]

Set one quota of the tenant in a pool (quotas.set)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool (pool_…)
--resource NAMEThe quota resource, e.g. gpus
--max NThe new limit
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute quotas set --pool pool_01k5w… --resource gpus --max 64

Usage

usage list

List hourly usage windows (usage.list)

better-compute usage list --tenant ID [--project ID] [--meter METER] [--from TIME] [--to TIME] [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List hourly usage windows (usage.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--project IDOnly this project’s (prj_…)
--meter METEROnly this meter (gpu.hours, …)
--from TIMEWindows ending after: ISO 8601, epoch ms, or -7d
--to TIMEWindows starting before
--limit NPage size
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute usage list --from -7d --meter gpu.hours

usage export

Export usage for billing as CSV or JSON lines (usage.export)

better-compute usage export --tenant ID --from TIME --to TIME [--project ID] [--meter METER] [--export-format FORMAT] [--out PATH] [--config PATH] [--url URL]

usage export returns every hourly window between --from and --to (at most 93 days apart) and the total per meter. With --out the CSV (or JSON lines) goes to a new file, never over one, and the output keeps the count, the totals and whether the export was truncated (too many windows: narrow the range or the meter).

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--from TIMEISO 8601, epoch ms, or -30d
--to TIMEISO 8601, epoch ms, -1d or +0s (now)
--project IDOnly this project’s (prj_…)
--meter METEROnly this meter
--export-format FORMATDefault csv
--out PATHWrite the export to this new file
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute usage export --from 2026-09-01 --to 2026-10-01 --out september.csv

Webhooks

webhooks list

List the tenant’s webhooks (webhooks.list)

better-compute webhooks list --tenant ID [--config PATH] [--url URL]

List the tenant’s webhooks (webhooks.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute webhooks list

webhooks get

Show a webhook (webhooks.get)

better-compute webhooks get WEBHOOK --tenant ID [--config PATH] [--url URL]

Show a webhook (webhooks.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute webhooks get whk_01k5w…

webhooks create

Send the tenant’s events to a URL, signed (webhooks.create)

better-compute webhooks create --tenant ID --endpoint URL --events PATTERN,… [--description TEXT] [--secret-out PATH] [--config PATH] [--url URL]

webhooks create posts every event matching --events (type patterns: node._, reservation.placed, _) to --endpoint, signed with X-Better-Compute-Signature. The signing secret is shown once: with --secret-out it goes to a new 0600 file instead of the output.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--endpoint URLWhere events are posted (https)
--events PATTERN,…Event type patterns
--description TEXTWhat it is for
--secret-out PATHWrite the signing secret to this new file instead of printing it
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute webhooks create --endpoint https://hooks.example.com/compute --events "node.*,reservation.*" --secret-out ./whk.secret

webhooks update

Change a webhook’s URL, events, description, or pause it (webhooks.update)

better-compute webhooks update WEBHOOK --tenant ID [--endpoint URL] [--events PATTERN,…] [--description TEXT] [--pause] [--resume] [--config PATH] [--url URL]

Change a webhook’s URL, events, description, or pause it (webhooks.update)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--endpoint URLThe new URL events are posted to
--events PATTERN,…The new event type patterns
--description TEXTThe new description
--pauseStop delivering (active = false)
--resumeDeliver again (active = true)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute webhooks update whk_01k5w… --pause

webhooks delete

Delete a webhook (webhooks.delete)

better-compute webhooks delete WEBHOOK --tenant ID [--config PATH] [--url URL]

Delete a webhook (webhooks.delete)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute webhooks delete whk_01k5w…

webhooks ping

Send a webhook a test delivery (webhooks.ping)

better-compute webhooks ping WEBHOOK --tenant ID [--config PATH] [--url URL]

Send a webhook a test delivery (webhooks.ping)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute webhooks ping whk_01k5w…

webhooks rotate-secret

Replace a webhook’s signing secret (webhooks.rotateSecret)

better-compute webhooks rotate-secret WEBHOOK --tenant ID [--keep-previous DURATION] [--secret-out PATH] [--config PATH] [--url URL]

webhooks rotate-secret issues a new signing secret, shown once (or written to --secret-out). --keep-previous signs with the old one too for that long (at most 24h), so the receiver can switch without dropping deliveries.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--keep-previous DURATIONKeep signing with the old secret this long
--secret-out PATHWrite the signing secret to this new file instead of printing it
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute webhooks rotate-secret whk_01k5w… --keep-previous 1h --secret-out ./whk.secret.new

webhooks deliveries

List a webhook’s deliveries (webhooks.deliveries)

better-compute webhooks deliveries WEBHOOK --tenant ID [--state STATE] [--limit N] [--config PATH] [--url URL]

List a webhook’s deliveries (webhooks.deliveries)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--state STATEOnly deliveries in this state
--limit NAt most this many
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute webhooks deliveries whk_01k5w… --state dead

webhooks redeliver

Deliver one event again (webhooks.redeliver)

better-compute webhooks redeliver DELIVERY --tenant ID [--config PATH] [--url URL]

Deliver one event again (webhooks.redeliver)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute webhooks redeliver dlv_01k5w…

webhooks event-types

List the event types a webhook can subscribe to (webhooks.eventTypes)

better-compute webhooks event-types --tenant ID [--config PATH] [--url URL]

List the event types a webhook can subscribe to (webhooks.eventTypes)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute webhooks event-types

Pools

pools get

Show a pool: its class, policy and offers (pools.get)

better-compute pools get POOL --tenant ID [--config PATH] [--url URL]

Show a pool: its class, policy and offers (pools.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pools get pool_01k5w…

pools create

Create a pool in a site (pools.create)

better-compute pools create --tenant ID --site ID --class CLASS [--gpu-model MODEL] [--sharing MODE] [--policy-file PATH] [--owner-quota RES=N,…] [--provider-account ID] [--key KEY] [--config PATH] [--url URL]

pools create makes a --class pool in --site, owned by the tenant. --policy-file is a PoolPolicy as JSON; --owner-quota sets the owner’s own limits (default: unlimited GPUs). Provider pools name their --provider-account and run no agent. Decided as compute:pools:create.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--site IDThe site (site_…)
--class CLASSThe pool class
--gpu-model MODELThe GPU model it holds (H100, …)
--sharing MODEHow GPUs are shared (default exclusive)
--policy-file PATHThe PoolPolicy, as JSON
--owner-quota RES=N,…The owner’s limits, e.g. gpus=64
--provider-account IDProvider pools: the account (pacct_…)
--key KEYIdempotency key: a retry returns the same pool
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pools create --site site_01k5w… --class metal-host --gpu-model H100 --owner-quota gpus=64

pools update

Replace a pool’s policy or change its GPU model (pools.update)

better-compute pools update POOL --tenant ID [--policy-file PATH] [--gpu-model MODEL] [--config PATH] [--url URL]

Replace a pool’s policy or change its GPU model (pools.update)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--policy-file PATHThe new PoolPolicy, as JSON (replaces the old one)
--gpu-model MODELThe new GPU model
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pools update pool_01k5w… --policy-file policy.json

pools archive

Archive a pool (pools.archive)

better-compute pools archive POOL --tenant ID [--config PATH] [--url URL]

Archive a pool (pools.archive)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pools archive pool_01k5w…

pools offer

Offer a pool to another tenant with a quota (pools.offer)

better-compute pools offer POOL --tenant ID --to TENANT --quota RES=N,… [--include-descendants] [--priority N] [--config PATH] [--url URL]

Offer a pool to another tenant with a quota (pools.offer)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--to TENANTThe tenant it is offered to
--quota RES=N,…That tenant’s limits, e.g. gpus=8
--include-descendantsAlso that tenant’s child tenants
--priority NScheduling priority
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pools offer pool_01k5w… --to org_research --quota gpus=16

pools unoffer

Withdraw a pool’s offer to a tenant (pools.unoffer)

better-compute pools unoffer POOL --tenant ID --to TENANT [--config PATH] [--url URL]

Withdraw a pool’s offer to a tenant (pools.unoffer)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--to TENANTThe tenant to withdraw it from
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pools unoffer pool_01k5w… --to org_research

drivers list

List the drivers this deployment runs (drivers.list)

better-compute drivers list --tenant ID [--kind KIND] [--config PATH] [--url URL]

List the drivers this deployment runs (drivers.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--kind KINDOnly this kind (runtime, bmc, …)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute drivers list --kind runtime

Secrets

secrets put

Store a secret for secret:// references (secrets.put)

better-compute secrets put --tenant ID --name NAME [--value-file PATH] [--stdin] [--scope SCOPE] [--key KEY] [--config PATH] [--url URL]

secrets put stores the value of --value-file, or of standard input with --stdin, under --name; user data, sandbox environments and plugins name it as secret://<tenant>/<name>. The value never goes on the command line (shell history, the process list). --scope fleet stores a platform secret. Decided as compute:secrets:manage.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--name NAMEThe secret name
--value-file PATHRead the value from this file
--stdinRead the value from standard input
--scope SCOPEDefault tenant
--key KEYIdempotency key, to retry the same request
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute secrets put --name hf-token --value-file ./hf-token
vault read -field=token secret/hf | better-compute secrets put --name hf-token --stdin

secrets delete

Delete a secret (secrets.delete)

better-compute secrets delete --tenant ID --name NAME [--scope SCOPE] [--config PATH] [--url URL]

Delete a secret (secrets.delete)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--name NAMEThe secret name
--scope SCOPEDefault tenant
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute secrets delete --name hf-token

Metal

metal enroll

Enroll a machine by its BMC into a pool (metal.enroll)

better-compute metal enroll --tenant ID --site ID --pool ID --bmc-address HOST|URL --bmc-driver DRIVER [--bmc-credentials REF] [--bmc-username USER] [--bmc-password-file PATH] [--bmc-tls-spki HEX] [--macs MAC,…] [--product-uuid UUID] [--arch ARCH] [--legacy-bios] [--hardware-profile NAME] [--config PATH] [--url URL]

metal enroll creates a node in --pool of --site from its BMC (--bmc-address, --bmc-driver) and queues BMC verification, inspection and commissioning. The BMC credentials are a reference (--bmc-credentials secret://…, vault://… or env://…), or --bmc-username with --bmc-password-file, which the server stores in its secret broker at once and never echoes. --bmc-tls-spki pins the BMC certificate. Decided as compute:nodes:provision.

| Flag | Description | Default | Environment | | -------------------------- | ------------------------------------------------------------------------------------------------------------- | --------------- | ----------------------- | --- | | --tenant ID | The tenant acting (the caller’s organization) | | BETTER_COMPUTE_TENANT | | --site ID | The site the machine is in | | | | --pool ID | The pool it joins | | | | --bmc-address HOST | URL | The BMC address | | | | --bmc-driver DRIVER | The BMC driver, e.g. redfish | | | | --bmc-credentials REF | secret://, vault:// or env:// reference | | | | --bmc-username USER | With --bmc-password-file, instead of a reference | | | | --bmc-password-file PATH | A file holding the BMC password | | | | --bmc-tls-spki HEX | SHA-256 of the BMC certificate’s public key | | | | --macs MAC,… | Its MAC addresses, for PXE and matching | | | | --product-uuid UUID | Its SMBIOS product UUID | | | | --arch ARCH | Default x86_64 | | | | --legacy-bios | Boots in legacy BIOS mode, not UEFI | | | | --hardware-profile NAME | Overrides the pool’s hardware profile | | | | --config PATH | Configuration module; default: the nearest better-compute.config.{mjs,js,ts,mts} | | BETTER_COMPUTE_CONFIG | | --url URL | Call a running compute server (https://host or https://host/api/compute) instead of loading the configuration | | BETTER_COMPUTE_URL | | --format FORMAT | Output as json (indented, default), compact (one line), or table | | | | --query PATH | Print only part of the result, e.g. protocol.max or findings[].check (strings print raw) | | |

better-compute metal enroll --site site_01k5w… --pool pool_01k5w… --bmc-address 10.20.0.14 --bmc-driver redfish --bmc-credentials secret://org_acme/bmc-r12

metal manage

Re-verify a ready node’s BMC access, then re-inspect and re-commission it (metal.manage)

better-compute metal manage NODE --tenant ID [--config PATH] [--url URL]

Re-verify a ready node’s BMC access, then re-inspect and re-commission it (metal.manage)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal manage node_01k5w…

metal inspect

Boot a ready node into the ramdisk to report its inventory (metal.inspect)

better-compute metal inspect NODE --tenant ID [--config PATH] [--url URL]

Boot a ready node into the ramdisk to report its inventory (metal.inspect)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal inspect node_01k5w…

metal provide

Commission a ready node: diagnostics, a quick wipe, power off (metal.provide)

better-compute metal provide NODE --tenant ID [--config PATH] [--url URL]

Commission a ready node: diagnostics, a quick wipe, power off (metal.provide)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal provide node_01k5w…

metal rescue

Boot a deployed node into the rescue ramdisk (metal.rescue)

better-compute metal rescue NODE --tenant ID [--config PATH] [--url URL]

Boot a deployed node into the rescue ramdisk (metal.rescue)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal rescue node_01k5w…

metal unrescue

Boot a rescued node back into its installed OS (metal.unrescue)

better-compute metal unrescue NODE --tenant ID [--config PATH] [--url URL]

Boot a rescued node back into its installed OS (metal.unrescue)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal unrescue node_01k5w…

metal recover

Move a failed node back to the last stable point for the state it failed in (metal.recover)

better-compute metal recover NODE --tenant ID [--config PATH] [--url URL]

Move a failed node back to the last stable point for the state it failed in (metal.recover)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal recover node_01k5w…

metal retire

Retire a node: its retiring workflow erases its disks and powers it off (metal.retire)

better-compute metal retire NODE --tenant ID [--config PATH] [--url URL]

Retire a node: its retiring workflow erases its disks and powers it off (metal.retire)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal retire node_01k5w…

metal deploy

Deploy a provision profile version on a ready node (metal.deploy)

better-compute metal deploy NODE --tenant ID --profile ID --profile-version N [--config PATH] [--url URL]

Deploy a provision profile version on a ready node (metal.deploy)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--profile IDThe provision profile
--profile-version NIts version
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal deploy node_01k5w… --profile ubuntu-gpu --profile-version 3

metal undeploy

Release a deployed node: wipe its disks and power it off (metal.undeploy)

better-compute metal undeploy NODE --tenant ID [--wipe MODE] [--config PATH] [--url URL]

Release a deployed node: wipe its disks and power it off (metal.undeploy)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--wipe MODEHow the disks are wiped (default quick)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal undeploy node_01k5w… --wipe secure-erase

metal abort

Stop a node’s running workflow and mark it failed (metal.abort)

better-compute metal abort NODE --tenant ID [--reason TEXT] [--config PATH] [--url URL]

Stop a node’s running workflow and mark it failed (metal.abort)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--reason TEXTRecorded as its last error
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal abort node_01k5w… --reason "wrong profile"

metal discovered

Machines a site saw PXE-booting from unknown MACs (metal.discovered)

better-compute metal discovered --tenant ID --site ID [--config PATH] [--url URL]

metal discovered lists what the site agent saw PXE/HTTP-boot from MACs with no boot entry: MAC, SMBIOS UUID, architecture, relay, first and last seen, and the enrolled machine it is a candidate for. Nothing in it is trusted or bound: confirm a candidate with metal confirm-mac.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--site IDThe site (site_…)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal discovered --site site_01k5w… --format table

metal confirm-mac

Bind the PXE MAC of a machine enrolled without one (metal.confirmMac)

better-compute metal confirm-mac NODE --tenant ID --mac MAC [--config PATH] [--url URL]

metal confirm-mac is the operator's decision that --mac is this machine's PXE MAC, usually the candidate metal discovered shows for it. Only then is the machine booted from that MAC; a MAC another machine holds is CONFLICT. The decision is audited (node.pxe-mac.confirmed).

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--mac MACThe machine's PXE MAC (aa:bb:cc:dd:ee:ff)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal confirm-mac node_01k5w… --mac 3c:ec:ef:12:34:56

metal power

Send a BMC power action through the site agent (metal.power)

better-compute metal power NODE --tenant ID --action ACTION [--config PATH] [--url URL]

Send a BMC power action through the site agent (metal.power)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--action ACTIONThe power action
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal power node_01k5w… --action graceful-restart

metal bulk

Run one lifecycle verb on many nodes of a pool as one batch (metal.bulk)

better-compute metal bulk --tenant ID --pool ID --verb VERB [--nodes ID,…] [--nodes-file PATH] [--wipe MODE] --reason TEXT [--config PATH] [--url URL]

metal bulk runs --verb on the nodes in --nodes (comma-separated) or --nodes-file (one per line) of --pool as one batch; destructive verbs wait for the batch quorum’s approvers, who see --reason. Decided as compute:nodes:provision.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe pool every node is in
--verb VERBThe verb every node gets
--nodes ID,…The nodes
--nodes-file PATHThe nodes, one id per line
--wipe MODEHow the disks are wiped (default quick)
--reason TEXTWhat the batch is for
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute metal bulk --pool pool_01k5w… --verb retire --nodes-file retire.txt --reason "EOL rack r12"

profiles list

List provision profiles: the tenant’s, public ones and the modules’ (profiles.list)

better-compute profiles list --tenant ID [--limit N] [--cursor CURSOR] [--config PATH] [--url URL]

List provision profiles: the tenant’s, public ones and the modules’ (profiles.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--limit NPage size (at most 200)
--cursor CURSORThe cursor of the previous page
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute profiles list

profiles get

Show a profile version, the latest by default (profiles.get)

better-compute profiles get PROFILE --tenant ID [--profile-version N] [--config PATH] [--url URL]

Show a profile version, the latest by default (profiles.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--profile-version NThe version (default: the latest)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute profiles get ubuntu-gpu --profile-version 3

profiles validate

Check a profile version against the registered steps (profiles.validate)

better-compute profiles validate PROFILE --tenant ID [--profile-version N] [--config PATH] [--url URL]

Check a profile version against the registered steps (profiles.validate)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--profile-version NThe version (default: the latest)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute profiles validate ubuntu-gpu --profile-version 3

profiles render

Show the steps a workflow of a profile version would run (profiles.render)

better-compute profiles render PROFILE --tenant ID [--profile-version N] [--config PATH] [--url URL]

Show the steps a workflow of a profile version would run (profiles.render)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--profile-version NThe version (default: the latest)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute profiles render ubuntu-gpu --profile-version 3

profiles create

Store a profile version from a JSON document (profiles.create)

better-compute profiles create FILE --tenant ID [--public] [--config PATH] [--url URL]

Store a profile version from a JSON document (profiles.create)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--publicLet every tenant use it (default private)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute profiles create ./ubuntu-gpu.v3.json

profiles update

Replace a profile version’s document or change its visibility (profiles.update)

better-compute profiles update PROFILE --tenant ID --profile-version N [--file PATH] [--visibility VIS] [--if-version N] [--config PATH] [--url URL]

Replace a profile version’s document or change its visibility (profiles.update)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--profile-version NThe version to change
--file PATHA replacement document (same id and version)
--visibility VISWho may use it
--if-version NOnly if the row is still at this version
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute profiles update ubuntu-gpu --profile-version 3 --visibility public

profiles delete

Hide a profile version, or every version, from new workflows (profiles.delete)

better-compute profiles delete PROFILE --tenant ID [--profile-version N] [--config PATH] [--url URL]

Hide a profile version, or every version, from new workflows (profiles.delete)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--profile-version NOnly this version (default: every version)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute profiles delete ubuntu-gpu --profile-version 2

provider-accounts list

List the tenant’s provider accounts (providerAccounts.list)

better-compute provider-accounts list --tenant ID [--config PATH] [--url URL]

List the tenant’s provider accounts (providerAccounts.list)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute provider-accounts list

provider-accounts get

Show a provider account (providerAccounts.get)

better-compute provider-accounts get ACCOUNT --tenant ID [--config PATH] [--url URL]

Show a provider account (providerAccounts.get)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute provider-accounts get pacct_01k5w…

provider-accounts delete

Delete a provider account no pool or instance uses (providerAccounts.delete)

better-compute provider-accounts delete ACCOUNT --tenant ID [--config PATH] [--url URL]

Delete a provider account no pool or instance uses (providerAccounts.delete)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute provider-accounts delete pacct_01k5w…

provider-accounts create

Store a provider account; its config is checked by the provider (providerAccounts.create)

better-compute provider-accounts create --tenant ID --provider ID --name NAME [--config-file PATH] [--key KEY] [--config PATH] [--url URL]

provider-accounts create names an account of --provider and stores --config-file (JSON), checked against the provider’s manifest. Credentials in it are secret references only (secret://…), never values.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--provider IDThe provider, e.g. provider-hetzner
--name NAMEThe account name
--config-file PATHThe account config, as JSON
--key KEYIdempotency key: a retry returns the same account
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute provider-accounts create --provider provider-hetzner --name hetzner-main --config-file hetzner.json

provider-accounts update

Replace a provider account’s config (providerAccounts.update)

better-compute provider-accounts update ACCOUNT --tenant ID --config-file PATH [--config PATH] [--url URL]

Replace a provider account’s config (providerAccounts.update)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--config-file PATHThe new config, as JSON
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute provider-accounts update pacct_01k5w… --config-file hetzner.json

provider-accounts offers

Ask the provider which machine types the account can get (providerAccounts.offers)

better-compute provider-accounts offers ACCOUNT --tenant ID [--kind KIND] [--region REGION] [--gpu-model MODEL] [--config PATH] [--url URL]

Ask the provider which machine types the account can get (providerAccounts.offers)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--kind KINDOnly this kind
--region REGIONOnly this provider region
--gpu-model MODELOnly this GPU model
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute provider-accounts offers pacct_01k5w… --kind metal --gpu-model H100

provider-accounts bootstrap

Turn a provider machine into a bcd host of a pool (providerAccounts.bootstrap)

better-compute provider-accounts bootstrap ACCOUNT --tenant ID --pool ID [--machine ID] [--offer ID] [--key KEY] [--config PATH] [--url URL]

Turn a provider machine into a bcd host of a pool (providerAccounts.bootstrap)

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pool IDThe vps-host or metal-host pool it joins
--machine IDA machine the account already has
--offer IDAn offer to order a machine from
--key KEYIdempotency key, to retry the same request
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute provider-accounts bootstrap pacct_01k5w… --pool pool_01k5w… --offer ax102-h100

Fleet

fleet export

Write the tenant's platform objects as a FleetConfig document (fleet.export)

better-compute fleet export --tenant ID [--out PATH] [--config PATH] [--url URL]

fleet export prints (or writes with --out) the sites, provider accounts, pools with their owner quotas and offers, and VPS flavors the tenant owns, keyed by name. Objects fleet did not create are named by their id; rename them in the document and the next apply keeps them. Secrets appear only as references.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--out PATHWrite the document to a new file
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute fleet export --out fleet.json

fleet plan

Show what applying a FleetConfig document would change (fleet.plan)

better-compute fleet plan FILE --tenant ID [--prune] [--fail-on-drift] [--calls] [--config PATH] [--url URL]

fleet plan diffs the document against the tenant's current objects and prints each change: + create, ~ update (with the fields), - delete (only with --prune), and BLOCKED for changes the platform cannot make (a site's region, a pool's class or site, dangling names). It changes nothing. The printed planHash, passed to fleet apply --plan-hash, applies exactly this plan. With --fail-on-drift it exits FLEET_DRIFTED when anything would change, for drift checks in CI; a blocked plan always exits PLAN_BLOCKED.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pruneAlso delete what the document no longer lists (pools are archived)
--fail-on-driftExit FLEET_DRIFTED when the plan is not empty
--callsAlso print the API calls apply would make
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute fleet plan fleet.json
better-compute fleet plan fleet.json --prune --fail-on-drift

fleet status

Show the last applied document and whether the fleet drifted from it (fleet.status)

better-compute fleet status --tenant ID [--fail-on-drift] [--config PATH] [--url URL]

fleet status shows the planHash, author and time of the tenant's last fleet apply, and the drift the fleet.drift job found when it last re-planned that document (every 15 minutes by default): changes made outside fleet, listed like fleet plan. With --fail-on-drift it exits FLEET_DRIFTED while there is drift, for monitoring.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--fail-on-driftExit FLEET_DRIFTED while the fleet drifted
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute fleet status
better-compute fleet status --fail-on-drift

fleet apply

Apply a FleetConfig document (fleet.plan, then fleet.apply)

better-compute fleet apply FILE --tenant ID [--prune] [--plan-hash HASH] [--yes] [--config PATH] [--url URL]

fleet apply plans the document, prints the changes, then applies exactly that plan. Every underlying call (sites.create, pools., quotas.set, flavors., providerAccounts.*) is decided for you before anything runs, and then all of them run in one transaction, so a denial or failure applies nothing. If the fleet changes between the plan and the apply, nothing is applied and the command exits CONFIG_DRIFT. Pass --plan-hash to apply a plan reviewed earlier with fleet plan; without --yes it only plans.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--pruneAlso delete what the document no longer lists (pools are archived)
--plan-hash HASHApply only if the plan still has this hash (from fleet plan)
--yesApply without stopping after the plan
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute fleet apply fleet.json --yes
better-compute fleet apply fleet.json --prune --plan-hash 3f1c…

daemon install-script

Write the POSIX sh script that installs bcd on a server and joins it to a pool

better-compute daemon install-script --pool ID --site ID --gateway-url URL --base-url URL [--base-path PATH] --bcd-version VERSION [--sha256 ARCH=HEX,…] [--release-manifest PATH] [--release-pins PINS] [--release-threshold N] [--plugins] [--install-plugins NAMES] [--unprivileged] [--attestor tpm] [--websocket-url URL] [--websocket-audience HOST] [--proxy-ca-file PATH] [--bmc-networks CIDRS] [--approver-set-sha256 HEX] [--instance-bridges BRIDGES] [--command-root-sha256 HEX,…] [--privileged-plugins NAMES] [--release-sequence N] [--pki PATH] [--out PATH]

daemon install-script emits the bcd installer (PLAN.md §3.3, §6). The script reads the join token only from BCD_JOIN_TOKEN or standard input, downloads the pinned bcd for the machine's architecture from {base-url}{base-path}/downloads/bcd/{version}/linux-{arch}, checks it against --sha256 (or the digests of a --release-manifest verified here under --release-pins, product bcd, version --bcd-version), installs /usr/local/bin/bcd, /etc/bcd/config.toml (mode 0600: gateway URL, site and the serving CA, join signing and join encryption key pins, in bcd's sha256/<base64> form), the JoinService encryption key, and a hardened bcd.service, then runs bcd join and starts the service. Run on a configured host without a token, it upgrades bcd in place. The pins come from the PKI bundle's public half (pki init), so no secret is needed. The script holds no secret either: create the join token separately (joinTokens.create) and hand it over out of band. --plugins prepares the node for bcx driver plugins: a [plugins] section (dir /usr/local/lib/bcd/plugins, a child of bcd's delegated cgroup) and a bcd.service.d drop-in that lets bcd run each plugin under its own uid in its own cgroup (CAP_SETUID/CAP_SETGID/CAP_KILL, Delegate=yes); --privileged-plugins names the ones that run as root and make namespaces (Kata). --install-plugins bcx-ipmi also installs plugins a release ships: each archive from {base-url}{base-path}/downloads/{name}/{version}/linux-{arch} is checked against its digest in the verified --release-manifest, only the plugin's own regular file is taken from it, and its binary digest becomes its line in the plugins' pins file. Any node whose config.toml has a [plugins] section gets the drop-in, and an upgrade keeps the sections the operator configured.

FlagDescriptionDefaultEnvironment
--pool IDThe pool the node joins (named in the script)
--site IDThe pool's site id (config.toml site)
--gateway-url URLThe regional gateway bcd dials, https://…BETTER_COMPUTE_GATEWAY_URL
--base-url URLThe control plane serving the bcd downloads, https://…BETTER_COMPUTE_BASE_URL
--base-path PATHThe API's base path/api/compute
--bcd-version VERSIONThe bcd release to install, e.g. 0.1.0BETTER_COMPUTE_BCD_VERSION
--sha256 ARCH=HEX,…SHA-256 of each architecture’s binary: amd64=<hex>,arm64=<hex> (or use --release-manifest)
--release-manifest PATHA signed bcd release manifest: its verified digests are pinned instead
--release-pins PINSComma-separated sha256/… pins of the release keysBCD_RELEASE_PINS
--release-threshold NRelease keys that must have signed (default 1)
--pluginsPrepare the node for bcx driver plugins ([plugins], the systemd drop-in)
--install-plugins NAMESInstall these bcx plugins from the release, pinned by --release-manifest (bcx-ipmi; implies --plugins)
--unprivilegedRun bcd node as the unprivileged bcd user beside a root bcd helper (privilege separation)
--attestor tpmJoin with the host's TPM (the tpm attestor: its endorsement key is the credential; the attestor's policy decides the pool) instead of a join token
--websocket-url URLDial the gateway over WebSocket (wss://…/v1/session) behind TLS-inspecting proxies
--websocket-audience HOSTThe gateway's public host the WebSocket proof names (default: the URL's host)
--proxy-ca-file PATHThe TLS-inspecting proxy's CA (PEM), trusted for the WebSocket's TLS only
--bmc-networks CIDRSA site agent's BMC networks (comma-separated): site.httpProxy never reaches into them
--approver-set-sha256 HEXPin the deployment's approver set (pki trust-version prints it): its first set, or nothing destructive
--instance-bridges BRIDGESWith --unprivileged, on VPS and sandbox hosts: the bridges instance NICs may join ([helper] instance_bridges, comma-separated)
--command-root-sha256 HEX,…Pin the command-signing roots (pki trust-version prints them): bcd helper applies host config units only from specs signed under them. Default: the PKI bundle’s current and next roots
--privileged-plugins NAMESPlugins that run as root without the seccomp filter (comma-separated; implies --plugins)
--release-sequence Nbcd's release floor ([update] release_sequence): it never installs an older release
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--out PATHWrite the script to a new file (mode 0755) instead of standard output
better-compute daemon install-script --pool pool_h100 --site fsn1 --gateway-url https://gw.eu.example.com --base-url https://compute.example.com --bcd-version 0.1.0 --sha256 amd64=<hex> > install-bcd.sh
BCD_JOIN_TOKEN=bcjt_... sudo -E sh install-bcd.sh

PKI

pki init

Create the machine PKI: root, node, serving and internal CAs and the JoinService keys

better-compute pki init --trust-domain DOMAIN [--pki PATH] [--offline-root PATH] [--organization NAME] [--plaintext]

pki init creates a deployment's machine PKI (PLAN.md §13) and writes it to one JSON file with mode 0600: an offline root CA, the per-region node CA (agent client certificates), the gateway serving CA, the internal control-plane CA, and the JoinService signing and encryption keys. No key does two jobs. The private keys are sealed with AES-256-GCM under a key derived from BETTER_COMPUTE_SECRET; without the secret, pass --plaintext to store them as PEM instead. --offline-root writes the root key to its own file and leaves it out of the bundle: keep that file off the servers. It prints the pins install scripts need (the same as pki show) and never overwrites an existing file.

FlagDescriptionDefaultEnvironment
--trust-domain DOMAINSPIFFE trust domain of agent identities, e.g. compute.example.comBETTER_COMPUTE_TRUST_DOMAIN
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--offline-root PATHWrite the root key here (sealed like the bundle) and leave it out of the bundle
--organization NAMESubject organization of the CA certificatesbetter-compute
--plaintextStore private keys as PEM (mode 0600) when BETTER_COMPUTE_SECRET is not set
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
BETTER_COMPUTE_SECRET=... better-compute pki init --trust-domain compute.example.com
better-compute pki init --trust-domain compute.test --plaintext --pki ./dev/pki.json
better-compute pki init --trust-domain compute.example.com --offline-root /media/usb/root-ca.key

pki show

Print the pins install scripts and bcd need, and the CA expiry dates

better-compute pki show [--pki PATH]

pki show prints the trust domain, the gateway serving CA's SPKI pin, the JoinService signing and encryption key pins in bcd's sha256/<base64> form, the encryption key as a JWK, and when each CA expires. With BETTER_COMPUTE_SECRET the public half is checked against the seal first (authenticated: true); without it the pins come from a file nothing vouches for, and a warning says so.

FlagDescriptionDefaultEnvironment
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pki show
better-compute pki show --query servingCaSpkiPin

pki add-sandbox-ca

Add the sandbox SSH CA to a bundle made before sandboxes

better-compute pki add-sandbox-ca [--pki PATH]

pki add-sandbox-ca creates the sandbox SSH CA (PLAN.md §3.7) in a bundle that lacks it and writes the bundle back (sealed with BETTER_COMPUTE_SECRET when set). sandboxes.exec signs with it (the tunnels module's sandboxSsh, pki tunnelsOptions()) and sandbox guests trust its public key (vps({ sandboxExec: { caPublicKey } })); it is separate from the break-glass CA, so a sandbox certificate never opens a node. Bundles from pki init already have one.

FlagDescriptionDefaultEnvironment
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pki add-sandbox-ca

pki reseal

Seal the bundle in the current format, authenticating its public parts

better-compute pki reseal [--pki PATH] [--accept-unauthenticated]

pki reseal rewrites the bundle sealed with BETTER_COMPUTE_SECRET in the current format, where the seal also covers every certificate and pin, so a file changed without the secret no longer loads. Bundles sealed before that format, or left unsealed while a secret is set, are refused everywhere else. First compare the pins it prints (the same as pki show) with the ones your install scripts and join tokens use, and check certificates (the SHA-256 of every certificate in every chain) holds nothing you did not put there: it seals whatever the file holds now. Needs BETTER_COMPUTE_SECRET; the current key and certificates are kept, nothing is rotated.

FlagDescriptionDefaultEnvironment
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--accept-unauthenticatedSeal a bundle nothing authenticates, after comparing the pins printed without it
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pki reseal
better-compute pki reseal --accept-unauthenticated

pki rotate-command-root

Rotate the command-signing root in three steps: add, switch, drop

better-compute pki rotate-command-root --stage STAGE [--pki PATH] [--force]

pki rotate-command-root performs one step of a command-signing root rotation (PLAN.md §13) on the bundle and writes it back (sealed with BETTER_COMPUTE_SECRET when set). --stage add creates a new root and publishes it beside the current one (the trust bundle's version goes up); workers keep signing under the current root. Roll the bundle out to every process: daemons receive the new version in Welcome.trust. --stage switch, once every daemon holds that version, makes workers sign under the new root while the old one stays trusted. --stage drop, at least 24 hours later (worker keys live at most 24 h), removes the old root (version up again). Each step prints the roots and version daemons will trust.

FlagDescriptionDefaultEnvironment
--stage STAGEadd a new root, switch signing to it, or drop the old one
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--forceswitch without checking that every connected daemon has the new trust version
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pki rotate-command-root --stage add
better-compute pki rotate-command-root --stage switch
better-compute pki rotate-command-root --stage drop

pki trust-version

Show the trust bundle version the deployment publishes, and the one to sign a quorum change for

better-compute pki trust-version [--pki PATH] [--config PATH]

pki trust-version reads the trust bundle the deployment last published (its version follows its content: a changed bundle publishes the next version, pki.trustVersion is a floor) and compares it with this configuration and PKI bundle: whether they publish the same bundle, the published approver-set epoch, whether the configured approver quorum differs (then its current approvers sign it for signQuorumForEpoch: better-compute batch sign-approvers --epoch <n>, with pki.approvers.epoch set to it), and how many connected daemons hold that version.

FlagDescriptionDefaultEnvironment
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pki trust-version

pki renew-ca

Renew a CA's certificate under the root, keeping its key

better-compute pki renew-ca --ca CA [--root-key PATH] [--days N] [--pki PATH]

pki renew-ca issues a new certificate for the node, serving or internal CA from the root CA, for the same key and subject, and writes the bundle back (sealed with BETTER_COMPUTE_SECRET when set). Certificates the CA issued stay valid and the serving CA's SPKI pin does not change, so nothing is re-pinned and no node rejoins; roll the bundle out to every process before the old certificate expires (doctor warns 30 days ahead). The root key comes from the bundle or, for an offline root, from --root-key; it is never written into the bundle then.

FlagDescriptionDefaultEnvironment
--ca CAWhich CA to renew
--root-key PATHThe offline root key (pki init --offline-root), when the bundle has none
--days NLifetime of the new certificate in days (capped by the root)365
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pki renew-ca --ca node
better-compute pki renew-ca --ca serving --root-key /media/usb/root-ca.key

pki issue-gateway

Issue a gateway serving certificate and key from the serving CA

better-compute pki issue-gateway --san NAMES [--out DIR] [--name NAME] [--days N] [--pki PATH]

pki issue-gateway generates a P-256 key and issues a serving certificate for it from the bundle's gateway serving CA (PLAN.md §7.1): DNS SANs from --san (comma-separated), serverAuth, 30 days by default. It writes <name>.key (mode 0600) and <name>.crt (the certificate followed by the serving CA's chain) into --out, replacing earlier files atomically so a running gateway can hot-reload them. Daemons trust the certificate through the serving CA pin (pki show).

FlagDescriptionDefaultEnvironment
--san NAMESDNS names, comma-separated, e.g. gw.eu-central.example.com
--out DIRDirectory for the key and certificate.better-compute/gateway
--name NAMEFile name stem: NAME.key and NAME.crtgateway
--days NCertificate lifetime in days30
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute pki issue-gateway --san gw.eu-central.example.com
better-compute pki issue-gateway --san gw1.example.com,gw.example.com --out /etc/better-compute/tls --days 14

Quorum

batch keygen

Create an approver key pair for destructive batches

better-compute batch keygen --out PATH

batch keygen creates an ECDSA P-256 approver key (PLAN.md §13): the private key (PKCS#8 PEM, mode 0600) at --out, the public key beside it at <out>.pub. Keep the private key with its operator, off the control plane (a hardware token or an offline machine is better). Add the public key to the deployment's pki.approvers.keys, e.g. approverKeyOf(readFileSync('alice.pem.pub', 'utf8')).spkiDer from @better-compute/pki, with pki.approvers.threshold; daemons receive it in their trust bundle. The kid printed is how approvals name the key. With BETTER_COMPUTE_APPROVER_PASSPHRASE set, the private key is encrypted under it (and every batch command reads it with it).

FlagDescriptionDefaultEnvironment
--out PATHWhere to write the private key (never overwritten)
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute batch keygen --out ~/.better-compute/approver.pem

batch sign

Sign a batch approval offline, from its scope

better-compute batch sign --key PATH [--batch ID] [--scope PATH] [--root HEX] [--size N] [--not-after TIME] [--trust-domain DOMAIN] [--pki PATH] [--expect PATH] [--allow-unbound] [--allow-opaque] [--operations-out PATH] [--max-hours N] [--unchecked]

batch sign signs one approval of a destructive batch with an approver key (--key, from batch keygen) without contacting the server: for approvers on an offline machine. Pass the batch's scope (--scope, the JSON batch show prints, or an array of { subject, kind }): the root is recomputed from it and every subject is shown by kind before signing (--expect checks them against your own list). --root and --size, when given as well, must match what the scope gives; signing --root and --size alone, a root the CLI cannot check, needs --unchecked. --not-after is the batch's expiry as the server set it (all approvers sign the same one), at most --max-hours away. Prints the signature ({ kid, sig }) to hand to batch approve --signature.

FlagDescriptionDefaultEnvironment
--key PATHThe approver private key (PEM)
--batch IDThe batch id (default: the scope file's batchId)
--scope PATHThe batch scope (JSON), to recompute the root from
--root HEXThe scope root (64 hex digits), checked against --scope
--size NThe scope size, checked against --scope
--not-after TIMEThe batch's expiry: ISO-8601 or epoch milliseconds (default: the scope file's notAfter)
--trust-domain DOMAINThe deployment you approve for (default: the PKI bundle's trust domain); any other is refusedBETTER_COMPUTE_TRUST_DOMAIN
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--expect PATHApprove only if the batch holds exactly these subjects (one per line) or { subject, kind } commands
--allow-unboundApprove commands whose payload the batch does not fix (shown as "any payload")
--allow-opaqueApprove commands whose operation the batch shows only as a digest
--operations-out PATHAlso write every distinct operation in full (JSON), with the subjects that run it
--max-hours NRefuse a batch that stays open longer than this many hours72
--uncheckedSign --root and --size without a scope to check them against
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute batch sign --key approver.pem --scope batch.json --not-after 2026-09-25T18:00:00Z
better-compute batch sign --key approver.pem --scope batch.json --expect my-nodes.txt

batch sign-approvers

Sign a change of the approver set with a current approver key

better-compute batch sign-approvers --key PATH --epoch N [--approvers PATHS] [--threshold K] [--tiers SIZE:K,…] [--unbatched N] [--trust-domain DOMAIN] [--pki PATH]

batch sign-approvers signs, as one of the CURRENT approvers (--key), the quorum a new trust bundle will carry: its approver keys (--approvers, the .pub files of the whole new set), --threshold, --tiers and --unbatched, for approver-set --epoch (one above the current set's; better-compute pki trust-version prints it) in the deployment's --trust-domain (default: the PKI bundle's; a set signed for staging never changes prod's quorum, even with shared approver keys). Daemons that hold approver keys take a changed quorum, removal included, only with such signatures from their current threshold of current approvers (PLAN.md §13): the join key alone, which the control plane holds, cannot lift or replace it. Members of the NEW set sign it too (their signatures move daemons that already hold that set to the new epoch); give all the signatures to the deployment's pki.approvers.setSignatures with pki.approvers.epoch; they stay valid on every later trust bundle version while the set is current. Prints the set's digest and the signature.

FlagDescriptionDefaultEnvironment
--key PATHYour CURRENT approver private key (PEM)
--epoch NThe new set's epoch: one above the current set's
--approvers PATHSThe new set: approver public keys (.pub files, comma-separated); empty lifts the quorum
--threshold KApprovals a batch needs in the new set
--tiers SIZE:K,…Per-size thresholds, ascending, e.g. 5:1,100:2
--unbatched NUnbatched destructive commands per hour0
--trust-domain DOMAINThe deployment you approve for (default: the PKI bundle's trust domain); any other is refusedBETTER_COMPUTE_TRUST_DOMAIN
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute batch sign-approvers --key alice.pem --epoch 2 --approvers alice.pem.pub,bob.pem.pub,carol.pem.pub --threshold 2

batch list

List destructive batches, newest first

better-compute batch list --tenant ID [--state STATE] [--limit N] [--cursor ID] [--config PATH] [--url URL]

batch list pages commandBatches.list: each batch with its reason, state (pending, approved, expired, cancelled), scope size, the approvals it has and needs, and its member commands by state. Needs compute:batches:read.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--state STATEOnly batches in this state
--limit NBatches per page50
--cursor IDThe previous page's next
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute batch list --tenant org_acme --state pending

batch show

Show a batch and check its scope against its root

better-compute batch show --tenant ID --batch ID [--scope-out PATH] [--config PATH] [--url URL]

batch show reads a batch (commandBatches.get), pages its scope (commandBatches.scope) and recomputes the Merkle root from the listed (subject, kind) commands: it fails with SCOPE_MISMATCH when they do not give the root the server asks approvers to sign. --scope-out writes the verified scope with the batch's expiry, the input of batch sign on an offline machine.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--batch IDThe batch (batch_…)
--scope-out PATHWrite the verified scope (JSON) for batch sign
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute batch show --tenant org_acme --batch batch_01k5w…
better-compute batch show --tenant org_acme --batch batch_01k5w… --scope-out batch.json

batch approve

Approve a destructive batch with your approver key

better-compute batch approve --tenant ID --batch ID [--key PATH] [--signature PATH] [--root HEX] [--expect PATH] [--allow-unbound] [--allow-opaque] [--operations-out PATH] [--max-hours N] [--trust-domain DOMAIN] [--pki PATH] [--yes] [--config PATH] [--url URL]

batch approve reads the batch, pages its scope and recomputes the root from the listed commands (refusing with SCOPE_MISMATCH when they do not give it), shows the reason, every subject by kind (--expect checks them against your own list) and the approvals so far, and then signs the batch's root, size and expiry with --key and submits the signature (commandBatches.approve, compute:batches:approve). Without --yes it stops after showing the batch; --root approves only the root you reviewed. Once the batch holds the required approvals it is released and its commands run. With --signature, submits a signature made offline by batch sign instead, after checking it is for this batch.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--batch IDThe batch (batch_…)
--key PATHYour approver private key (from batch keygen)
--signature PATHA signature from batch sign (instead of --key)
--root HEXApprove only if the batch still has this root (64 hex digits)
--expect PATHApprove only if the batch holds exactly these subjects (one per line) or { subject, kind } commands
--allow-unboundApprove commands whose payload the batch does not fix (shown as "any payload")
--allow-opaqueApprove commands whose operation the batch shows only as a digest
--operations-out PATHAlso write every distinct operation in full (JSON), with the subjects that run it
--max-hours NRefuse a batch that stays open longer than this many hours72
--trust-domain DOMAINThe deployment you approve for (default: the PKI bundle's trust domain); any other is refusedBETTER_COMPUTE_TRUST_DOMAIN
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--yesSign and submit without stopping after the summary
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute batch approve --tenant org_acme --batch batch_01k5w… --key ~/.better-compute/approver.pem
better-compute batch approve --tenant org_acme --batch batch_01k5w… --key approver.pem --root 3f1c…
better-compute batch approve --tenant org_acme --batch batch_01k5w… --signature alice.sig.json

batch create

Create a destructive batch that waits for its approvers

better-compute batch create --tenant ID --reason TEXT --scope PATH [--hours N] [--key KEY] [--config PATH] [--url URL]

batch create creates a batch (commandBatches.create, compute:batches:create) from --scope FILE: a JSON array of { subject, kind, payload } leaves. subject is the node id for kinds a node runs on itself, bmc:<site agent node id>/<address> for bmc.*, node:<target> for site.httpProxy. payload binds the leaf to exactly one operation, which approvers are shown: { json } (the kind's payload message as proto3 JSON, encoded here) or { typeUrl, value } (base64); a leaf without one admits any payload of its kind and approvers must accept that explicitly. The batch admits its commands for --hours (default 24, at most 72) once approved; run them with commands.run { batchId }. Prints the batch; approvers then run better-compute batch approve.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--reason TEXTWhat the batch does, shown to approvers
--scope PATHThe leaves (JSON array of { subject, kind, payload })
--hours NHow long it admits its commands24
--key KEYIdempotency key: repeat it to retry the same creation
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute batch create --tenant org_acme --reason "reimage rack 7" --scope rack7.json

batch members

List a batch's commands: node, kind, state, whether held for approval

better-compute batch members --tenant ID --batch ID [--state STATE] [--limit N] [--cursor ID] [--config PATH] [--url URL]

batch members pages commandBatches.members: every command enqueued into the batch with its node (the site agent for bmc: subjects), subject, kind and state; held ones are awaitingApproval until the batch is approved. Needs compute:batches:read.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--batch IDThe batch (batch_…)
--state STATEOnly commands in this state (pending, succeeded, …)
--limit NCommands per page100
--cursor IDThe previous page's next
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute batch members --tenant org_acme --batch batch_01k5w… --state pending

batch cancel

Cancel a pending batch and its held commands

better-compute batch cancel --tenant ID --batch ID [--config PATH] [--url URL]

batch cancel ends a pending batch (commandBatches.cancel, compute:batches:approve): none of its commands run, and the pending ones are cancelled with reason batch-cancelled.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenant acting (the caller’s organization)BETTER_COMPUTE_TENANT
--batch IDThe batch (batch_…)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--url URLCall a running compute server (https://host or https://host/api/compute) instead of loading the configurationBETTER_COMPUTE_URL
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute batch cancel --tenant org_acme --batch batch_01k5w…

IAM

iam setup

Print the example compute policies and create the root billing meters in better-iam

better-compute iam setup --audience URL [--gpu-model MODEL] [--regions LIST] [--max-gpus N] [--max-hours N] [--feature FLAG] [--meters] [--iam-url URL] [--iam-base-path PATH] [--root-tenant ID] [--gpu-models LIST]

iam setup prints the PLAN.md §12.6 example policies, gpu-user and agent-guardrails, for an organization administrator to apply: compute cannot write organization policies. The policies are parameterized by the session JWT audience (the compute API's URL), the GPU model, regions and limits of the bounded-reservation statement. With --meters it also creates the §12.7 root meters in better-iam: one gpu.<model>.hours per GPU model family, plus node.metal.hours, vps.vcpu.hours and vps.memory.gib.hours. Meters that already exist are left alone, so re-running is safe. Creating meters calls better-iam at --iam-url as BETTER_IAM_TOKEN, which must be a root credential, through the optional peer @better-iam/client. It sets no prices: pricing is an operator decision (billing.setPrice).

FlagDescriptionDefaultEnvironment
--audience URLThe compute API URL, used as the session JWT audienceBETTER_COMPUTE_BASE_URL
--gpu-model MODELGPU model family the bounded-reservation statement allowsH100
--regions LISTRegions the bounded-reservation statement allows, comma-separated
--max-gpus NGPUs per reservation8
--max-hours NHours per reservation, extensions included72
--feature FLAGTenant feature the reservation statement requires (default compute.<model>)
--metersAlso create the root billing meters in better-iam
--iam-url URLbetter-iam base URL for --metersBETTER_IAM_URL
--iam-base-path PATHbetter-iam's API base path/api/iam
--root-tenant IDThe root tenant that owns the meters (required with --meters)BETTER_COMPUTE_ROOT_TENANT
--gpu-models LISTGPU model families to meter, comma-separated (default: every known family)
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute iam setup --audience https://compute.example.com --query "[0]"
BETTER_IAM_TOKEN=... better-compute iam setup --audience https://compute.example.com --meters --iam-url https://iam.example.com --root-tenant ten_root

Audit

audit checkpoint

Seal committed audit events into signed per-tenant checkpoints

better-compute audit checkpoint [--pki PATH] [--limit N] [--config PATH]

audit checkpoint runs what the audit module's hourly job does: it seals every audit event committed since the last checkpoint into a signed, hash-chained Merkle checkpoint per tenant, signed with the PKI bundle's audit key (unsealed with BETTER_COMPUTE_SECRET). It repeats until nothing is left. The deployment must include auditModule() and be migrated.

FlagDescriptionDefaultEnvironment
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--limit NEvents per pass (default 50000)
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute audit checkpoint

audit verify

Verify a tenant's audit trail against its signed checkpoints

better-compute audit verify --tenant ID [--pin PIN] [--pki PATH] [--config PATH]

audit verify recomputes every checkpoint of --tenant from the audit events as they are now: signatures under the audit key's pin, the hash chain, contiguous ranges, sizes and Merkle roots. An edited, deleted or inserted event in a sealed range, a broken chain or a foreign signature is a problem; with any problem the command exits AUDIT_TAMPERED after printing the report. It only reads.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenantBETTER_COMPUTE_TENANT
--pin PINThe audit signing key pin (sha256/…); default: from the PKI bundleBETTER_COMPUTE_AUDIT_PIN
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute audit verify --tenant org_acme
better-compute audit verify --tenant org_acme --query problems

audit prove

Write an inclusion proof that one audit event is sealed

better-compute audit prove EVENT --tenant ID [--out PATH] [--config PATH]

audit prove writes a self-contained proof that the event is in a signed checkpoint: the event, its Merkle path and the signed checkpoint. Anyone holding the audit key pin checks it offline with audit check-proof, without database access.

FlagDescriptionDefaultEnvironment
--tenant IDThe tenantBETTER_COMPUTE_TENANT
--out PATHWrite the proof to a file instead of printing it
--config PATHConfiguration module; default: the nearest better-compute.config.{mjs,js,ts,mts}BETTER_COMPUTE_CONFIG
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute audit prove aud_01k… --tenant org_acme --out proof.json

audit check-proof

Check an inclusion proof offline against the audit key pin

better-compute audit check-proof FILE [--pin PIN] [--pki PATH]

audit check-proof verifies a proof written by audit prove: the checkpoint's signature under the pin and the event's Merkle path to the signed root. It needs no database; it exits INVALID_PROOF when the proof does not hold.

FlagDescriptionDefaultEnvironment
--pin PINThe audit signing key pin (sha256/…); default: from the PKI bundleBETTER_COMPUTE_AUDIT_PIN
--pki PATHThe PKI bundle written by pki init.better-compute/pki.jsonBETTER_COMPUTE_PKI
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute audit check-proof proof.json --pin sha256/…

Release

release keygen

Create an offline release signing key and print its pin

better-compute release keygen --out PATH

release keygen writes a new P-256 release signing key to --out (owner-only, never overwritten) and its public half to <out>.pub.pem, and prints the key's pin. Keep the key offline. Give the pin to installers (BCD_RELEASE_PINS) and daemons; give the .pub.pem to whoever builds manifests that you will cosign.

FlagDescriptionDefaultEnvironment
--out PATHWhere to write the key
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute release keygen --out ~/keys/release-alice.pem

release sign

Sign a release manifest over a directory's artifacts

better-compute release sign DIR --product NAME --version SEMVER --sequence N --key PATH [--cosigners PATHS] [--validity-days DAYS] [--out PATH]

release sign hashes every file in DIR (not subdirectories; *.sha256, manifest.json and signature files are skipped), derives each artifact's platform from goreleaser names (_linux_amd64.), and writes a manifest signed with --key. --sequence must increase with every release of --product: installers and bcd refuse a manifest whose sequence is below the last one they accepted. List the public halves of keys that will cosign later with --cosigners so their signatures count.

FlagDescriptionDefaultEnvironment
--product NAMEe.g. bcd
--version SEMVERe.g. 1.4.0
--sequence NThe rollback counter: higher than every earlier release
--key PATHThe signing key
--cosigners PATHSComma-separated .pub.pem files of keys that will cosign
--validity-days DAYSHow long installers accept the manifest (default 365)
--out PATHWhere to write the manifest (default DIR/manifest.json)
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute release sign dist --product bcd --version 1.4.0 --sequence 14 --key ~/keys/release-alice.pem

release cosign

Add your signature to a release manifest

better-compute release cosign MANIFEST --key PATH

release cosign adds --key's signature to MANIFEST in place. The key must be one the manifest lists (release sign --cosigners), so a threshold of release managers can each sign on their own machine.

FlagDescriptionDefaultEnvironment
--key PATHYour signing key
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute release cosign dist/manifest.json --key ~/keys/release-bob.pem

release verify

Verify a release manifest (and its artifacts) against pinned keys

better-compute release verify MANIFEST --pins PINS [--threshold N] [--min-sequence N] [--product NAME] [--dir DIR]

release verify checks MANIFEST the way installers and bcd do, in this order: a --threshold of distinct keys among --pins signed it; its sequence is not below --min-sequence (rollback); it has not expired; and, with --dir, every listed artifact present there matches its size and SHA-256. It exits RELEASE_REJECTED with the reason otherwise.

FlagDescriptionDefaultEnvironment
--pins PINSComma-separated sha256/… pins of the release keysBCD_RELEASE_PINS
--threshold NDefault 1
--min-sequence NThe last sequence accepted before
--product NAMERefuse other products
--dir DIRAlso check the artifacts in DIR
--format FORMATOutput as json (indented, default), compact (one line), or table
--query PATHPrint only part of the result, e.g. protocol.max or findings[].check (strings print raw)
better-compute release verify dist/manifest.json --pins sha256/…,sha256/… --threshold 2 --dir dist

Environment

VariableDescription
BETTER_COMPUTE_CONFIGConfiguration module, like --config
BETTER_COMPUTE_TOKENIAM session token, API key or session JWT that token commands act as
BETTER_COMPUTE_URLCompute server token commands call, like --url
BETTER_COMPUTE_DATABASE_URLWithout a configuration file: the database (postgres://, pglite:DIR, pglite:memory), with BETTER_COMPUTE_SECRET
BETTER_COMPUTE_SECRETWithout a configuration file: the deployment secret (at least 32 characters)
BETTER_COMPUTE_BASE_URLWithout a configuration file: the public origin (default http://localhost:3000)
BETTER_COMPUTE_DEBUGPrint the stack trace of a failure

Was this page helpful?

Better Compute is created by Sean Filimon

Last updated

On this page

Setupinitmigratedoctorversioncompletionservedevsimsupport bundleAPIapiAccessauth loginauth statusauth logoutssh-certconsoleexecforwardCapacityreservereleaseextendreservations listreservations getpools listcapacitySandboxessandbox createsandbox listsandbox getsandbox extendsandbox snapshotsandbox deletesandbox execsandbox-template createsandbox-template updatesandbox-template deletesandbox templatessandbox-volume createsandbox-volume listsandbox-volume getsandbox-volume resizesandbox-volume deletesandbox-snapshot listsandbox-snapshot getsandbox-snapshot deleteNodesnodes listnodes getnodes eventsnodes cordonnodes uncordonnodes drainnodes labelnodes taintnodes deletejoin-tokens createjoin-tokens listjoin-tokens revokejoin-requests listjoin-requests approvejoin-requests rejectinventory getinventory historyinventory diffsite-tokens createsite-tokens revokeInstancesinstances listinstances getinstances createinstances startinstances stopinstances deleteinstances resizeEventsevents listevents watchOperationsreleases registerreleases listrollouts createrollouts listrollouts getrollouts pauserollouts resumerollouts abortworkflows listworkflows getworkflows abortworkflows pauseworkflows resumeworkflows retrycommands listcommands getcommands cancelCatalogsites listsites getsites createsites updatesites deleteprojects listprojects getprojects createprojects archiveimages listimages getimages registerimages promoteflavors listflavors createflavors updateflavors deletequotas getquotas setUsageusage listusage exportWebhookswebhooks listwebhooks getwebhooks createwebhooks updatewebhooks deletewebhooks pingwebhooks rotate-secretwebhooks deliverieswebhooks redeliverwebhooks event-typesPoolspools getpools createpools updatepools archivepools offerpools unofferdrivers listSecretssecrets putsecrets deleteMetalmetal enrollmetal managemetal inspectmetal providemetal rescuemetal unrescuemetal recovermetal retiremetal deploymetal undeploymetal abortmetal discoveredmetal confirm-macmetal powermetal bulkprofiles listprofiles getprofiles validateprofiles renderprofiles createprofiles updateprofiles deleteprovider-accounts listprovider-accounts getprovider-accounts deleteprovider-accounts createprovider-accounts updateprovider-accounts offersprovider-accounts bootstrapFleetfleet exportfleet planfleet statusfleet applydaemon install-scriptPKIpki initpki showpki add-sandbox-capki resealpki rotate-command-rootpki trust-versionpki renew-capki issue-gatewayQuorumbatch keygenbatch signbatch sign-approversbatch listbatch showbatch approvebatch createbatch membersbatch cancelIAMiam setupAuditaudit checkpointaudit verifyaudit proveaudit check-proofReleaserelease keygenrelease signrelease cosignrelease verifyEnvironment