Appearance
Disconnect / unlink
Disconnecting a Device revokes its Host Link, invalidates its NATS user JWT, and removes it from system.devices.devices.list results (or shows it as revoked, depending on the caller's filter). This page covers what happens, how to invoke it, and how to verify.
Two paths
From the platform UI
Per HIVECAST-DEVICE-ENROLLMENT-SPEC.md Step 7 ("Add device inventory and revocation controls"), the signed-in Devices page exposes a revoke action:
POST /api/devices/:deviceId/revokeLive proof per richard-ui-revoke-proof-20260501-064035:
- A signed-in session revoked its own device through the path.
- The unauthenticated path was denied.
- The issued NATS user credential was rejected afterward.
GET /api/devices/:deviceIdreturneddevice:nullafter revoke.
From CLI
bash
matrix invoke system.auth auth.hostLink.revoke \
'{"hostLinkId":"<id>"}'
# or
matrix invoke system.auth auth.hostLink.revoke \
'{"hostId":"<install-id>","principalId":"p_xxx"}'The op (system-auth/src/index.ts:790-803) accepts hostLinkId, hostId, or principalId (or any combination). It marks the link status: 'revoked', sets revokedAt, and triggers NATS user public-key revocation through the account JWT.
What revocation does
- Flip Host Link status —
status: 'revoked',revokedAt: <now>. - Revoke NATS user public keys — every public key in
link.natsUserPublicKeys[]is added to the principal account JWT's revocation list. The NATS server is signaled to reload, after which the issued device-scoped JWT no longer authenticates. - Clear heartbeat token — the heartbeat token hash is invalidated. Any further heartbeats from the Device are rejected.
- Update
system.devices— the Device row'sstatusflips torevoked. Defaultdevices.listfilters revoked rows;?includeRevoked=trueincludes them.
What revocation does NOT do
- Does not delete local data on the Device. The Device's
<host-home>directory, runtime records, and packages stay. The Device can keep running locally; it simply can no longer publish to or read from the cloud bus. - Does not stop local apps. Edge keeps working on
127.0.0.1. Local apps keep working. - Does not delete the Host Link record. The record stays in storage with
status: 'revoked'for audit purposes. There is no automatic prune. - Does not affect other Devices. Per
CONSTRAINTS.mdC8, revoking one Device does not break others. Each Device's NATS user JWT is independently signed. - Does not unclaim namespace. The principal's namespace claim stays — they can pair a fresh Device under the same
routeKey.
Live revoke verification
bash
# 1. Take a Host Link id from devices.list
matrix invoke system.devices devices.list '{"includeOffline":true}'
# 2. Revoke
matrix invoke system.auth auth.hostLink.revoke '{"hostLinkId":"<id>"}'
# 3. Confirm the credential is now rejected.
# On the affected Device, attempt to send a request:
matrix invoke host.control status '{}' --home <host-home>
# Expect a NATS auth failure or a clean exit with "credentials rejected".
# 4. Confirm the dashboard reflects the change
matrix invoke system.devices devices.list '{"includeRevoked":true}'
# Look for the row with status: 'revoked'.Re-pairing after revoke
A revoked Device can be re-paired. The pair flow creates a new Host Link bonded to the same principal/Space; the old (revoked) link record remains for audit. The Device's local credential file is overwritten with the new JWT/seed.
If the user's intent is "I lost my laptop" — revoke immediately. The lost laptop's credentials are dead. When the user gets a new laptop, they pair fresh.
If the user's intent is "I want to re-pair this same laptop" — they can revoke and re-pair, but it is unnecessary. auth.hostLink.credentials.refresh rotates the JWT without requiring re-pair (see Device health).
Authorization
Revoke is authenticated and principal-bound. Per the spec:
Host Link revoke is authenticated and principal-bound.
Implementations:
- The CLI op requires the calling principal to match the link's
principalId(or to be a platform operator on a loopback connection). - The HTTP route
/api/devices/:deviceId/revokerequires a validmx_sessioncookie for the link's principal.
A Device cannot revoke another principal's Devices. An operator can, by invoking auth.hostLink.revoke directly on the platform Host with the appropriate scope.
Bulk revoke (target)
There is no auth.hostLink.bulkRevoke op. To revoke every Device for a principal (e.g., suspended account):
bash
# List
matrix invoke system.auth auth.hostLink.list '{"principalId":"p_xxx"}'
# For each id, call revoke
for id in <ids>; do
matrix invoke system.auth auth.hostLink.revoke "{\"hostLinkId\":\"$id\"}"
doneA bulk op would be a small addition. Target state.
See also
- Pairing — how Host Links are created.
- Device health — heartbeat protocol and credential refresh.
- Edge / Device Management / Disconnect — Edge-side flow.
- Reference: Actor surfaces — full op list.
Source:
projects/matrix-3/packages/system-auth/src/index.ts:790-803for the op. The HTTP revoke route is inprojects/matrix-3/packages/system-gateway-http/src/host-platform-http-surface.ts. Live proofs are recorded inWORKSTREAMS/product-launch/HIVECAST-DEVICE-ENROLLMENT-SPEC.mdStep 7.