Appearance
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.
The Host Link record
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.
| Concept | Field | File | When generated |
|---|---|---|---|
| Install identity | installId | <host-home>/credentials/hivecast-install.json | first hivecast install (idempotent) |
| Link identity | hostId | <host-home>/credentials/hivecast-link.json | first hivecast login --device |
| Device label | hostName | hivecast-link.json | hivecast login --device --device-name <name> |
| Device projection key | deviceSlug | hivecast-link.json | minted 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
heartbeatTokenHashon the link record. - The link is
active(notrevoked). - 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
- Principal identity — the principal a grant binds to.
- Authority roots — the Space scope of a grant.
- Discovery projections —
system.devicesas a projection. WORKSTREAMS/product-launch/HIVECAST-DEVICE-ENROLLMENT-SPEC.md— enrollment spec.