API Reference

The live OpenAPI specification for the FastVM public API.

API Endpoint: https://api.fastvm.org
OpenAPI YAML Specification: https://fastvm.org/openapi.decorated.yaml

Plain-text endpoint reference

Plain HTML mirror of the API reference, generated at build time from api/openapi.yaml. Same content as the interactive view above; included for screen readers, search-engine indexers, and LLM-based crawlers that can’t render the JavaScript surface. See also /llms.txt and /llms-full.txt.

VMs

VM lifecycle

GET /v1/vms

List VMs

Lists all non-deleted VMs for the authenticated org. Supports metadata-equality filtering; callers pass repeated query parameters of the form `metadata.<key>=<value>` (e.g. `metadata.env=prod&metadata.role=api`). The optional `status` query filter narrows by lifecycle status (e.g. `?status=paused`).

Auth required (X-API-Key).

Parameters

  • status (query, VMStatus) Restrict to VMs with this status. Accepts any value of `VMStatus`; unknown values return an empty list.

Responses

  • 200 VM[] List of VMs
  • 401 Error Missing or invalid credentials
  • 500 Error Internal server error

POST /v1/vms

Launch a VM

Creates a new VM, either from a machineType (fresh boot) or a snapshotId (restore from snapshot). - Returns **201** when the VM is already running in the response. - Returns **202** when the VM is queued; clients must poll `GET /v1/vms/{id}` until status transitions to `running`. Terminal failure statuses are `error` and `stopped`. The SDK's `launch()` helper handles the 201/202 branching and polling automatically.

Auth required (X-API-Key).

Request body: CreateVMRequest

Responses

  • 201 VMCreateResponse VM is already running. The response is a VM object, with two optional warning fields surfacing non-fatal failures: - `snapshotRestoreWarnings`: pre-registered services from the snapshot failed to land on the new VM. The VM itself is good; the user can re-register the listed services manually. - `attachmentWarnings`: one or more inline `volumes` / `bucketMounts` entries failed to attach during the synchronous create handshake. The VM is **not rolled back** — it boots without the failed mounts. The `failedVolumeAttachments` / `failedBucketMountAttachments` arrays carry the per-attachment `statusMessage`; callers that require all-or-nothing semantics must inspect the warning field and decide whether to delete the VM and retry, or call `POST /v1/vms/{id}/{volumes,bucket-mounts}` to retry the failed entries individually.
  • 202 VM VM is queued; poll for readiness
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 403 Error Org quota exceeded
  • 404 Error Snapshot or base image not found
  • 500 Error Internal server error
  • 502 Error Upstream service error
  • 503 Error Service temporarily unavailable

GET /v1/vms/{id}

Get a VM

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Responses

  • 200 VM VM
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 500 Error Internal server error

PATCH /v1/vms/{id}

Update a VM

Renames a VM and/or replaces its metadata map. At least one of `name` or `metadata` must be provided. Sending `metadata: {}` clears all metadata; omitting `metadata` leaves it unchanged.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Request body: UpdateVMRequest

Responses

  • 200 VM Updated VM
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 500 Error Internal server error

DELETE /v1/vms/{id}

Delete a VM

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Responses

  • 200 DeleteResponse VM deletion result
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 500 Error Internal server error
  • 502 Error Upstream service error

POST /v1/vms/{id}/pause

Pause a VM

Captures the VM state, frees the worker and all customer-facing quotas, and transitions the VM to `paused`. Idempotent on already-paused VMs (returns 200 with the current state). Synchronous; ~3 s end-to-end.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Responses

  • 200 VM Paused VM (or already paused — idempotent).
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error VM in a state that cannot be paused.
  • 500 Error Internal server error
  • 502 Error Upstream service error

POST /v1/vms/{id}/resume

Resume a paused VM

Restores the VM's prior state, re-acquires quota, and transitions to `running`. Sync-when-fast / async-when-queued: returns 200 if the VM is running inline, or 202 if queued for cluster capacity. Idempotent on already-running.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Responses

  • 200 VM VM running (resumed inline).
  • 202 VM Resume queued for capacity. VM is in `resuming`; poll `GET /v1/vms/{id}` or wait for the `vm.resumed` webhook.
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error VM not in a state that can be resumed.
  • 429 QuotaExceeded Org quota exceeded; body indicates the dimension.
  • 500 Error Internal server error
  • 502 Error Upstream service error

POST /v1/vms/{id}/ttl/refresh

Reset the VM's TTL cycle

Resets the TTL countdown to a fresh `seconds` budget. From `running`, the deadline moves to `now + seconds*1000`. From `paused`, the remaining-budget is reset to `seconds*1000` and takes effect on next resume. 409 if no TTL is configured.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Responses

  • 200 VM Updated VM with reset TTL cycle.
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error VM has no TTL configured, OR is in a transitional state.
  • 500 Error Internal server error

Snapshots

Snapshot lifecycle

GET /v1/snapshots

List snapshots

Lists all snapshots for the authenticated org. Supports metadata-equality filtering; callers pass repeated query parameters of the form `metadata.<key>=<value>` (e.g. `metadata.env=prod&metadata.role=api`).

Auth required (X-API-Key).

Responses

  • 200 Snapshot[] List of snapshots
  • 401 Error Missing or invalid credentials
  • 500 Error Internal server error

POST /v1/snapshots

Create a snapshot from a VM

Captures a VM's state into a customer-visible snapshot. Supported on `running` and `paused` VMs; returns 201 Created with the new snapshot in both cases. On a paused VM, repeated calls within the same pause cycle are idempotent: the second call returns the same snapshot record without modification.

Auth required (X-API-Key).

Request body: CreateSnapshotRequest

Responses

  • 201 Snapshot Snapshot created
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 403 Error Snapshot quota exceeded
  • 404 Error Source VM not found
  • 409 Error Source VM is in a non-snapshottable state (provisioning, pausing, resuming, error, deleting), or the paused VM already has a snapshot with a different name.
  • 500 Error Internal server error
  • 502 Error Upstream service error

GET /v1/snapshots/{id}

Get a snapshot

Returns the full Snapshot record for the given ID, scoped to the authenticated org. Used by the SDK's `build()` flow to fetch the completed snapshot after polling reports `completed`.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) Snapshot ID (UUID).

Responses

  • 200 Snapshot Snapshot record
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 500 Error Internal server error

PATCH /v1/snapshots/{id}

Rename a snapshot

Auth required (X-API-Key).

