Skip to content

Devices

A Device is a linked compute participant: a laptop, workstation, server, VM, container, phone, or appliance running HiveCast/Matrix and paired to an account. Devices are the unit of compute on the platform — the Host that runs the user's actors.

User-facing language

Per WORKSTREAMS/product-launch/HIVECAST-DEVICE-ENROLLMENT-SPEC.md and CONSTRAINTS.md C13, the product term is "Device." Internal terms (hostId, host.json, installId, Host Service, Host Link, leaf, bundle, account seed) stay out of UI surfaces and end-user docs. This page is operator-facing, so internal terms are referenced for cross-checking against the code, but the platform shell and Edge UI must say "Device."

Device identity, three fields

Per HIVECAST-DEVICE-ENROLLMENT-SPEC.md "Device Identity Fields" and CLAUDE.md:

FieldSourceWhat it identifiesScope
installId<host-home>/credentials/hivecast-install.jsonThe local install (laptop install, container install, server install)Always present, durable per install
hostId<host-home>/credentials/hivecast-link.jsonThe pairing bond between an install and a HiveCast accountPer-link, only after pairing
hostName<host-home>/credentials/hivecast-link.jsonMutable human label ("Richard's Laptop")Mutable
deviceSlugAllocated once by system-auth from hostName, collision-resolvedStable, address-safe management projection keyPer-link, durable across renames

installId is durable per install — it survives rsync between machines and idempotent reinstalls. hostId only exists after pairing. hostName is the mutable label users can change. deviceSlug is the stable management key under system.devices.<deviceSlug>; it does not change when hostName changes.

displayName is principal/user identity only. It is forbidden as a Device label. deviceName is forbidden in new contracts (the CLI keeps a user-facing --device-name flag, but it writes hostName internally).

Where a Device lives in code

The platform stores a Device as an IHostLinkRecord (system-auth/src/host-auth.ts:94-114):

ts
export interface IHostLinkRecord {
  readonly id: string;
  readonly hostId: string;
  readonly hostName?: string;
  readonly deviceSlug: string;
  readonly natsUserPublicKeys?: readonly string[];
  heartbeatTokenHash?: string;
  heartbeatTokenIssuedAt?: string;
  readonly principalId: string;
  readonly spaceId: string;
  readonly routeKey?: string;
  readonly publicNamespace?: string;
  readonly authorityRoot: string;
  readonly scopes: readonly string[];
  status: 'active' | 'revoked';
  readonly createdAt: string;
  updatedAt: string;
  lastRefreshedAt?: string;
  revokedAt?: string;
}

A Host Link is the durable proof that this install (hostId) is bonded to this principal (principalId) inside this Space (spaceId, authorityRoot). It carries the per-Device NATS user public keys (so revocation can target exactly that Device's credentials), the heartbeat token hash, and any granted scopes.

The platform-account-facing facade

system.devices is the product/account-facing linked-Device facade. Per CLAUDE.md and HIVECAST-DEVICE-ENROLLMENT-SPEC.md:

system.devices is an inventory/control facade. It may expose devices.list, devices.get, devices.heartbeat, devices.runtimes.list/register. It must not become the public actor namespace. Actors still mount logically through system.registry as chat, system.inference.openai, director, etc. Logical address != management path.

The Devices page on the platform shell (/apps/web/#dashboard) and Edge both read system.devices.devices.list directly through the bus to render linked Devices. Per P1.43, system.devices is the authority for Device inventory. Any HTTP /api/devices endpoint that may exist is a read-only projection for non-bus-aware external clients (CI smoke tests, third-party uptime monitors); internal substrate consumers always invoke the bus actor.

The Devices facade unifies:

  • Durable Host Links (the records above) — these define "linked Devices."
  • Live runtime presence via host.control heartbeats — these define "online/offline."
  • Runtime inventorydevices.runtimes.list lists runtimes under one Device.

An offline linked Device is still a linked Device. Runtime presence alone does not create a durable linked Device — that requires the pairing approval ceremony.

Pairing model

There are three entry points for pairing, but one backend protocol (per the device enrollment spec). Each entry point starts the same auth.device.* or auth.pair.* flow in system-auth:

  1. Browser-initiated (auth.pair.*). User opens Edge on their Device, clicks "Connect to HiveCast." Edge starts an interactive pair request, opens the browser to hivecast.ai, the user authenticates, approves, and the Device redeems an approval code for credentials.
  2. CLI-initiated headless (auth.device.*). User runs hivecast login --device --cloud https://hivecast.ai. CLI prints a user code; user approves on any signed-in browser; CLI polls and exchanges for credentials.
  3. HiveCast-initiated (auth.device.*). User signed into HiveCast clicks "Connect another device" or "Open on this machine"; HiveCast creates an enrollment, the target Device redeems it.

All three end with the same outcome: a Host Link record on the platform Host, a credential file on the Device, and a heartbeat token the Device uses to phone home.

What pairing returns

The exchange ops (auth.device.exchange, auth.pair.exchange) return:

ts
{
  ok: true,
  status: 'exchanged',
  hostLink: { id, hostId, deviceSlug, principalId, spaceId, authorityRoot, ... },
  credentials: { jwt, seed, ... },   // device-scoped NATS user JWT (24h TTL)
  principalId,
  routeKey,             // v1 alias for spacePath
  publicNamespace,
  spaceId,
  authorityRoot,
}

The Device persists hostLink, credentials, and a heartbeat token. The Device's host.control then sends authenticated heartbeats — auth.hostLink.verifyHeartbeat validates the token and updates lastRefreshedAt.

Credentials are per-Device, not shared

Per CONSTRAINTS.md C7-C8 and Rule 4 (Factotum-only):

  • One principal → one principal-scoped NATS account.
  • Each Device gets its own NATS user JWT, signed by the principal account.
  • The principal account seed stays server-side (system.factotum/system-auth).
  • Revoking one Device revokes only that Device's user JWT, not other Devices.
  • There is no shared broker password.

The credential rotation path is auth.hostLink.credentials.refresh. The Device-side CLI is mx refresh-credentials --home <matrix-home>.

Health: what counts as online

A Device is "online" if its most recent heartbeat is within the heartbeat TTL. Heartbeats arrive at system-gateway-http's /_auth/host-link/heartbeat route, which calls auth.hostLink.verifyHeartbeat. system.devices aggregates that into the Device row's status.

A Device that has no fresh heartbeat is rendered as "offline" but still exists as a linked Device — it does not disappear from the list. Disappearance only happens on explicit revoke.

Target state

  • Renaming a Deviceauth.hostLink.create accepts hostName, but no UI today exposes a rename flow. CLI flag --device-name is the only path.
  • Per-Device scopesIHostLinkRecord.scopes exists; the scope authorization model is target state.
  • Physical-device grouping — multiple installs (e.g., laptop + container on the same laptop) belong to one physical machine but show as separate Devices today.
  • Driver/resource summaries on the Device card — Devices expose drivers and resources via system.drivers / system.resources, but the Device card does not summarize them yet.

See also

Source: WORKSTREAMS/product-launch/HIVECAST-DEVICE-ENROLLMENT-SPEC.md is the SSOT for the Device model. projects/matrix-3/packages/system-auth/src/host-auth.ts:92-157 for the schemas. projects/matrix-3/packages/system-auth/src/index.ts:719-1188 for the ops.