Skip to content

Host vs runtime

A common confusion: "the daemon," "the host," "the runtime," "the supervisor" sound like the same thing. They are not. The four operational units rule (see repo CLAUDE.md § "Architecture: The Four Operational Units") names four distinct things: Environment, Package, Runner/Runtime, and Host.

This page is just about the last two.

Definitions

Host — one process. Loads host.json, supervises NATS (or connects to an external one), spawns child processes, persists their runtime.json records, auto-starts the ones whose startup is auto. Implementation: MatrixHostService in projects/matrix-3/packages/host-service/src/matrix-host-service.ts.

Runtime — one supervised child process. Reads its <host-home>/runtime-env/<id>.environment.json, connects to the same NATS the Host configured, instantiates a MatrixRuntime and the actors of one or more deployed packages, mounts them on declared subjects, then runs forever. Examples in the default install: RUNTIME-HOST-...-SYSTEM, RUNTIME-HOST-...-HOST-CONTROL, RUNTIME-HOST-...-GATEWAY, RUNTIME-HOST-...-WEB, etc.

Status: today vs target. The codebase today implements one package per runtime; the runtime ID encodes the package (-CHAT, -DIRECTOR, etc.). The substrate target shape is multi-package runtimes: a single runtime hosts every shared-isolation package in a substrate role, named RUNTIME-HOST-...-CONTROL-PLANE / -USER-APPS / -WEB-SHELL / -EDGE. See P1.40. Read every "runtime hosts <package>" claim with that correction in mind.

Lifetimes

HostRuntime
Process count per machineone (per Host home)many
Owns NATS?yes (or connects to external)no, connects only
Owns disk state?yes — the home directoryno, only its log dir
Restarts other processes?yes (per restart policy)no
Survives a Host crash?n/aonly via auto-start on next Host start
Holds long-lived browser sockets?noyes (gateway runtime)

Records

A runtime is represented to the Host by a JSON record on disk:

json
// <host-home>/runtimes/RUNTIME-HOST-LOCAL-ABC123-CHAT/runtime.json
{
  "runtimeId": "RUNTIME-HOST-LOCAL-ABC123-CHAT",
  "packageRef": "@open-matrix/chat",
  "packageDir": "/home/user/.matrix/packages/global/node_modules/@open-matrix/chat",
  "environment": "host",
  "pid": 41522,
  "status": "running",
  "startup": "auto",
  "restart": "always",
  "startedAt": "2026-05-04T18:21:01.103Z",
  "updatedAt": "2026-05-04T18:21:02.991Z",
  "runtimeMount": "system.runtimes.RUNTIME-HOST-LOCAL-ABC123-CHAT",
  "controlMount": "system.runtimes.RUNTIME-HOST-LOCAL-ABC123-CHAT.control",
  "runtimeWireRoot": "COM.OPEN-MATRIX.LOCAL.AUTHORITY",
  "localMounts": [
    "chat",
    "system.runtimes.RUNTIME-HOST-LOCAL-ABC123-CHAT",
    "system.runtimes.RUNTIME-HOST-LOCAL-ABC123-CHAT.control"
  ],
  "metadata": { "command": "...", "args": ["..."], "cwd": "...", "logDir": "..." }
}

Status: target state — deployedPackages: [] field, P1.40-a. Today the schema has a single packageRef. P1.40-a adds an additive deployedPackages: [{ packageRef, instanceName, mountRoot }] array so a multi-package runtime can list every package it hosts; the legacy packageRef field stays for backward compatibility.

The full schema lives in IHostRuntimeRecord (projects/matrix-3/packages/host-service/src/types.ts:79-96) and is documented in Runtime record schema.

Why separate processes

The retired matrixd daemon mounted every package's actors inside its own single process. One leaky actor took the whole daemon down; every package shared a heap and an event loop. The host-service split fixes that: every runtime has its own pid, its own crash signal, and its own restart policy.

The substrate target shape (P1.40) layers per-package isolation declarations on top of that split. A package that declares runtime.isolation: process keeps its own pid; packages that declare shared co-tenant in one runtime to save resources but still cannot crash the supervisor. The crash domain is the runtime, not the package, and the runtime is replaceable.

See projects/matrix-3/packages/docs/content/architecture/daemon-migration.md for the post-mortem.

How a Host starts a runtime

Source: MatrixHostService.startRuntimeProcess at projects/matrix-3/packages/host-service/src/matrix-host-service.ts:344-492.

  1. Sanitize runtimeId. Resolve the package directory.
  2. Detect mount conflicts: if another live runtime already owns a logical mount or web route claimed by this start, refuse with a clear error (matrix-host-service.ts:543-576).
  3. Create the per-runtime log directory; open append-mode log streams.
  4. Spawn the child with spawn(spec.command, spec.args, { cwd, env, ... }), windowsHide: true (so failing-runtime + restart: always does not pop console windows on Windows), stdout/stderr piped into the log files.
  5. Write the runtime.json record before waiting for readiness, so a crashed readiness probe still leaves a durable record.
  6. Wait for runtime.ready on the controlMount (with runtimeId and pid identity-checked against the spawn) and, if the runtime owns system.runtimes, additionally $ping that mount.
  7. Hook the child's exit event: write a final record, restart per restart policy.

See also