Parameters

  • id (path, string, required) Snapshot ID (UUID).

Request body: UpdateSnapshotRequest

Responses

  • 200 Snapshot Updated snapshot
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 500 Error Internal server error

DELETE /v1/snapshots/{id}

Delete a snapshot

Auth required (X-API-Key).

Parameters

  • id (path, string, required) Snapshot ID (UUID).

Responses

  • 200 DeleteResponse Snapshot deletion result
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 500 Error Internal server error
  • 502 Error Upstream service error

Firewall

VM firewall policy

PUT /v1/vms/{id}/firewall

Replace firewall policy

Replaces the full firewall policy on a VM.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Request body: FirewallPolicy

Responses

  • 200 VM Updated VM
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 500 Error Internal server error
  • 502 Error Upstream service error

PATCH /v1/vms/{id}/firewall

Patch firewall policy

Updates one or more blocks of the firewall policy. Each top-level block (`ingress`, `egress`, `dns`) is optional; when present, the supplied object **replaces that block wholesale**. Per-rule diffing is not supported — to change a single rule, send the full block with the desired rule list. An empty body (`{}`) is a no-op. Examples: - `{"ingress": {"default": "deny", "rules": []}}` clears all ingress rules and sets the default action. - `{"dns": {"mode": "allow", "domains": ["api.example.com"], "blockBypass": true}}` updates only the DNS block.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Request body: PatchFirewallRequest

Responses

  • 200 VM Updated VM
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 500 Error Internal server error
  • 502 Error Upstream service error

Exec

In-VM command execution

POST /v1/vms/{id}/exec

Execute a command inside a VM

Runs `command` inside the VM. Response shape is determined by the client's `Accept` header: - **`Accept: application/json`** (default, omitted, or `*/*`): buffered `ExecVMResponse` — the server collects all output and returns a single JSON object once the command exits. Per-stream output is capped at 4 MiB; overflow bytes are dropped and signalled via `stdoutTruncated` / `stderrTruncated`. - **`Accept: application/x-ndjson`**: newline-delimited stream of `ExecEvent`s — zero or more `stdout`/`stderr` chunks followed by exactly one terminal `exit` event. Use this for incremental output (long builds, test runners, live logs). No server-side cap. Both modes share the same request body. `timeoutSec` bounds server-side execution; clients should set their own HTTP timeout in addition. 502 responses are transient (the upstream VM host is unreachable or returned an error). The SDK's `run()` helper does NOT auto-retry these by default: exec is **not idempotent**, so if a 502 hides a successful exec a retry may run the command twice. Callers opt in with `max_retries=N` per call.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Request body: ExecVMRequest

Responses

  • 200 ExecVMResponse Command completed. `application/json` (default) returns a single `ExecVMResponse`; `application/x-ndjson` returns an event stream terminated by one `exit` event.
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error VM is not running
  • 500 Error Internal server error
  • 502 Error Upstream VM host unreachable or returned an error. Not retried by default (non-idempotent).

Console

Interactive serial console access

POST /v1/vms/{id}/console-token

Mint a console token

Returns a short-lived token and WebSocket path. Open a WebSocket to `wss://<host><websocketPath>?session=<token>` to attach to the VM's serial console. The WebSocket endpoint itself is intentionally not modeled in this spec because it uses a capability-URL flow (no API key on upgrade) and a custom binary/text protocol. See `src/fastvm/lib/console.py` in the Python SDK for a reference client.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Responses

  • 200 ConsoleTokenResponse Console token
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error VM is not running
  • 500 Error Internal server error

ssh

Per-user authorized SSH key management. Register one pubkey on your user, then `ssh <vmId>@<org-slug>.ssh.<stack-domain>` works for every VM in every org you're a member of. The per-stack SSH gateway uses CA-signed user certificates internally — VMs trust the CA, not your raw pubkey, so the customer-facing pubkey lives only at the gateway.

GET /v1/me/ssh-keys

List the calling user's authorized SSH keys

Returns every SSH public key registered to the calling user. Keys are personal: registering a key once authorizes `ssh <vmId>@ssh.<domain>` for any VM in any org you are a member of. SSH terminates at the per-stack gateway and is forwarded to the VM over the cluster network; the VM does not need to be publicly IPv4-reachable, and you do not need to know the VM's IPv6 address.

Auth required (X-API-Key).

Responses

  • 200 SshKeyListResponse Authorized keys list
  • 401 Error Missing or invalid credentials
  • 500 Error Internal server error

POST /v1/me/ssh-keys

Register an SSH public key for the calling user

Adds one authorized SSH public key to the calling user. The fingerprint is derived server-side and returned. Duplicate fingerprints return 409. Up to 32 keys per user. After this call, `ssh <vmId>@ssh.<domain>` works for any VM you have access to. Each fingerprint is globally unique: registering a public key that another user already has on file returns 409.

Auth required (X-API-Key).

Request body: AddSshKeyRequest

Responses

  • 201 SshKey Key registered
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 409 Error Key with this fingerprint is already registered (by this user or another user)
  • 500 Error Internal server error

DELETE /v1/me/ssh-keys/{fingerprint}

Remove an authorized SSH key

Deletes one of the calling user's keys by fingerprint. Existing SSH sessions are NOT terminated — the key simply won't authorize new connections after removal.

Auth required (X-API-Key).

Parameters

  • fingerprint (path, string, required) OpenSSH SHA256 fingerprint of the key to delete (e.g. `SHA256:abc...`). The base64 hash includes `+` and `/` and the prefix has `:`, so callers MUST URL-encode the value into the path segment. SDKs do this automatically.

Responses

  • 200 DeleteResponse Key removed
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 500 Error Internal server error

Files

File upload/download to/from a running VM

POST /v1/vms/{id}/files/presign

Mint signed URLs for uploading a file to a VM

Returns a pair of short-lived signed URLs targeting a per-VM staging location. Upload to `uploadUrl` with PUT (`Content-Type: application/octet-stream`), then pass `downloadUrl` to `POST /v1/vms/{id}/files/fetch` to have the server pull it into the guest filesystem.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Request body: FilePresignRequest

Responses

  • 200 FilePresignResponse Signed URLs + upload size ceiling
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error VM is not running
  • 500 Error Internal server error
  • 501 Error File transfer not configured on this deployment

POST /v1/vms/{id}/files/fetch

Fetch a file into a VM from a presigned URL

