Skip to content

Package vs runtime

A package is a directory of code. A runtime is a process that runs one package's code. They are not the same thing, and the same package can spawn many runtimes — one per environment, per Host, per replica.

Definitions

TermMeaning
PackageA versioned directory in the package store (<MATRIX_HOME>/packages/.../node_modules/<name>/)
RuntimeA live OS process started by Host Service that has loaded the package's runtime.entry and (optionally) called the matrix.service.json factory
Runtime record<MATRIX_HOME>/runtimes/<runtimeId>.json — the persistent description of a desired runtime
Runtime idA stable identifier such as RUNTIME-HOST-CHAT, used for up/down/logs

A runtime is born when the Host's runtime.start op is invoked with a startSpec (see host-service/src/cli.ts:196-235). A runtime dies when its runtime.stop is invoked or the process exits.

The same package can run as multiple runtimes

Two reasons this happens in practice today:

  1. Multiple environments. The same package built once may be started under different --env profiles (.matrix/dev.environment.json, .matrix/integration.environment.json), each producing its own runtime record with its own runtimeId, runtimeMount, and root.
  2. Two-runtime dev topology. A single Host may start @open-matrix/matrix-web and @open-matrix/matrix-edge as two distinct runtimes inside the same Host (see CLAUDE.md "Two-Runtime Dev Topology"). They share NATS, share the authority root, and live as different supervised processes with different ports.

What turns a package into a runtime

Three pieces, in this order:

  1. The CLI assembles a startSpec. Source: projects/matrix-3/packages/host-service/src/cli.ts:196-229.
  2. Host Service writes a runtime record under <host-home>/runtimes/ and spawns the runtime process.
  3. The runtime process imports runtime.entry, calls the matrix.service.json factory if present, and registers itself with system.runtimes and system.registry via the runner control plane (packages/mx-cli/src/utils/runner-runtime-control.ts).

Identity facets

A live runtime carries multiple identifiers — do not collapse them:

FieldExampleSource
runtimeIdRUNTIME-HOST-CHATstart spec metadata
runtimeMountsystem.runtimes.chat-dev-01derived from runtimeId
controlMountsystem.runtimes.chat-dev-01.controlderived from runtimeId
packageRef@open-matrix/chat@0.2.9from the package being run
runtimeWireRootCOM.NIMBLETEC.RICHARD-SANTOMAUROenvironment, not the package

The wire root comes from the environment, not from the package. CLAUDE.md is explicit on this: packages do not own ports, storage, or transport roots.

Lifecycle states a runtime moves through

The runner reports lifecycle state via the control mount (see runner-runtime-control.ts:RunnerRuntimeLifecycleState):

starting → live → stopping → stopped

                  failed

hivecast runtimes and system.runtimes queries surface these states. See Running → Health.

Practical implications

  1. Build once, start many times. Reuse the same dist/ for different environments by passing different --env flags.
  2. One runtime crashing does not kill its package. Other runtimes loading the same package keep running. Host Service supervises each independently.
  3. A runtimeId is not a mount. Mounts are bus addresses for actors; runtimeId is the identity of the supervised process.

See also

Source: projects/matrix-3/packages/host-service/src/cli.ts:145-265 implements up/down/invoke; packages/mx-cli/src/utils/runner-runtime.ts wraps the in-process runtime; lifecycle states are in packages/mx-cli/src/utils/runner-runtime-control.ts.