Appearance
Device vs runtime
The Matrix product splits what computes from what's running on it. A Device is the machine; a runtime is a process on that machine; a mount is a logical actor address. These three layers each have their own authority and their own actor.
The chain
text
Device (linked compute participant)
has many
Runtimes (supervised processes)
each mounts many
Actors (logical addresses)Each layer has a single authority surface:
| Layer | Authority actor | What it owns |
|---|---|---|
| Device | system.devices | linked-device inventory + presence |
| Runtime | system.runtimes | physical runtime/process inventory |
| Actor mount | system.registry | logical mount claims / providers |
Why the address is logical
When a user opens a chat app at COM.NIMBLETEC.RICHARD-SANTOMAURO.chat.$inbox, the address says nothing about which Device or runtime is currently serving chat. That is a feature, not an oversight. The same logical address can be served by:
- the user's laptop today, the user's workstation tomorrow
- a HiveCast-hosted runtime when no Device is online
- a Devices-page-driven failover from one Device to another
- an explicit
--mountoverride during local development
If the address embedded the Device, every move would break every link. So the contract is:
text
Logical address (the actor) != management path (the Device)
chat != system.devices.<deviceSlug>.chat
system.inference.openai != system.devices.<deviceSlug>.system.inferencesystem.devices.<deviceSlug> may exist as a management projection (e.g. for "open the Devices page for this device"). It must not become the canonical actor namespace for everything running on that Device.
What system.devices exposes about runtimes
A Device record carries a runtimes map. Each entry is an IDeviceRuntimeRecord:
ts
// projects/matrix-3/packages/system/src/SystemDevicesActor.ts:9-25
interface IDeviceRuntimeRecord {
runtimeId: string;
runtimeWireRoot?: string;
runtimeMount?: string;
controlMount?: string;
packageRef?: string;
packageDir?: string;
environmentName?: string;
app?: string;
status: string;
mountCount?: number;
localMounts?: readonly string[];
webapp?: Record<string, unknown>;
registeredAt: number;
updatedAt: number;
metadata: Record<string, unknown>;
}This is enough to render a Devices page row that shows: which packages this Device is serving, what their logical mounts are, what HTTP webapp surface they expose. It is not enough to invoke the runtime directly — invocation goes through the logical address (registry.resolve then bus call).
How runtime records get into a Device
host.control posts heartbeats. The flow lives in HostControlActor._publishDeviceHeartbeat() and runtimeHeartbeatPayload():
ts
// projects/matrix-3/packages/host-control/src/HostControlActor.ts:300-313
for (const runtime of runtimes) {
const runtimeId = readString(runtime.runtimeId);
if (!runtimeId) continue;
await RequestReply.execute(
this.context,
'system.devices',
'devices.runtimes.register',
runtimeHeartbeatPayload(link, runtime),
{ timeoutMs: DEVICE_HEARTBEAT_TIMEOUT_MS },
);
}For each live runtime under the Host, host.control calls devices.runtimes.register on system.devices with a payload derived from the durable runtime record (<host-home>/runtimes/<runtime-id>/runtime.json) plus the link metadata. system.devices re-keys the runtime under the correct Device record by principalId + authorityRoot scope.
devices.runtimes.list reads them back, scoped to the same principal/Space.
What the runtime record does NOT contain
The IDeviceRuntimeRecord deliberately omits:
- the runtime's PID, log file path, or supervisor signal handles (those are Host-local and live in
host.status.json/ supervisor RPC) - the package's source path on the Device (that is
packageDir, but only when the heartbeat populates it) - credentials, tokens, or secret material
Anything secret never travels in system.devices payloads. The heartbeat does carry metadata.cloudUrl and metadata.deviceRegistryRoot, but those are non-secret addressing fields.
Filtering rule: runtime wire root must match Host Link
host.control filters which runtimes it advertises to the Devices facade:
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;
});
}Only runtimes whose runtimeWireRoot matches the Host Link's authorityRoot are advertised. A Host that hosts runtimes for two different authority roots (rare, but possible during dev) only advertises runtimes for the linked root through the heartbeat path.
See also
- Device model — the Device side of the relationship.
- Inventory / Runtime visibility — how runtime data is surfaced to UI.
- Reference / Device actor ops — full signature of
devices.runtimes.register/devices.runtimes.list.
Source:
projects/matrix-3/packages/system/src/SystemDevicesActor.ts(runtime record shape and ops);projects/matrix-3/packages/host-control/src/HostControlActor.ts(heartbeat + filter logic). The architectural rule comes fromWORKSTREAMS/product-launch/HIVECAST-DEVICE-ENROLLMENT-SPEC.md§ "Ontology Contract".