Appearance
Device pairing
Pairing is the act of turning a local Host install into a Device on a HiveCast account. There is exactly one pairing protocol; different surfaces (CLI, local UI, HiveCast UI) start it differently but they all run the same five-step ceremony.
The user-facing terms are Device (the linked compute participant) and Space path (the user's public Space route handle). Internal data-model fields (hostId, installId, routeKey) appear on this reference page because we are explaining the data model, not because they belong in CLI prose or product UI copy.
The five-step ceremony
text
1. start enrollment ─ produce a pending pairing record (deviceCode / pairRequestId)
2. authenticate + approve ─ a signed-in principal approves the pairing
3. redeem on the device ─ the Host exchanges the approved record for a Host Link
4. issue credentials ─ system-auth mints device-scoped NATS creds + heartbeat token
5. register + heartbeat ─ host.control posts presence, runtimes show up in inventoryThis shape is taken directly from HIVECAST-DEVICE-ENROLLMENT-SPEC.md § "Uniform Solution" and § "Backend Protocol". It is the same flow whether the user runs hivecast login --device headless or clicks "Connect to HiveCast" from the local Edge UI.
Two pending-record families
system.auth keeps two stores for in-flight pairings, used by different initiation surfaces:
| Store | Constructor | Initiated from | TTL |
|---|---|---|---|
HostDeviceLinkStore | host-auth.ts:990 | headless / device-code: auth.device.start | 30 min (DEVICE_LINK_DURATION_MS) |
HostPairingStore | host-auth.ts (later) | interactive browser-pair: auth.pair.start | 10 min (PAIRING_DURATION_MS) |
The headless device-code flow gives the user a userCode like ABCD-EFGH that they enter on a separate browser session. The browser-pair flow returns a pairRequestId plus an approvalCode that the Host carries through the local OAuth redirect.
Both flows terminate in the same place: a row in hostLinks[] with status: 'active', principalId, spaceId, authorityRoot, and a freshly allocated deviceSlug.
Anatomy of a successful pairing
After step 3 (redeem), the Host has on disk:
| File | Contents |
|---|---|
<host-home>/credentials/hivecast-link.json | Link record. See reference. |
<host-home>/credentials/hivecast-nats.json | Device-scoped NATS user JWT + seed. |
<host-home>/credentials/hivecast-host-link-token | Bearer token (single line, no JSON) used as Authorization: Bearer <token> for cloud heartbeats. |
<host-home>/credentials.json (mirror) | Symmetric copy of the NATS creds for the Host's own NATS client. |
The cloud has on the system.auth side:
| State | Source |
|---|---|
hostLinks[] row with status: 'active' | HostLinkStore.create() in host-auth.ts:799-859 |
heartbeatTokenHash recorded against the row | HostLinkStore.rotateHeartbeatToken() |
| External identity → principal mapping | HostPrincipalStore.findOrCreate() |
Optional publicNamespace claim | HostNamespaceStore.claim() if the link includes a Space-path |
The five steps mapped to ops
| Step | Headless device-code op | Interactive browser-pair op |
|---|---|---|
| 1. start | auth.device.start | auth.pair.start |
| 2. authenticate (browser) | auth.google.login → auth.google.callback | same |
| 2. approve | auth.device.approve | auth.pair.approve |
| 3. redeem | auth.device.poll → auth.device.exchange | auth.pair.exchange |
| 4. issue credentials | side-effect of exchange: auth.hostLink.credentials.refresh mints a device-scoped NATS user JWT (see system-auth/src/index.ts) | same |
| 5. register | host.control reads hivecast-link.json and posts devices.register + devices.runtimes.register heartbeats | same |
The Host CLI command that drives steps 1+3+local-write side of this flow is hivecast login --device --cloud <url> (headless) or hivecast login --setup-code <code> --cloud <url> (interactive). See the Pairing section for the per-flow detail.
What pairing does NOT mint
Pairing does not give the Device:
- a copy of the principal's account-level NATS account seed
- a HiveCast session cookie or principal session JWT
- an OAuth refresh token from Google/GitHub/Anthropic/Codex
The Device only ever holds device-scoped credentials. See Why hosts do not receive raw OAuth tokens for the security argument; this is a real architectural rule, not a polish detail.
See also
- Pairing / Login — the headless device-code flow.
- Pairing / Device flow — the formal protocol shape used by
auth.device.*. - Pairing / Pairing grant — what
auth.*.exchangereturns. - Operations / Pair — runbook.
Source:
WORKSTREAMS/product-launch/HIVECAST-DEVICE-ENROLLMENT-SPEC.md§ "Uniform Solution" and § "Backend Protocol";projects/matrix-3/packages/system-auth/src/host-auth.ts(HostLinkStore,HostDeviceLinkStore,HostPairingStore);projects/matrix-3/packages/system-auth/src/index.ts(op surface).