Pulls `url` into the guest at `path`. `url` must be a presigned storage URL previously minted by `POST /v1/vms/{id}/files/presign` (URLs from other sources are rejected). Response mirrors `/v1/vms/{id}/exec`: reports stdout/stderr/exit code of the underlying download+unpack operation. Not idempotent; not retried by default.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Request body: FileFetchRequest

Responses

  • 200 ExecVMResponse Fetch completed (command result)
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error VM is not running
  • 413 Error Object too large for VM disk / insufficient guest disk space
  • 500 Error Internal server error
  • 502 Error Failed to HEAD the presigned URL

Quotas

Org quotas and usage

GET /v1/org/quotas

Get org quotas and usage

Auth required (X-API-Key).

Responses

  • 200 OrgQuotaUsage Quota limits and current usage
  • 401 Error Missing or invalid credentials
  • 500 Error Internal server error

Builds

Build snapshots from a Docker image ref or Dockerfile

POST /v1/builds

Build a snapshot from an image ref or Dockerfile

Submits an asynchronous build. The scheduler creates a build VM, runs `buildah pull` (image-only path) or `buildah bud` (Dockerfile path) inside it, snapshots the result, and tears the VM down. At least one of `imageRef` or `dockerfileContent` must be provided. If `dockerfileContent` is set, the worker writes it verbatim into `/tmp/buildctx/Dockerfile` — buildah handles multi-stage, `SHELL`, `RUN --mount`, etc. natively. For `COPY` instructions that need files, upload the build context first via `POST /v1/build-contexts/presign` and pass the returned download URL as `contextDownloadUrl`. Response is `202 Accepted` with a build ID; poll `GET /v1/builds/{id}` until `status` is `completed` or `failed`.

Auth required (X-API-Key).

Request body: CreateBuildRequest

Responses

  • 202 BuildResponse Build accepted; poll `GET /v1/builds/{id}` for status
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 403 Error Org quota exceeded
  • 413 Error `contextDownloadUrl` references an object larger than the server-side cap (1 GiB). The cap is also returned as `maxUploadBytes` from `POST /v1/build-contexts/presign`, so the SDK can advise users before upload — this 413 is the authoritative server-side enforcement.
  • 500 Error Internal server error
  • 501 Error File staging is not configured on this cluster (no `FILE_STAGING_BUCKET`). Returned only when `contextDownloadUrl` is supplied — builds without a context work fine on staging-disabled clusters.

GET /v1/builds/{id}

Get build status

Returns the current state of a build. While the build is in progress, `status` is `pending` or `running` and `progress` contains a human-readable string describing the current phase (e.g. `Pulling image`, `Building (3 steps)`, `Settling VM`). On success, `status` is `completed` and `snapshotId` references a `ready` snapshot — fetch it via `GET /v1/snapshots/{id}`. On failure, `status` is `failed` and `error` carries the worker's diagnostic.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) Build ID (UUID).

Responses

  • 200 BuildResponse Build status
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 500 Error Internal server error

POST /v1/build-contexts/presign

Mint signed URLs for uploading a build context tarball

Returns a pair of short-lived signed URLs targeting a per-org staging location. Tar+gzip your build-context directory, PUT it to `uploadUrl` with `Content-Type: application/gzip`, then pass `downloadUrl` as `contextDownloadUrl` on `POST /v1/builds`. Unlike `/v1/vms/{id}/files/presign`, this endpoint isn't keyed to a specific VM — context uploads happen *before* the build VM exists.

Auth required (X-API-Key).

Responses

  • 200 FilePresignResponse Signed URLs + upload size ceiling
  • 401 Error Missing or invalid credentials
  • 500 Error Internal server error
  • 501 Error File staging is not configured on this deployment

VM Services

Per-VM service registrations exposed via the public 4to6 HTTP proxy

GET /v1/vms/{id}/services

List service registrations

Returns the services currently registered on this VM, sorted by name. Each service is exposed at `https://<name>--<vmIdHexNoHyphens>.proxy.<stack-domain>` over HTTPS.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Responses

  • 200 Service[] Services
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 500 Error Internal server error

POST /v1/vms/{id}/services

Register a service on a VM

Registers an HTTP service on the VM under `name`, listening on `port`. The service immediately becomes addressable at `https://<name>--<vmIdHexNoHyphens>.proxy.<stack-domain>` once the firewall is applied (synchronous). Idempotent: a POST with a name that already exists at the same `(port, h2c)` returns 201 with the existing entry. POST with a name that already exists at a different port OR different `h2c` returns 409 — use PUT to update an existing service. Per-VM cap: currently 16 services per VM (configurable via `MAX_SERVICES_PER_VM` on the scheduler).

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Request body: RegisterServiceRequest

Responses

  • 201 Service Service registered (or idempotent same-`(port, h2c)` re-register)
  • 400 any Invalid name, invalid port, or per-VM cap exceeded. The body is a `QuotaExceededError` for the cap case (carries the structured `vm_service_quota_exceeded` reason + numeric count) and an `Error` otherwise.
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error The service name is already registered at a different port or different `h2c` (use PUT to update), or the VM is in `error` state and cannot be modified.
  • 500 Error Internal server error

PUT /v1/vms/{id}/services/{serviceName}

Register or update a service on a VM

Idempotent register-or-update: same name + new port updates the port; same name + same port is a no-op. Returns the resulting entry. Used to change the upstream port for an existing service registration without dropping and re-creating it.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).
  • serviceName (path, string, required) Service registration name. 1–29 chars, lowercase letters and digits with optional single internal hyphens (no leading, trailing, or consecutive hyphens). Embedded in the public URL as the leftmost label.

Request body: UpdateServiceRequest

Responses

  • 200 Service Service updated (or no-op same-port re-issue)
  • 400 any Invalid name or port, or per-VM cap exceeded. The body is a `QuotaExceededError` for the cap case and an `Error` otherwise.
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error VM is in `error` state and cannot be modified
  • 500 Error Internal server error

DELETE /v1/vms/{id}/services/{serviceName}

Deregister a service from a VM

Idempotent: deleting a service that doesn't exist returns 204. Removes the firewall auto-rule synchronously; the proxy stops routing to the service within seconds (cache invalidation broadcast; 30s TTL is the safety net).

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).
  • serviceName (path, string, required) Service registration name. 1–29 chars, lowercase letters and digits with optional single internal hyphens (no leading, trailing, or consecutive hyphens). Embedded in the public URL as the leftmost label.

