Appearance
Runtime visibility
The Devices facade exposes a Device's runtimes through two ops: devices.runtimes.register and devices.runtimes.list. This is runtime inventory, not runtime control. The same system.devices actor will not start, stop, or reconfigure runtimes — that is host.control's job through system.runtimes.
What gets exposed
devices.runtimes.list({ deviceId, principalId? }) returns:
ts
{
ok: boolean;
deviceId: string;
count: number;
runtimes: IDeviceRuntimeRecord[];
}with each runtime shaped as:
ts
// projects/matrix-3/packages/system/src/SystemDevicesActor.ts:9-25
interface IDeviceRuntimeRecord {
runtimeId: string;
runtimeWireRoot?: string;
runtimeMount?: string;
controlMount?: string;
packageRef?: string; // e.g. '@open-matrix/chat'
packageDir?: string; // local path to the package source
environmentName?: string; // 'dev', 'hivecast', etc.
app?: string; // appName for browser packages
status: string; // 'running', 'starting', 'stopped', ...
mountCount?: number;
localMounts?: readonly string[];
webapp?: Record<string, unknown>; // routePrefix, displayName, icon, ...
registeredAt: number;
updatedAt: number;
metadata: Record<string, unknown>;
}This is enough to render a Device detail page that shows:
- which packages this Device is currently serving
- the logical mounts each runtime publishes (so the user can
invokethem) - the HTTP webapp surface (so the shell can list "open this app on this Device")
- environment name + status for at-a-glance health
What does NOT travel through devices.runtimes.list
Deliberately omitted:
- PID / process metadata. That is Host-local and lives in
host.status.jsonplus per-runtimeruntime.json. Cross-Device invocation does not need to know a remote Device's PIDs. - Log file paths. Same reasoning. Logs are read via Host Service's per-runtime log files (
<host-home>/logs/runtimes/<runtime-id>/), not through the Devices facade. - Credentials, tokens, or any secret material. Runtimes that hold per-provider credentials (e.g. inference catalog) keep them in the Device's local
system.factotum. Heartbeats never carry secrets. - Resource handles. A driver runtime exposing a USB serial device reports its logical mount, not the kernel handle. The handle is meaningful only on the Device.
How runtimes get registered
host.control reads each runtime record and calls devices.runtimes.register once per runtime per heartbeat. The payload is constructed by runtimeHeartbeatPayload() in host-control/src/HostControlActor.ts:415-451. The receiver:
ts
// projects/matrix-3/packages/system/src/SystemDevicesActor.ts:284-328
onDevicesRuntimesRegister(payload: Record<string, unknown>): { … } {
// 1. validate deviceId + runtimeId
// 2. check the Device record exists
// 3. check the payload identity scope matches the Device's identity scope
// 4. carry forward previous values (mountCount, localMounts, webapp, registeredAt)
// 5. set status, updatedAt, metadata
}Step 3 is the same deviceMatchesPayloadMutationScope() rule used by devices.register: a runtime cannot be registered against a Device record whose principalId/spaceId/authorityRoot/hostLinkId/publicNamespace /routeKey differs.
Filtering by Host Link authority root
Before host.control calls devices.runtimes.register, it filters out runtimes whose runtimeWireRoot does not match the Host Link's authorityRoot:
ts
// projects/matrix-3/packages/host-control/src/HostControlActor.ts:453-461
function filterRuntimeRecordsForLinkedHost(
runtimes: readonly IHostRuntimeRecord[],
link: IResolvedHiveCastHostLink,
): IHostRuntimeRecord[] {
return runtimes.filter((runtime) => {
const runtimeWireRoot = readString(runtime.runtimeWireRoot);
return runtimeWireRoot === link.authorityRoot;
});
}Why: a Host can technically host runtimes for multiple authorityRoots during dev (or while migrating between Spaces). Only runtimes attached to the linked Space are advertised to the linked-Device facade.
Multi-runtime dev topology and Devices
The two-runtime dev topology described in CLAUDE.md ("Two-Runtime Dev Topology") runs matrix-web (platform shell) and matrix-edge (Device shell) inside the same Host. Both runtimes appear under the same Device record because they share the Host Link's authorityRoot. From the devices.runtimes.list view they are two separate entries with different runtimeIds and different webapp.routePrefix values (/apps/web/, /apps/edge/).
See also
- Device vs runtime — the architectural separation.
- Heartbeats — what triggers
devices.runtimes.register. - Reference / Device actor ops — full signature.
Source:
projects/matrix-3/packages/system/src/SystemDevicesActor.ts(onDevicesRuntimesRegister(),onDevicesRuntimesList(),IDeviceRuntimeRecord);projects/matrix-3/packages/host-control/src/HostControlActor.ts(runtimeHeartbeatPayload(),filterRuntimeRecordsForLinkedHost()).