Base URL: https://<server>
Most endpoints require a Bearer token in the Authorization header:
Authorization: Bearer <admin_token | worker_token>
Token comparison uses constant-time algorithm to prevent timing attacks.
Browser-facing member endpoints are authenticated with Cloudflare Access. The origin expects the Access JWT in Cf-Access-Jwt-Assertion and validates it against the configured Access audience and issuer.
The browser frontend lives under frontend/ and uses pnpm:
cd frontend
pnpm install
pnpm dev
pnpm buildHealth check.
Response 200
{ "status": "ok" }Returns the currently authenticated member from Cloudflare Access.
Response 200
{
"id": "2b2f8a8f-...",
"access_sub": "7335d417-61da-459d-899c-0a01c76a2f94",
"email": "user@example.com",
"display_name": "user",
"role": "member",
"created_at": "2026-03-24T07:10:00Z",
"updated_at": "2026-03-24T07:10:00Z",
"last_seen_at": "2026-03-24T07:10:00Z"
}Returns workers and their reported VMs for the authenticated browser session.
This is the browser-safe equivalent of the old admin inventory call and is intended for the React frontend.
Response 200
[
{
"id": "w-abc123",
"hostname": "mini-01",
"status": "active",
"cpu_cores": 10,
"memory_bytes": 549755813888,
"vms": [
{
"id": "vm-123",
"worker_id": "w-abc123",
"state": "ready",
"ip_address": "192.168.64.5",
"state_since": "2026-03-24T07:10:00Z",
"created_at": "2026-03-24T07:05:00Z"
}
]
}
]Returns the currently assigned dedicated workstation for the authenticated member.
If the workstation exists but is stopped or errored, the server will mark it to run again before returning it.
Response 200
{
"id": "ws-123",
"member_id": "2b2f8a8f-...",
"worker_id": "w-abc123",
"slot": 0,
"vm_name": "ws-abc123def456",
"power_state": "running",
"desired_power_state": "running",
"ip_address": "192.168.64.5",
"last_error": "",
"created_at": "2026-03-24T07:05:00Z",
"updated_at": "2026-03-24T07:10:00Z"
}| Status | Body |
|---|---|
404 |
{"error": "workstation not found"} |
Claims a workstation for the authenticated member.
If the member already has a workstation:
- with no request body, the existing workstation is resumed/returned
- with
worker_id, the workstation is moved to that target worker if it has capacity
Request
{
"worker_id": "w-abc123"
}worker_id is optional.
Response 200 or 201
{
"id": "ws-123",
"member_id": "2b2f8a8f-...",
"worker_id": "w-abc123",
"slot": 1,
"vm_name": "ws-abc123def456",
"power_state": "starting",
"desired_power_state": "running",
"ip_address": "",
"last_error": "",
"created_at": "2026-03-24T07:05:00Z",
"updated_at": "2026-03-24T07:10:00Z"
}| Status | Body |
|---|---|
409 |
{"error": "target worker is not available"} |
409 |
{"error": "no workstation slots available on target worker"} |
409 |
{"error": "no workstation slots available"} |
409 |
{"error": "workstation is being deleted"} |
Marks the member workstation for deletion.
Response 200
Creates a browser-initiated proxy session for either 5900 or 22.
Request
{
"target": "vm",
"vm_id": "vm-123",
"port": 5900
}Response 201
{
"session_id": "9e4b2f10-...",
"token": "6c7c4f..."
}The browser then connects to:
/api/v1/proxy/ws?session=<session_id>&role=client&token=<token>
WebSocket endpoint for browser terminal sessions. This endpoint is authenticated by Cloudflare Access and then bridges SSH on the server side, so the browser only needs xterm.js.
Query params
| Param | Required | Description |
|---|---|---|
target |
no | vm or host, defaults to vm |
vm_id |
for target=vm |
VM identifier |
worker_id |
for target=host |
Worker identifier |
cols |
no | Initial terminal width |
rows |
no | Initial terminal height |
Client -> server messages
{ "type": "input", "data": "ls -la\r" }
{ "type": "resize", "cols": 132, "rows": 40 }Server -> client messages
- Binary frames: terminal output bytes
- Text frames:
{ "type": "ready" }
{ "type": "error", "message": "SSH session could not be established." }Returns the worker setup script as plain text. The script only injects the server
address at serve time; first-time enrollment expects the token to already exist in
~/mini-control-enroll.key on the Mac mini (or to be passed via --token).
Response 200 text/x-shellscript
Returns metadata for the most recently uploaded release.
Response 200
{
"id": "8306d3eb-...",
"version": "0.1.0",
"sha256": "074c4368...",
"size_bytes": 37443040,
"signing_identity": "(team: TEAMID, id: MiniControlWorker)",
"uploaded_at": "2026-03-17T07:45:42Z"
}| Status | Body |
|---|---|
404 |
{"error": "no releases available"} |
Downloads a release binary.
Path params: version — must match ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$
Response 200 application/octet-stream
Response headers:
| Header | Example |
|---|---|
Content-Disposition |
attachment; filename=minicontrol-worker |
X-Release-SHA256 |
074c4368... |
X-Release-Version |
0.1.0 |
| Status | Body |
|---|---|
400 |
{"error": "invalid version"} |
404 |
{"error": "version not found"} |
Upload a signed worker binary as a raw request body. Requires auth.
Content-Type: application/octet-stream
Path params: version — must match ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$
Server validates the Mach-O code signature against the configured required_signing_identity. If the identity is empty, signature validation is skipped.
Response 201
{
"id": "...",
"version": "0.1.0",
"sha256": "074c4368...",
"size_bytes": 37443040,
"signing_identity": "(team: TEAMID, id: MiniControlWorker)",
"uploaded_at": "2026-03-17T07:45:42Z"
}| Status | Body |
|---|---|
400 |
{"error": "invalid version"} |
400 |
{"error": "binary body is required"} |
400 |
{"error": "<signature validation error>"} |
409 |
{"error": "release version already exists"} |
503 |
{"error": "release management not configured"} |
Register a worker with the server. Registration uses the request-body token and accepts either:
enrollment_tokenfor first install / re-enrollment- an issued per-worker
worker_tokenfor normal restarts
Request
{
"hostname": "mac-1",
"hardware_uuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"cpu_cores": 10,
"memory_bytes": 17179869184,
"tart_version": "2.31.0",
"worker_version": "0.1.0",
"worker_token": "<token>"
}Response 200
{
"worker_id": "mac-1-abc123",
"pool_size": 2,
"base_image": "minictl-tahoe-base",
"worker_token": "wkt_..."
}| Status | Body |
|---|---|
401 |
{"error": "invalid token"} |
If registration used enrollment_token, the server issues a new per-worker worker_token in the response.
List all workers and their VMs. Requires auth.
Response 200
[
{
"id": "mac-1-abc123",
"hostname": "mac-1",
"hardware_uuid": "...",
"cpu_cores": 10,
"memory_bytes": 17179869184,
"tart_version": "2.31.0",
"worker_version": "0.1.0",
"pool_size": 2,
"base_image": "minictl-tahoe-base",
"status": "active",
"registered_at": "2026-03-17T07:00:00Z",
"last_heartbeat": "2026-03-17T07:50:00Z",
"vms": [
{
"id": "vm-xxxx",
"worker_id": "mac-1-abc123",
"state": "ready",
"ip_address": "192.168.64.5",
"state_since": "2026-03-17T07:10:00Z",
"created_at": "2026-03-17T07:05:00Z"
}
]
}
]Worker heartbeat. Updates VM states. Requires auth.
Request
{
"vm_statuses": [
{
"vm_id": "vm-xxxx",
"state": "ready",
"ip_address": "192.168.64.5"
}
],
"lan_address": "192.168.1.100"
}Response 200
{
"status": "ok"
}Update VM state. Requires auth. If state is "destroying", the VM record is deleted.
Request
{
"worker_id": "mac-1-abc123",
"state": "ready",
"ip_address": "192.168.64.5",
"error_message": ""
}Response 200
{ "status": "ok" }Create a TCP proxy session (e.g. for VNC forwarding). Requires auth.
Request
{
"target": "vm",
"vm_id": "vm-xxxx",
"port": 5900
}For host proxy:
{
"target": "host",
"worker_id": "mac-1-abc123",
"port": 22
}target defaults to "vm" if omitted.
Response 201
{
"session_id": "sess-xxxx",
"token": "<session-token>"
}| Status | Body |
|---|---|
400 |
{"error": "port is required"} |
400 |
{"error": "vm_id is required for vm target"} |
400 |
{"error": "worker_id is required for host target"} |
403 |
{"error": "port not allowed"} |
404 |
{"error": "vm not found"} |
409 |
{"error": "vm not available"} |
Get pending proxy sessions for a worker. Requires auth.
Query params: worker_id (required)
Response 200
[
{
"session_id": "sess-xxxx",
"target": "vm",
"vm_id": "vm-xxxx",
"port": 5900,
"token": "<session-token>"
}
]WebSocket endpoint for proxy data relay. Auth via query parameter.
Query params:
| Param | Required | Values |
|---|---|---|
session |
yes | Session ID |
role |
yes | client or worker |
token |
yes | Session token |
Upgrades to WebSocket. Binary frames are relayed between client and worker.
Assign a sequential hostname (mac-N) to a worker. Requires auth.
Request
{
"hardware_uuid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}Response 200
{
"hostname": "mac-1"
}active | draining | offline
starting | bootstrapping | ready | busy | destroying | failed