Responses

  • 204 Service deregistered (or already absent)
  • 400 Error Invalid service name
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error VM is in `error` state and cannot be modified
  • 500 Error Internal server error

Health

Service health

GET /healthz

Health check

Returns 200 when the API is reachable. SDK clients call this on startup to warm HTTP/2 connections before the first real request.

Responses

  • 200 object Service is healthy
  • 500 Error Internal server error

Volumes

Managed shared-volume lifecycle (POSIX-coherent multi-attach via virtio-fs).

GET /v1/volumes

List volumes

Auth required (X-API-Key).

Responses

  • 200 Volume[] List of volumes
  • 401 Error Missing or invalid credentials
  • 500 Error Internal server error

POST /v1/volumes

Create a managed volume

Auth required (X-API-Key).

Request body: CreateVolumeRequest

Responses

  • 201 Volume Volume created
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 403 Error Org quota exceeded
  • 409 Error Name collision
  • 500 Error Internal server error
  • 502 Error Upstream service error
  • 503 Error Service temporarily unavailable

GET /v1/volumes/{id}

Get a volume

Auth required (X-API-Key).

Parameters

  • id (path, string, required) Volume identifier (e.g. `vol_<22-char-lowercase-hex>`).

Responses

  • 200 Volume Volume
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found

PATCH /v1/volumes/{id}

Update a volume's name, sizeGiB (grow / shrink-if-not-overfull), or accessMode

Auth required (X-API-Key).

Parameters

  • id (path, string, required) Volume identifier (e.g. `vol_<22-char-lowercase-hex>`).

Request body: UpdateVolumeRequest

Responses

  • 200 Volume Updated volume
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 403 Error Org quota exceeded (grow above limit)
  • 404 Error Resource not found
  • 409 Error Conflict (volume_overfull_for_shrink, volume_in_use_by_attached_vms)
  • 502 Error Upstream service error

DELETE /v1/volumes/{id}

Delete a volume

Returns 200 when the volume transitions to `deleting`. Substrate cleanup is asynchronous; the volume disappears from `GET` after the substrate-cleanup controller completes (typically seconds to minutes for large volumes).

Auth required (X-API-Key).

Parameters

  • id (path, string, required) Volume identifier (e.g. `vol_<22-char-lowercase-hex>`).

Responses

  • 200 DeleteResponse Volume marked for deletion
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 object Volume currently mounted on running VMs
  • 500 Error Internal server error

GET /v1/volumes/{id}/attachments

List VMs currently attached to this volume

Auth required (X-API-Key).

Parameters

  • id (path, string, required) Volume identifier (e.g. `vol_<22-char-lowercase-hex>`).

Responses

  • 200 VolumeAttachmentItemWithVm[] List of attachments
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found

POST /v1/vms/{id}/volumes

Attach a volume to a VM

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Request body: VolumeAttachmentRequest

Responses

  • 201 VolumeAttachmentItem Volume attached
  • 400 Error Invalid request
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error VM in invalid state, VM in transitional state (`error: vm_transitioning`; retry-able with `Retry-After`), volume not ready, mount-path collision, duplicate-volume, or already-attached. The body's `error` token distinguishes.
  • 502 Error Upstream service error
  • 503 Error Service temporarily unavailable

DELETE /v1/vms/{id}/volumes/{volumeId}

Detach a volume from a VM

Returns 200 with `{detached: true}` on the clean path. May include a `warnings` array on the force-teardown path (eject-ack timeout or guest-unresponsive). Returns 502 with `error: guest_umount_busy` when the guest reports EBUSY; the volume STAYS ATTACHED in this case. Resolve by killing in-VM users of the mount and retrying.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).
  • volumeId (path, string, required)

Responses

  • 200 DetachVolumeResponse Detached (with optional warnings)
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error VM in transitional state (`error: vm_transitioning`; honor `Retry-After`) or terminal state (`error: vm_invalid_state`).
  • 502 Error Upstream service error
  • 503 Error Service temporarily unavailable

Bucket Mounts

BYO GCS/S3 bucket mounts as a VM sub-resource.

GET /v1/vms/{id}/bucket-mounts

List bucket-mounts on a VM

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Responses

  • 200 BucketMount[] List
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found

POST /v1/vms/{id}/bucket-mounts

Attach a customer GCS / S3 bucket to a VM

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).

Request body: CreateBucketMountRequest

Responses

  • 201 BucketMount BucketMount created and mounted
  • 400 Error Validation failure (bad URI, malformed credentials, bucket-not-found)
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error Path collision, VM invalid state, VM in transitional state (`error: vm_transitioning`; retry-able with `Retry-After`), or per-VM cap exceeded. The body's `error` token distinguishes.
  • 502 Error Upstream service error
  • 503 Error Service temporarily unavailable

GET /v1/vms/{id}/bucket-mounts/{bucketMountId}

Get a bucket-mount

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).
  • bucketMountId (path, string, required) BucketMount identifier (e.g. `bm_<22-char-lowercase-hex>`), unique per VM.

Responses

  • 200 BucketMount BucketMount
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found

PATCH /v1/vms/{id}/bucket-mounts/{bucketMountId}

Rotate bucket-mount credentials in-place

Replaces the stored credentials and re-authenticates the FUSE mount on the worker. Brief I/O blip (~50-200 ms typical) during the swap. Returns 502 on the rollback path; flips `mountStatus` to `failed` on full failure.

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).
  • bucketMountId (path, string, required) BucketMount identifier (e.g. `bm_<22-char-lowercase-hex>`), unique per VM.

Request body: UpdateBucketMountRequest

Responses

  • 200 BucketMount Rotation succeeded
  • 400 Error Credentials invalid
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error Concurrent rotation in flight (`error: concurrent_rotation`). `Retry-After` indicates safe wait.
  • 502 Error Rotation rolled back or failed
  • 503 Error Worker/Redis split-brain (`error: worker_state_inconsistent`): the worker has no record of this BM with no rotation in flight (e.g., post worker-restart). The controller reaper reconciles at lease expiry; clients should NOT retry.

DELETE /v1/vms/{id}/bucket-mounts/{bucketMountId}

Detach and delete a bucket-mount

Auth required (X-API-Key).

Parameters

  • id (path, string, required) VM ID (UUID).
  • bucketMountId (path, string, required) BucketMount identifier (e.g. `bm_<22-char-lowercase-hex>`), unique per VM.

