SpipCP
Reference

Instances API

The /api/v1 instances surface — list, CRUD, lifecycle, limits, readiness, console, snapshots.

Everything the panel does is available on the API. The web UI and any mobile client all call the same procedures. This page documents the instances surface. Each procedure requires a permission, and the server enforces it — the access rules below are real, not just documentation.

Transport

Procedures are reached over oRPC at /api/rpc/* (typed client) and exposed on /api/v1 (OpenAPI). Inputs/outputs below are the wire shapes; the agent-facing protocol types they map to live in @spipcp/protocol.

Permissions

PermissionGrants
instances:viewList, get, drift, runs, readiness, metrics. Held by operators.
instances:createCreate an instance.
instances:manageEdit limits, start/stop/restart.
instances:removeDelete an instance.
instances:reconcileTrigger a reconcile / dry-run drift check. Held by operators.
instances:consoleOpen a console PTY session. Admin-only, audited.
instances:snapshotCreate / restore / delete snapshots. Admin-only.

Operators hold only instances:view + instances:reconcile. Every other instance procedure returns 403 for an operator.

Read

instances.list

instances:view{ nodeId?: string } → InstanceSummary[]. All instances newest-first, optionally scoped to one node.

instances.get

instances:view{ id } → InstanceSummary & { drift }. One instance with its latest drift state. 404 if unknown.

instances.drift

instances:view{ id } → DriftState | null.

instances.runs

instances:view{ id, limit?, offset? } → ReconcileRun[]. Reconcile-run history (newest-first).

InstanceSummary fields: id, nodeId, name, image, type, status, cpuLimit, memLimitMb, diskGb, configVersion, createdAt, updatedAt. status is one of pending | creating | running | stopped | error.

Create & limits

instances.create

instances:create

{ nodeId, name, image, type?: "container" | "vm",
  cpuLimit?: 1..64, memLimitMb?: 128..262144, diskGb?: 1..4096 }
  → { instance: InstanceSummary, runId }

Records the instance (status pending) and starts the first reconcile; the agent builds the real Incus container. name must be a valid Incus name (^[a-z][a-z0-9-]*$). 404 if the node is unknown.

instances.updateLimits

instances:manage{ id, cpuLimit, memLimitMb, diskGb } → { instance, runId }. Applies new CPU, memory, and disk limits live.

Lifecycle

instances.lifecycle

instances:manage{ id, action: "start" | "stop" | "restart" } → { queued: true }. Starts, stops, or restarts the instance. Audited as instance.<action>.

instances.remove

instances:remove{ id } → { removed: true }. Deletes the instance and tears the container down on the node. Audited.

instances.triggerReconcile

instances:reconcile{ id, dryRun?: boolean } → { runId }. Triggers a reconcile, or a dry-run drift check (no mutations). 404 if unknown.

Readiness

instances.readiness

instances:view{ id } → { ready: boolean, gates: Gate[] } where

Gate = { check: string, status: "pass" | "fail", code: string, detail: string, remedy?: string }

The checks (NIC on bridge, DHCP lease, DNS, apt reachability) run inside the instance before any service install; a failing check names its remedy. The instance isn't "ready" until all checks pass.

Metrics

instances.metrics

instances:view{ id } → IncusInstanceMetricsResult:

{ name, status, privateIp?, cpuUsageNs, cpuAllocatedTimeNs?,
  memoryUsedMb, memoryTotalMb, diskUsedGb, diskTotalGb,
  networkRxBytesTotal, networkTxBytesTotal, uptimeSeconds?, sampledAt }

A live sample read from the agent; it powers the workspace usage bars. Poll it for a moving view.

Console

instances.consoleSession

instances:console (admin-only, audited) — { id } → { ptyUrl }. Mints a short-lived, single-use token and returns a fully-formed WebSocket URL of the form ws(s)://<worker>/pty/instance/:instanceId?token=…. The client connects xterm.js to it for an interactive incus exec PTY. Returns 403 for operators (and the UI hides the console tab).

Snapshots

instances:snapshot (admin-only) — all keyed by the snapshot name:

ProcedureInputOutput
instances.snapshot.list{ id }IncusSnapshot[] ({ name, createdAt?, expiresAt?, stateful?, sizeBytes? })
instances.snapshot.create{ id, snapshotName }{ snapshotName }
instances.snapshot.restore{ id, snapshotName }{ restored: true }
instances.snapshot.delete{ id, snapshotName }{ deleted: true }

Mobile-app ready

These are the same procedures the web workspace calls — a mobile client lists, creates, consoles, and snapshots through this same surface. There's no separate "mobile API".

On this page