Skip to content

Device records

A Device record is the in-memory entry kept by SystemDevicesActor for each linked compute participant. The actor stores them in this._devices: Map<string, IDeviceRecord> keyed by deviceId. This page is the field-by-field reference for that record and for the IDeviceView shape that devices.list / devices.get actually return.

In-memory record (authoritative)

ts
// projects/matrix-3/packages/system/src/SystemDevicesActor.ts:27-46
interface IDeviceRecord {
  deviceId: string;                          // == hostId
  hostName?: string;
  deviceSlug?: string;
  hostLinkId?: string;
  principalId?: string;
  spaceId?: string;
  linkStatus?: 'active' | 'revoked';
  authorityRoot?: string;
  publicNamespace?: string;
  routeKey?: string;
  status: 'online' | 'offline';              // presence-only
  source: 'presence';                        // always 'presence' in storage
  registeredAt: number;
  updatedAt: number;
  lastHeartbeatAt: number;
  heartbeatTtlMs: number;                    // default 45_000
  metadata: Record<string, unknown>;
  runtimes: Map<string, IDeviceRuntimeRecord>;
}

Notes on the storage shape:

  • runtimes is a Map, not an array. The view flattens it into a sorted array (sortedRuntimes()).
  • status in storage is only online or offline. The linked / revoked view-statuses come from the cloud-side Host Link record, not from in-memory presence.
  • source in storage is always 'presence' because the actor only stores records that have actually heartbeat. Host-link-only records show up in the view via the merge step but are NOT persisted in the map.

Wire view (what devices.list / devices.get returns)

ts
// projects/matrix-3/packages/system/src/SystemDevicesActor.ts:48-53
interface IDeviceView extends Omit<IDeviceRecord, 'runtimes' | 'status' | 'source'> {
  source: 'presence' | 'host-link' | 'merged';
  status: 'online' | 'offline' | 'linked' | 'revoked';
  runtimeCount: number;
  runtimes: IDeviceRuntimeRecord[];          // flattened, sorted
}

Differences from the in-memory record:

  • source widens from 'presence' only to all three values.
  • status widens from two values to four.
  • runtimes is an array, not a Map.
  • runtimeCount is added so UIs can render the count without iterating.

The three sources

The source field on a view tells you where the data came from:

sourceProvenanceWhen it appears
presence_devices map only — record is live but the cloud Host Link is unknown to this actorLocal-owner Hosts that are not linked to a HiveCast cloud account; or when devices.list is called without a principalId
host-linkauth.hostLink.list only — durable record exists but no live presenceLinked Devices that are currently offline; long-tail "I have these Devices" inventory
mergedBoth presence AND host-link agree (same hostLinkId, principalId, spaceId, authorityRoot)Linked Devices that are currently online

The merge logic is in _listHostLinksForPrincipal() and hostLinkDeviceView(). The actor will not merge a presence record with a host-link record that disagrees on any of those four fields — see deviceCanMergeWithHostLink(). This prevents a malicious or buggy heartbeat from "claiming" a host-link it does not own.

Source compaction

devices.list runs the resulting views through compactDeviceViews() to prefer one canonical record per device. The rules:

1. If a record has a stable installId (matches /^install_[a-f0-9]{20}$/),
   it wins for its installId.
2. Records grouped by (authorityRoot, publicNamespace, routeKey) prefer
   one entry per group.
3. The 'preferred' record within a group is the online one; ties break
   on most recent updatedAt.

Why: re-pairing the same install with a new hostId would otherwise show two entries (one revoked Host Link plus the new live one). The compaction keeps the live one and elides the dead namespace-keyed record.

Status semantics in the wire view

statusMeaning
onlineLive presence within heartbeatTtlMs
offlinePresence record exists but heartbeat exceeded TTL
linkedDurable Host Link with status: 'active', no live presence
revokedHost Link status: 'revoked'

devices.list({ includeOffline: false }) excludes offline and revoked views. The default behaviour is includeOffline: false.

Re-keying on devices.register

devices.register is idempotent across re-pairings, with a critical safety check: a record already in memory cannot be carried forward unless the new payload's identity scope agrees with the previous one (deviceRecordCanCarryForward). Specifically:

  • if the previous record has a principalId, the new payload MUST have the same principalId
  • same for spaceId, authorityRoot, hostLinkId, publicNamespace, routeKey

If they don't agree, devices.register returns { ok: false } and the record is left untouched. This is the "ownership cannot be silently mutated" rule in code form.

See also

Source: projects/matrix-3/packages/system/src/SystemDevicesActor.ts (IDeviceRecord at lines 27-46, IDeviceView at lines 48-53, compactDeviceViews() at lines 593-621, deviceRecordCanCarryForward() at lines 510-536).