Responses

  • 204 Detached and deleted
  • 401 Error Missing or invalid credentials
  • 404 Error Resource not found
  • 409 Error Conflict. Either a concurrent rotation owns the worker-side slot (`error: concurrent_rotation`) or the VM is in a transitional state (`error: vm_transitioning`). Both are retry-able; honor `Retry-After`.
  • 500 Error Internal server error
  • 502 Error Guest reported EBUSY on umount. The BucketMount STAYS attached.
  • 503 Error Worker unreachable, OR worker/Redis split-brain (`error: worker_state_inconsistent`). In the split-brain case, the controller reaper reconciles at lease expiry; clients should NOT retry.

Schemas

Error

  • error (string, required) Human-readable error message.

QuotaExceededError

Per-VM service quota exceeded. The `error` token is a stable machine-readable code so SDKs can branch on it; `count` is the configured cap at denial time.

  • error ("vm_service_quota_exceeded", required)
  • count (integer, required)

DeleteResponse

  • id (string, required)
  • deleted (boolean, required)

VMStatus

Lifecycle status. Known values: `provisioning`, `running`, `stopped`, `pausing`, `paused`, `resuming`, `deleting`, `error`. Terminal failure statuses are `error` and `stopped`; transitional values (`provisioning`, `pausing`, `resuming`, `deleting`) indicate the VM is in flight. Additional values may be introduced in future server versions; clients should treat unknown values as "in transition" rather than as hard errors.

SnapshotStatus

Snapshot lifecycle status. Known values: `creating`, `ready`, `error`. Additional values may be introduced in future server versions.

TTL

Per-VM auto-action timer. The cycle ticks down while the VM is `running` and freezes on pause. `seconds` is the original cycle duration; refresh and PATCH-time updates reset to this value.

  • seconds (integer, required) Cycle duration. Refresh resets to this value. Capped at 1 year (31536000s); larger values are rejected with 400.
  • action ("pause" | "delete", required) Action taken on expiry. `pause` re-arms the cycle for the next running session; `delete` is terminal.

QuotaExceeded

429 body returned by `/v1/vms/{id}/resume` when the org's quota for one of the listed dimensions would be exceeded.

  • error (string, required)
  • dimension ("vcpu" | "memory_mib" | "disk_gib" | "snapshot_count", required)

MachineType

Machine size identifier (e.g. `c1m2`, `c2m4`). Controls CPU and memory allocation. Must be supplied on launch unless restoring from a snapshot.

VM

  • id (string, required)
  • name (string, required)
  • orgId (string, required)
  • machineName (string)
  • sourceName (string) Source snapshot or image name (empty on fresh boot).
  • firewall (FirewallPolicy)
  • effectiveFirewall (any) Read-only composed view: `firewall` (the user policy) unioned with per-service auto-rules from this VM's registered services. Each auto-rule has source CIDR `::/0` and a `description` of the form `auto: proxy service <name>`. The same policy is what the worker firewall actually enforces. Set `firewall` to mutate; this field is computed per-response from `firewall` and the current service registry, never persisted.
  • metadata (Metadata)
  • envVars (EnvVars)
  • publicIpv6 (string)
  • cpu (integer, required)
  • memoryMiB (integer, required)
  • diskGiB (integer, required)
  • status (VMStatus, required)
  • createdAt (string, required)
  • deletedAt (string)
  • ttl (any) Optional auto-action timer. Null when no TTL is configured. See `TTL` for semantics.
  • expiresAtMs (integer) Absolute timestamp in ms when the TTL fires. Set only while the VM is `running` (the countdown freezes on pause).
  • ttlRemainingMs (integer) Remaining cycle budget in ms. Set only while the VM is paused; restored to `expiresAtMs` on resume.
  • pausedAt (string) When the VM became paused; null otherwise.
  • volumes (VolumeAttachmentItem[]) Currently-attached volumes on this VM.
  • bucketMounts (BucketMount[]) Currently-attached bucket-mounts on this VM.

Snapshot

  • id (string, required)
  • name (string, required)
  • orgId (string, required)
  • vmId (string, required)
  • firewall (FirewallPolicy)
  • metadata (Metadata)
  • envVars (EnvVars)
  • services (SnapshotService[]) Captured service registrations from the source VM at snapshot time.
  • volumes (SnapshotVolumeAttachment[]) Volume attachments captured at snapshot time.
  • bucketMounts (SnapshotBucketMountAttachment[]) BucketMount metadata captured at snapshot time (no credentials).
  • status (SnapshotStatus, required)
  • createdAt (string, required)

PolicyAction

Allow/deny verb. Used both as the per-direction default posture and as each rule's action.

IngressRuleKind

Ingress rule kind. Only `cidr` is supported — inbound packets don't carry a domain the worker could match on without TLS interception.

EgressRuleKind

Egress rule kind. - `cidr`: match by destination IP/CIDR + port/proto. - `fqdn`: match by destination domain (resolved through the in-process DNS resolver) + port/proto. Resolved IPs land in a per-rule dynamic nft set; the chain emits one rule per fqdn rule keyed on (set, proto, port). Port/proto enforcement on fqdn rules is honest — the prior `kind: domain` shape with a shared allow-set silently ignored them. Fqdn values accept an optional leading `*.` wildcard (e.g. `*.example.com`). Bare wildcards and non-leading wildcards are rejected. Wildcards match one-or-more labels left of the suffix and do not match the apex (matches DNS wildcard semantics).

DNSMode

Toggles the meaning of `dns.domains`. - `allow`: allowlist — only listed domains can resolve; any other query returns NXDOMAIN. - `deny`: blocklist — listed domains return NXDOMAIN; all other queries resolve through the upstream resolver. Default is `deny` with an empty list, which means "resolve everything" — the safe default that preserves existing behavior when callers omit the `dns` block.

IngressRule

  • action (PolicyAction, required)
  • kind (IngressRuleKind, required)
  • value (string, required) CIDR (e.g. `::/0`, `10.0.0.0/8`). IPv4 and IPv6 CIDRs are both accepted in the schema; L3 enforcement coverage per family is a worker-side concern.
  • protocol ("tcp" | "udp" | "any", required)
  • ports (string, required) Single port (`443`), inclusive range (`8080-8090`), or `any`. When `protocol` is `any`, `ports` MUST be `any`.
  • description (string)

IngressPolicy

  • default (PolicyAction, required)
  • rules (IngressRule[])

