Skip to content

SDK vs runtime host

MatrixRuntime is a class. Host Service is a process. They look like they do the same thing because the Host Service ultimately constructs a MatrixRuntime for each runtime it supervises. They are not the same job, and you choose between them at the right level of the stack.

Three layers

┌────────────────────────────────────────────────────────────────┐
│  Host Service  (one OS process; supervises N runtimes)          │
│    ├── runtime A (system, auto-start)                           │
│    ├── runtime B (host-control, auto-start)                     │
│    ├── runtime C (system-gateway-http, auto-start)              │
│    ├── runtime D (matrix-web --serve, auto-allocated port)      │
│    └── runtime E (your-package --serve)                         │
│             │                                                    │
│             ▼                                                    │
│        ┌────────────────────────┐                               │
│        │  Runner (per runtime)  │                               │
│        │   loads package.json   │                               │
│        │   loads matrix.json    │                               │
│        │   constructs:          │                               │
│        │   ┌──────────────────┐ │                               │
│        │   │  MatrixRuntime   │ │  ← THE SDK                    │
│        │   │  + transport     │ │                               │
│        │   │  + actors        │ │                               │
│        │   └──────────────────┘ │                               │
│        └────────────────────────┘                               │
└────────────────────────────────────────────────────────────────┘
  • Host Service is the supervision layer. It owns runtime records under <host-home>/runtimes/, restarts crashed runtimes, manages the bundled NATS sibling, exposes the gateway HTTP port, and knows about installs, links, ports, and devices.
  • Runner is per-runtime. It reads one package and one environment, builds a MatrixRuntime, attaches a transport, mounts actors. Browsers are runners too: every tab connects to NATS and instantiates its own MatrixRuntime with a BrowserDomStrategy.
  • MatrixRuntime is the SDK class. It is the unit of actor system — a transport, a root context, a set of mounted actors, plus a topology supervisor for root-level actors.

When to use MatrixRuntime directly

Construct MatrixRuntime yourself when:

  • Writing a unit or integration test. The runtime accepts a stub transport and a stub host. You can mount fake actors, send envelopes, and assert on what came back.
  • Embedding Matrix in another process that already has its own supervisor. For example, a CI tool that spins up a runtime inside a Node test process and tears it down per test.
  • Building a bespoke runner. If your needs do not fit the Host Service model (e.g. you want to mount actors inside an Electron renderer with its own lifecycle), you can wrap MatrixRuntime directly.

The minimal wiring (projects/matrix-3/packages/core/src/runtime/MatrixRuntime.ts:127-201):

ts
import { MatrixRuntime, MatrixActor, InMemoryTransport, InMemoryBroker } from '@open-matrix/core';

const broker = new InMemoryBroker();
const transport = new InMemoryTransport(broker, { name: 'my-runtime' });
const runtime = new MatrixRuntime({ transport });

class MyActor extends MatrixActor {
  static accepts = {
    'foo.bar': {
      description: 'Trivial example op',
      schema: {},
      returns: { ok: { type: 'boolean', description: 'Always true' } },
    },
  };
  async onFooBar() { return { ok: true }; }
}

await runtime.create(MyActor, 'my-actor');

If you do not pass a transport, MatrixRuntime creates its own InMemoryTransport (MatrixRuntime.ts:146-152). That is fine for tests and demos; it is not what you want in production because nothing else can reach actors mounted on a private in-memory broker.

When to use Host Service

Use the Host Service path for anything that ships:

  • Production Matrix packages. A package becomes runnable by being supervised under Host Service. The runner reads matrix.json, allocates ports through the gateway, registers itself with system.runtimes, and is restarted on crash according to its restart policy.
  • Local development. hivecast install followed by hivecast start brings up a full local Host with bundled NATS, system actors, gateway, Matrix Web, and Matrix Edge. matrix run <package> --serve adds your package to that running Host as another supervised runtime.
  • Browser-served packages. --serve flips a runtime from headless to a static HTTP server registered behind the gateway. Browsers load the resulting index.html and bootstrap their own MatrixRuntime against the same NATS bus.

The dev loop is documented in the project root CLAUDE.md ("Host Service / Docker-NPM parity dev loop"). The relevant commands:

bash
node projects/matrix-3/packages/hivecast/bin/hivecast.mjs install --home /tmp/matrix-home
node projects/matrix-3/packages/hivecast/bin/hivecast.mjs start --home /tmp/matrix-home

pnpm --filter @matrix/mx-cli exec matrix run <package-or-dir> --serve --home /tmp/matrix-home

What the runner does on your behalf

When a runtime is started by Host Service, the runner does roughly this for you (paraphrasing the per-package wiring; the canonical implementation lives in @open-matrix/host-service and @matrix/mx-cli):

  1. Read matrix.json to find the runtime entry, the actor components, and the config contract.
  2. Read environment variables (per the envPrefix) and any service config (per system.config).
  3. Open a transport against the local NATS sibling using the install's authority root.
  4. Construct MatrixRuntime({ transport, ... }).
  5. For every entry in components, dynamically import() the actor class and call runtime.createSupervised(ActorClass, mount, props).
  6. Register the runtime with system.runtimes and (if --serve) bind a static HTTP listener through system-gateway-http.
  7. Heartbeat system.devices so the Devices page reflects reality.

You don't write this glue per-package. You write actors and a matrix.json, and the SDK + Host Service take care of the rest.

Rule of thumb

Tip: If a human or operator will install/start your code, use Host Service. If only your test runner will start it, construct MatrixRuntime directly with an InMemoryTransport.

The retired matrixd daemon path is intentionally disabled and must not be revived for product work. See the project root CLAUDE.md § "THE RULES" #1.

See also

Source: projects/matrix-3/packages/core/src/runtime/MatrixRuntime.ts:78-201 (constructor, default transport), root CLAUDE.md "Host Service / Docker-NPM parity dev loop".