Skip to content

Device grants

A Device grant is a Host Link record — the durable evidence that one linked Device (a HiveCast install) belongs to one principal/Space. The grant authorizes the Device to:

  • heartbeat presence into system.devices
  • register runtimes under that Device id
  • act on the principal's behalf for ops that consult Device identity

Device grants live in system.auth. The full op surface is in projects/matrix-3/packages/system-auth/src/index.ts:237-300.

Verbatim from projects/matrix-3/packages/system-auth/src/host-auth.ts:92:

typescript
export type HostLinkStatus = 'active' | 'revoked';

export interface IHostLinkRecord {
  readonly id: string;                // 'lnk_' prefix
  readonly hostId: string;            // generated by system-auth at link time
  readonly hostName?: string;         // mutable human label ('Richard's MacBook Pro')
  readonly deviceSlug: string;        // stable slug under principal/Space
  readonly natsUserPublicKeys?: readonly string[];
  heartbeatTokenHash?: string;        // hashed bearer for heartbeat
  heartbeatTokenIssuedAt?: string;
  readonly principalId: string;       // owner
  readonly spaceId: string;           // Space scope
  readonly routeKey?: string;         // v1 alias for spacePath
  readonly publicNamespace?: string;  // 'space.<spacePath>'
  readonly authorityRoot: string;     // bus root
  readonly scopes: readonly string[]; // permissions
  status: HostLinkStatus;
  readonly createdAt: string;
  updatedAt: string;
  lastRefreshedAt?: string;
  revokedAt?: string;
}

The id field is lnk_<random>. The hostId is the durable Device identity (per CLAUDE.md § Identity lifecycle). deviceSlug is the stable address-safe management projection key under system.devices.<deviceSlug>.

Three identity concepts. Don't conflate them.

Source: CLAUDE.md § "Identity lifecycle". The repo-level rules emphasize this is mistaken often.

ConceptFieldFileWhen generated
Install identityinstallId<host-home>/credentials/hivecast-install.jsonfirst hivecast install (idempotent)
Link identityhostId<host-home>/credentials/hivecast-link.jsonfirst hivecast login --device
Device labelhostNamehivecast-link.jsonhivecast login --device --device-name <name>
Device projection keydeviceSlughivecast-link.jsonminted by system-auth

installId is durable per local install — survives rsync and reinstalls. hostId only exists after pairing. hostName is mutable. deviceSlug is the stable address-safe slug used in system.devices and Device URLs.

The grant ops

The system.auth ops governing Device grants (verbatim from projects/matrix-3/packages/system-auth/src/index.ts):

typescript
'auth.hostLink.create':              { description: 'Create a host link record', ... },
'auth.hostLink.get':                 { description: 'Get a host link by id', id: 'string' },
'auth.hostLink.list':                { description: 'List host links for a principal/Space', ... },
'auth.hostLink.revoke':              { description: 'Revoke an active host link', id: 'string' },
'auth.hostLink.refresh':             { description: 'Refresh heartbeat token for a host link', id: 'string' },
'auth.hostLink.credentials.refresh': { description: 'Refresh per-Device NATS credentials', id: 'string' },
'auth.hostLink.verifyHeartbeat':     { description: 'Verify a heartbeat token bound to a host link', token: 'string' },

// Device pairing flow
'auth.device.start':    { description: 'Start a device-code pairing session', ... },
'auth.device.status':   { description: 'Get the status of a pairing session', ... },
'auth.device.approve':  { description: 'Approve a pending pairing session', ... },
'auth.device.poll':     { description: 'Poll a pending session (CLI-side)', ... },
'auth.device.exchange': { description: 'Exchange an approved code for a host link', ... },
'auth.device.cancel':   { description: 'Cancel a pending session', ... },

Pairing flow

Source: WORKSTREAMS/product-launch/HIVECAST-DEVICE-ENROLLMENT-SPEC.md (canonical).

1. Local install starts: NATS + Host Service + gateway + Edge.
2. User runs: hivecast login --device --cloud https://hivecast.ai
   → CLI calls auth.device.start, gets userCode + verification URL.
3. User opens browser, signs in with Google, approves the pairing.
4. CLI polls auth.device.poll until status='approved'.
5. CLI calls auth.device.exchange.
   → system.auth creates a Host Link record (status='active'),
     mints heartbeatToken + per-Device NATS credentials,
     returns { hostLinkId, deviceSlug, authorityRoot, ... }.
6. mx-cli persists hivecast-link.json under host-home/credentials.
7. host.control reads the link, filters runtime records to the
   linked authority root, sends devices.heartbeat with the heartbeat token.

Heartbeat token

The heartbeat token is a bearer credential bound to one Host Link. The runtime sends it on every devices.heartbeat call. system.auth verifies it via auth.hostLink.verifyHeartbeat:

  • The token's hash matches heartbeatTokenHash on the link record.
  • The link is active (not revoked).
  • The token has not been rotated past its issue window (heartbeatTokenIssuedAt).

The HTTP gateway (system-gateway-http) plays the verification role for cloud-linked Devices — it calls auth.hostLink.verifyHeartbeat and, if the token is valid, updates system.devices and devices.runtimes.register for the Host.

Scopes

The scopes: readonly string[] array on the link record controls what the Device may do. Today scopes are coarse — the link grants effectively "act on behalf of this principal under this Space". Finer-grained per-mount scoping is target state.

Status: target state, partial. Scope catalog standardization (devices.heartbeat, runtimes.register, etc.) is workstream material. Today most links carry the same broad scope set.

Revocation

auth.hostLink.revoke flips the link to status: 'revoked' and clears the heartbeat token hash. Subsequent verifyHeartbeat calls fail; the Device falls out of system.devices after its existing presence record expires. Re-pairing creates a new link with a new hostId.

Persistent storage

Host Link records are stored in the platform's auth store (hivecast.ai) and in the Host's local credentials directory. They must NOT be hand-edited — the local copy is read-only state derived from the platform record. hivecast logout --revoke-cloud-link clears both.

Browser sessions are not Device grants

A browser session (from /api/auth/login/google) authenticates a principal. It does not create a Host Link. Browser sessions live in session cookies and short-lived JWTs; Device grants live in the Host Link record.

See also