EgressRule

  • action (PolicyAction, required)
  • kind (EgressRuleKind, required)
  • value (string, required) For `kind: cidr`, an IPv4 or IPv6 CIDR. For `kind: fqdn`, a domain name with optional leading `*.` wildcard. Must be reachable through the `dns` gate — a fqdn value blocked by `dns.mode`/`dns.domains` is rejected at PUT time as a dead rule.
  • protocol ("tcp" | "udp" | "any", required)
  • ports (string, required) Single port (`443`), inclusive range (`8080-8090`), or `any`. When `protocol` is `any`, `ports` MUST be `any`.
  • description (string)

EgressPolicy

  • default (PolicyAction, required)
  • rules (EgressRule[])

DNSPolicy

DNS-layer filtering, independent of egress L4 rules. The resolver applies the DNS gate BEFORE L4 enforcement; a domain blocked here returns NXDOMAIN regardless of what egress.rules says about its IPs. All fields are optional — the server defaults `mode` to `deny` when missing, `domains` to `[]`, and `blockBypass` to false (see `normalizeDNSPolicy` in `scheduler/internal/httpapi/firewall.go`).

  • mode (DNSMode)
  • domains (string[])
  • blockBypass (boolean) When true, the worker denies DoT (TCP 853) and the known public DoH endpoint IPs at the nft layer so guests cannot sidestep the in-process resolver. Default `false` — turning this on breaks workloads that legitimately reach `1.1.1.1` / `8.8.8.8` / etc. on TCP/443 for non-DoH reasons (e.g. services whose data plane lives on a Cloudflare anycast IP). Operators who enable DNS allowlist mode typically also flip this on explicitly.

FirewallPolicy

Top-level firewall policy with three independent axes. All sub-blocks are optional — the server substitutes the safe default (ingress deny / egress allow / dns mode=deny + empty) for missing blocks. Sending `firewall: null` on VM create is also valid.

  • ingress (IngressPolicy)
  • egress (EgressPolicy)
  • dns (DNSPolicy)

PatchFirewallRequest

Partial firewall update. Each block (`ingress`, `egress`, `dns`) is optional; when present, the supplied object replaces that block wholesale. To change a single rule, send the full block with the desired rule list. An empty body (`{}`) is a no-op.

  • ingress (IngressPolicy)
  • egress (EgressPolicy)
  • dns (DNSPolicy)

SnapshotService

Captured (name, port, h2c) tuple for a single service registration on a snapshotted VM. Carried across snapshot/ restore by `POST /v1/vms` (snapshot-restore branch) so the new VM gets the same service registrations the source VM had at snapshot time.

  • name (string, required)
  • port (integer, required)
  • h2c (boolean)

SnapshotRestoreWarnings

Reports best-effort failures during the snapshot-restore service-replay step. Only present when restoring from a snapshot AND the post-create bulk service registration failed. The VM is created successfully and usable; the user can manually re-register the listed services with one `POST /v1/vms/{id}/services` per service. Bulk service registration is atomic at Redis (one Lua call either writes all-N entries or zero), so partial state ("5 of 8 registered") is impossible — the response is always either a VM with all services registered or a VM with zero services and the full list returned here.

  • servicesRegistrationFailed (boolean, required) Always `true` when this object is present.
  • unregisteredServices (SnapshotService[]) Services from the snapshot that did not land on the new VM. Caller can re-register each via `POST /v1/vms/{id}/services`.
  • reason (string) Operator-facing diagnostic for the failure.

VMCreateResponse

VM object as returned by `POST /v1/vms`. On snapshot restore, an optional `snapshotRestoreWarnings` field may be present if the captured services failed to re-register on the new VM. Existing SDK callers that don't know about the field see the unchanged VM wire shape (`omitempty` keeps the field absent on cold boots and on warning-free restores).

CreateVMRequest

Boot behavior depends on which fields are set: - `snapshotId` set → restore from snapshot (takes precedence over `machineType` if both are sent). - Otherwise → fresh boot. `machineType` selects the size; if omitted or empty, defaults to `c1m2`.

  • name (string) User-facing name (trimmed + whitespace-collapsed, max 64 runes after normalization; longer values are truncated server-side). Auto-generated as `vm-<8-char-id-prefix>` if empty.
  • machineType (MachineType)
  • snapshotId (string) Snapshot ID to restore from.
  • diskGiB (integer) Override the default disk size (GiB).
  • firewall (FirewallPolicy)
  • metadata (Metadata)
  • envVars (EnvVars)
  • ttl (TTL)
  • volumes (VolumeAttachmentRequest[]) Cold-boot inline volume attachments (managed Volume IDs). On snapshot restore, this list authoritatively replaces the captured list. Omit to use the captured list.
  • bucketMounts (CreateBucketMountRequest[]) Cold-boot inline bucket-mounts. Same authoritative-replace semantics on snapshot restore.

UpdateVMRequest

At least one of `name`, `metadata`, or `ttl` must be provided. Sending `metadata: {}` clears all metadata; omitting it leaves existing metadata unchanged. Sending `ttl: null` explicitly clears the TTL; sending a `TTL` object replaces it; omitting the field leaves the current TTL unchanged.

  • name (string)
  • metadata (Metadata)
  • ttl (any)

CreateSnapshotRequest

  • vmId (string, required)
  • name (string) Snapshot name (trimmed + whitespace-collapsed, max 64 runes; longer values are truncated server-side). Auto-generated as `snapshot-<8-char-vmId-prefix>` if empty.

UpdateSnapshotRequest

Rename a snapshot. `name` is optional; if omitted or empty, the server regenerates the auto-name (`snapshot-<8-char-vmId-prefix>`).

  • name (string)

CreateBuildRequest

At least one of `imageRef` or `dockerfileContent` must be set. If only `imageRef` is provided, the build VM pulls that image and rsyncs its rootfs over the VM's `/`. If `dockerfileContent` is provided, the build VM writes it verbatim to `/tmp/buildctx/Dockerfile` and runs `buildah bud`.

  • name (string) Optional human-readable name for the resulting snapshot. If omitted, the build ID is used.
  • imageRef (string) Docker image reference (e.g. `python:3.13-slim`, `ghcr.io/user/repo:tag`). Used directly on the no-Dockerfile path, and as a fallback `FROM` source otherwise.
  • dockerfileContent (string) Raw Dockerfile content to feed to `buildah bud` inside the build VM. Multi-stage, `SHELL`, `RUN --mount`, and every standard Dockerfile feature is supported (handled natively by buildah). Container-runtime metadata (`CMD`, `ENTRYPOINT`, `EXPOSE`, `LABEL`, `HEALTHCHECK`) is consumed by buildah but does not surface on the resulting FastVM snapshot — when the snapshot boots, systemd takes over, not the container's CMD.
  • machineType (MachineType)
  • diskGiB (integer) Disk size for the build VM. Defaults to 10 GiB if omitted.
  • contextDownloadUrl (string) Presigned GET URL for a `tar.gz` of the build context. The worker downloads and extracts this into `/tmp/buildctx` before invoking buildah, so `COPY` instructions resolve against the user's files. Obtain via `POST /v1/build-contexts/presign`.

