Skip to content

Host supervisor actor

host.supervisor is the supervisor's own RPC mailbox on NATS. It is not a MatrixActor — it is a plain NATS subscription bound by the Host process. The friendlier HostControlActor (next page) wraps it, but both speak the same envelope.

Subjects

The mailbox subject is built by buildHostSupervisorSubject (host-supervisor-rpc.ts:43-53):

<root>.<supervisorMount>.$inbox
  • <root> is the Host's transport root from host.json (transport.root), e.g. COM.OPEN-MATRIX.LOCAL.AUTHORITY.
  • <supervisorMount> is host.supervisor.PID-<pid> for live Hosts (set by _buildStatus at matrix-host-service.ts:935). The pid suffix prevents two Host processes from claiming the same mailbox.

Reply subjects: <root>.$reply.host-supervisor.<correlationId> (host-supervisor-rpc.ts:99-100).

Server

HostSupervisorControlServer.start (host-supervisor-control-server.ts:28-60):

  1. Connects to NATS using the credentials referenced in host.status.json.
  2. Subscribes to the supervisor inbox.
  3. For each request, decodes the envelope, runs the execute callback (provided by MatrixHostService._executeSupervisorOp, matrix-host-service.ts:798-846), and publishes the response to the request's replyTo.

The execute callback handles every supervisor op listed below.

Ops

OpPayloadResult
status{}{ ok: true, status: <hostStatus>, runtimes: [...] }
runtime.list{}{ ok: true, runtimes: [...] } (with control liveness)
runtime.registerfull IHostRuntimeRecord{ ok: true, runtimeId }
runtime.start{ startSpec: IHostRuntimeStartSpec }{ ok: true, runtime: <record> }
runtime.stop{ runtimeId: string }{ ok: true, runtime: <record> }
config.reload{}{ ok: true, status: "reloading" } (real restart, see Config inheritance)
shutdown{}{ ok: true, status: "stopping", host: <status> } (then the Host exits)

Source: MatrixHostService._executeSupervisorOp, matrix-host-service.ts:798-846.

Errors return { ok: false, error: "..." }. The transport never throws; every error is delivered as a response.

Request envelope

json
{
  "op": "runtime.list",
  "payload": {},
  "correlationId": "1714838712345-abc",
  "replyTo": "COM.OPEN-MATRIX.LOCAL.AUTHORITY.$reply.host-supervisor.1714838712345-abc"
}

buildHostSupervisorSubject, decodeHostSupervisorRequest, and encodeHostSupervisorResponse (host-supervisor-rpc.ts:43-163) own the wire format. The reply envelope mirrors the request:

json
{
  "ok": true,
  "correlationId": "1714838712345-abc",
  "result": { "runtimes": [ ... ] }
}

Calling from code

ts
import { invokeHostSupervisorFromStatus } from '@open-matrix/host-control';

const hostStatus = JSON.parse(fs.readFileSync('<home>/host.status.json', 'utf8'));
const result = await invokeHostSupervisorFromStatus(
  hostStatus,
  'runtime.list',
  {},
  5_000,
);

invokeHostSupervisorFromStatus (host-supervisor-rpc.ts:55-76) extracts transport.root, transport.nats.url, supervisorMount, and the optional credentialsRef from the live Host status, then opens a short-lived NATS connection, sends the request, waits for the matching correlationId, and closes.

Calling from the CLI

bash
matrix invoke host.supervisor.PID-12345 runtime.list '{}' --timeout 5000

Or — recommended — through the host-control wrapper, which knows the mailbox without you specifying the pid:

bash
matrix invoke host.control runtime.list '{}'

When to call which

  • host.supervisor is the truth. Call it when:
    • The host-control runtime is itself in trouble.
    • You need the precise current status without host.control adding its own device-heartbeat side effects.
    • You're writing tooling that runs alongside the Host's own CLI.
  • host.control is the product surface. Call it when:
    • You're a normal package author needing supervisor info.
    • You want device heartbeats to flow as a side effect.
    • You want a stable mount name (host.control) that doesn't include a pid suffix.

See also