BuildResponse

Build state snapshot. Returned by `POST /v1/builds` (initial `pending` state) and `GET /v1/builds/{id}` (current state on each poll).

  • id (string, required) Build ID (UUID). Use this to poll status.
  • name (string)
  • status (string, required) Current state. Known values: `pending` (accepted, not yet started), `running` (worker is executing), `completed` (snapshot is ready), `failed` (build did not produce a snapshot). Additional values may be introduced in future server versions; clients should treat unknown values as "in progress" rather than as hard errors.
  • snapshotId (string) Set when `status` is `completed`. Fetch the corresponding Snapshot record via `GET /v1/snapshots/{id}`.
  • imageRef (string, required)
  • progress (string) Human-readable phase string while the build runs (e.g. `creating build VM`, `buildah pull`, `buildah bud`, `applying image`, `settling VM`, `creating snapshot`). Not present after a terminal status.
  • error (string) Set when `status` is `failed`. Diagnostic from the worker (truncated to ~4 KiB).
  • createdAt (string, required)

Metadata

Free-form string→string map. Server-enforced limits: up to 256 keys, key length 1–256 bytes, value length ≤4096 bytes, total JSON encoding ≤65536 bytes.

EnvVars

Environment variable string→string map injected into the VM at boot. Keys must be 1–256 bytes and match shell-variable name (`[A-Za-z_][A-Za-z0-9_]*`); values may not contain newline, carriage return, or null bytes. Total JSON encoding ≤65536 bytes.

ExecVMRequest

  • command (string[], required) Argv-style command. First element must be non-empty. For shell strings, wrap as `["sh", "-c", "<string>"]`.
  • timeoutSec (integer) Server-side execution timeout in seconds. Must be positive when provided; omit to use the server default.
  • stdin (string) Optional base64-encoded stdin blob, written to the child's stdin before the process starts reading much and then closed. Streaming stdin is not supported — pipe from a file inside the guest if you need that shape.

ExecEvent

One event in the NDJSON exec stream returned by `POST /v1/vms/{id}/exec` under `Accept: application/x-ndjson`. Short field names (`t`, `d`, `c`, `to`, `ms`) keep per-chunk overhead small since high-output commands can produce thousands of events per exec.

  • t ("o" | "e" | "x", required) Event type: `o` = stdout chunk, `e` = stderr chunk, `x` = terminal exit event.
  • d (string) For `o`/`e`: base64-encoded raw bytes of the chunk. For `x`: optional diagnostic string (e.g. spawn failure) when non-empty.
  • c (integer) Exit code. Present on `x` events only.
  • to (boolean) True if the command was killed by the timeout. `x` events only.
  • ms (integer) Guest-reported duration in milliseconds. `x` events only.

ExecVMResponse

Buffered response shape for `POST /v1/vms/{id}/exec` under `Accept: application/json`. The server collects the streamed events and returns this aggregate once the command exits. Per-stream output is capped at 4 MiB; overflow bytes are dropped and signalled via `stdoutTruncated` / `stderrTruncated`. Streaming clients (`Accept: application/x-ndjson`) receive every byte without a cap.

  • exitCode (integer, required)
  • stdout (string, required)
  • stderr (string, required)
  • timedOut (boolean, required)
  • stdoutTruncated (boolean, required) True if the collector dropped stdout bytes past the 4 MiB cap.
  • stderrTruncated (boolean, required) True if the collector dropped stderr bytes past the 4 MiB cap.
  • durationMs (integer, required)

FilePresignRequest

  • path (string, required) Absolute destination path inside the guest filesystem (where the file will land after `fetchFileToVm`). Used only to scope the staging object key; any value server-side is accepted here.

FilePresignResponse

Pair of signed URLs scoped to the same per-VM staging object. Usable in either direction: either side (client or VM) PUTs bytes to `uploadUrl`, and either side GETs them back via `downloadUrl`. URLs expire after `expiresInSec` seconds and the staging object is auto-deleted after about a day.

  • uploadUrl (string, required) Presigned PUT URL for the staging object. Accepts `Content-Type: application/octet-stream`. Used by the client on upload, or by the VM (via an exec'd `curl -T -`) on download.
  • downloadUrl (string, required) Presigned GET URL for the same staging object. Used by the VM (via `POST /v1/vms/{id}/files/fetch`) on upload, or by the client (via `httpx.stream` / `curl`) on download.
  • expiresInSec (integer, required) Lifetime of both URLs in seconds.
  • maxUploadBytes (integer, required) Upper bound on upload size (equals the VM's disk size in bytes).

FileFetchRequest

  • url (string, required) Must be the `downloadUrl` previously returned by `POST /v1/vms/{id}/files/presign` (URLs from other sources are rejected).
  • path (string, required) Absolute destination path inside the guest filesystem.
  • timeoutSec (integer) Per-fetch timeout in seconds.

ConsoleTokenResponse

  • token (string, required)
  • expiresInSec (integer, required)
  • websocketPath (string, required) Relative WebSocket path; combine with your API host as `wss://<host><websocketPath>?session=<token>`.

SshKey

  • name (string) Optional human label.
  • publicKey (string, required) OpenSSH-format public key, of the form `<type> <base64-blob>` — the optional comment is stripped server-side. Supported types: `ssh-ed25519`, `ssh-rsa`, `ecdsa-sha2-nistp{256,384,521}`, plus FIDO2 hardware-backed variants (`sk-...@openssh.com`).
  • fingerprint (string, required) OpenSSH SHA256 fingerprint, e.g. `SHA256:abc...`. This is the **identifier** — matches what `ssh-keygen -lf` prints and what your ssh client shows on first connect; pass it back as the `{fingerprint}` path segment to `deleteSshKey`.
  • createdAt (string, required)

SshKeyListResponse

  • keys (SshKey[], required)

AddSshKeyRequest

  • name (string) Optional human label.
  • publicKey (string, required) OpenSSH-format public key (`ssh-ed25519 AAA...`). Comments are stripped. Newlines are rejected.

OrgQuotaValues

  • vcpu (integer, required)
  • memoryMiB (integer, required)
  • diskGiB (integer, required)
  • snapshotCount (integer, required)
  • volumeCount (integer, required)
  • volumeGiB (integer, required)

OrgQuotaUsage

  • orgId (string, required)
  • limits (OrgQuotaValues, required)
  • usage (OrgQuotaValues, required)

Service

  • name (string, required) Service name (1–29 chars). Embedded in the public URL as `<name>--<vmIdHexNoHyphens>.proxy.<stack-domain>`.
  • port (integer, required) TCP port the service listens on inside the VM. Privileged ports (<1024) are rejected.
  • h2c (boolean, required) When true, the proxy speaks HTTP/2 cleartext (h2c) to the backend. Required for gRPC and h2c-only apps. When false (default), the proxy uses HTTP/1.1 — covers HTTP/1.1 apps, Server-Sent Events, and WebSocket pass-through.

RegisterServiceRequest

  • name (string, required)
  • port (integer, required)
  • h2c (boolean) Optional. When true, the proxy uses HTTP/2 cleartext to the backend (required for gRPC). Defaults to false (HTTP/1.1).

UpdateServiceRequest

  • port (integer, required) New TCP port. Same value as the existing entry is a no-op.
  • h2c (boolean) Optional. When true, the proxy uses HTTP/2 cleartext to the backend. Same value as the existing entry is a no-op; a different value updates the registered transport.

Volume

  • id (string, required)
  • name (string, required)
  • orgId (string, required)
  • accessMode (string, required) Access mode. Known values: `rw`, `ro`. Future server versions may introduce additional values.
  • sizeGiB (integer, required)
  • status (string, required) Lifecycle status. Known values: - `creating` — the substrate-create saga is in flight. Set by the server briefly between the customer's `POST /v1/volumes` and the worker substrate provisioning; attach attempts are rejected with `VOL_NOT_READY` until the saga commits. Clients polling immediately after create may observe this state. - `ready` — substrate is up; attachable. - `deleting` — cleanup is in progress; not attachable. Future server versions may introduce additional values.
  • pendingSizeGiB (integer) When non-zero, a resize saga is in flight; `sizeGiB` is still the pre-resize value and `pendingSizeGiB` is the target. Set briefly between `PATCH /v1/volumes/{id}` and the substrate resize commit. Clients polling immediately after a resize may observe a non-zero value.
  • mountedCount (integer, required) Number of currently-running VMs with this volume attached (paused VMs are NOT counted).
  • usedGiB (integer) Bytes used inside the volume (rounded down to GiB). Fetched on-demand from the substrate; omitted when the substrate is unreachable.
  • createdAt (string, required)

CreateVolumeRequest

  • name (string, required)
  • sizeGiB (integer, required)
  • accessMode ("rw" | "ro", required)

UpdateVolumeRequest

At least one of `name`, `sizeGiB`, or `accessMode` must be present. `accessMode` requires `mountedCount == 0`. `sizeGiB` shrink requires `usedGiB <= newSizeGiB`.

  • name (string)
  • sizeGiB (integer)
  • accessMode ("rw" | "ro")

VolumeAttachmentRequest

  • volumeId (string, required)
  • mountPath (string, required) Absolute path; must start with /mnt/ or /data/.
  • readOnly (boolean)

VolumeAttachmentItem

  • volumeId (string, required)
  • mountPath (string, required)
  • readOnly (boolean)
  • mountStatus (string, required) Known values: `mounted`, `failed`, `pending`. `pending` appears on attachments to paused VMs (mount happens on resume) and briefly during in-flight hot-attach.
  • statusMessage (string)

VolumeAttachmentItemWithVm

DetachVolumeResponse

  • detached (boolean, required)
  • warnings (DetachWarning[])

DetachWarning

  • type (string, required) Known values: `ack_timeout`, `guest_unresponsive`.
  • message (string, required)

BucketMount

  • id (string, required)
  • vmId (string, required)
  • bucketUri (string, required) `gs://...` or `s3://...`; future schemes may be added.
  • mountPath (string, required)
  • readOnly (boolean)
  • mountStatus (string, required) Known values: `mounted`, `failed`, `pending`.
  • statusMessage (string)
  • createdAt (string, required)

BucketMountCredentials

Customer-provided credentials. Never returned in API responses. Discriminated union: the `type` property selects the per-provider shape so SDKs surface typed per-type values.

GcpServiceAccountCredentials

  • type ("gcp-service-account-json", required)
  • value (object, required)

AwsCredentials

  • type ("aws-credentials", required)
  • value (object, required)

CreateBucketMountRequest

  • bucketUri (string, required) Customer's GCS or S3 bucket URI. `gs://<bucket>[/prefix]` or `s3://<bucket>[/prefix]`.
  • mountPath (string, required)
  • readOnly (boolean)
  • credentials (BucketMountCredentials, required)

UpdateBucketMountRequest

  • credentials (BucketMountCredentials, required)

AttachmentWarnings

  • skippedSnapshotVolumes (SnapshotVolumeSkip[])
  • failedVolumeAttachments (FailedVolumeAttachment[])
  • skippedSnapshotBucketMounts (SnapshotBucketMountSkip[])
  • failedBucketMountAttachments (FailedBucketMountAttachment[])

SnapshotVolumeSkip

  • volumeId (string, required)
  • mountPath (string, required)
  • reason (string, required) Known values: `deleted`, `deleting`, `cross_org`, `vol_ro_ceiling_after_patch`.

FailedVolumeAttachment

  • volumeId (string, required)
  • mountPath (string, required)
  • statusMessage (string, required)

SnapshotBucketMountSkip

  • bucketUri (string, required)
  • mountPath (string, required)
  • reason (string, required) Known values: `credentials_invalid`, `bucket_unreachable`, `credentials_unavailable`.

FailedBucketMountAttachment

  • bucketUri (string, required)
  • mountPath (string, required)
  • statusMessage (string, required)

SnapshotVolumeAttachment

  • volumeId (string, required)
  • mountPath (string, required)
  • readOnly (boolean)

SnapshotBucketMountAttachment

  • bucketUri (string, required)
  • mountPath (string, required)
  • readOnly (boolean)