Appearance
Secret rotation
Each credential layer has its own rotation cadence and runbook. This page lists them in order of severity (highest blast radius first).
Layer 1 — HiveCast session secret (auth.sessionSecret)
Blast radius: every session JWT signed by this secret.
Rotation:
- Generate a new secret:
node -e 'console.log(require("crypto").randomBytes(32).toString("hex"))'orHostSessionService.generateSecret(). - Set the new value in the
system.authactor's config. - Restart
system.auth(or perform a config reload if supported). - All existing JWTs become invalid. Users will be redirected to login on their next request.
Trigger:
- Suspected compromise of the secret (e.g. accidentally committed to git).
- Periodic rotation (e.g. quarterly).
- Personnel change with access to the secret.
Cost: all active sessions are invalidated. Users sign in again.
Layer 2 — Principal NATS account seed
Blast radius: every per-Device user JWT issued under this account.
Rotation:
- Mint a new account seed via
@open-matrix/nats-authhelpers. - Re-issue the principal's account JWT with the new seed.
- Update the operator/JWT NATS configuration.
- Re-issue every Device user JWT under the rotated account (each Device must re-pair or run
auth.hostLink.credentials.refresh).
Trigger:
- Compromise of the account seed.
- Operator rotation policy.
Cost: every linked Device for that principal must re-pair before its NATS user JWT works against the rotated account. Plan maintenance window accordingly.
Layer 3 — Device-scoped NATS user JWT
Blast radius: one Device.
Rotation:
bash
matrix invoke system.auth auth.hostLink.credentials.refresh \
'{"hostLinkId":"hostlink_<id>","principalId":"p_<sha20>"}'This mints a fresh user JWT (24h TTL by DEVICE_NATS_CREDENTIAL_TTL_SECONDS) plus a fresh heartbeat token. The Device's local <host-home>/credentials/hivecast-nats.json and <host-home>/credentials/hivecast-host-link-token are rewritten.
The headless / canonical path is to re-pair: hivecast logout --local-only then hivecast login --device. Both produce the same artefact set.
Trigger:
- Routine — every 24 hours (target state: automatic; today: re-pair when needed).
- Suspected token leak.
- Device handoff (e.g. employee reassignment of a corporate VM).
Cost: none for end users; brief NATS reconnect window for the Device.
Layer 4 — Heartbeat bearer token
Blast radius: one Host Link's ability to post heartbeats.
Rotation: rotates with Layer 3. The same auth.hostLink.credentials.refresh op mints a fresh heartbeat token along with the NATS creds.
The cloud stores only the SHA-256 hash; the plaintext is returned exactly once and written to <host-home>/credentials/hivecast-host-link-token (mode 0o600).
Trigger:
- Routine — bound to Layer 3.
- Suspected leak (token in logs, leaked through a debug surface, etc.).
Layer 5 — Local provider OAuth (Anthropic / Codex)
Blast radius: one Device's ability to call one provider's API.
Rotation:
For OAuth providers with refresh tokens:
bash
matrix invoke system.factotum credential.refresh \
'{"provider":"anthropic"}'(Today's credential.refresh is partial — it returns the existing token's expiry. Full refresh-token round-trip is target state. The canonical operational path today is re-running the OAuth flow when a token expires.)
For API-key providers:
- Generate a new key in the provider's console.
matrix invoke system.factotum credential.store {...}with the new key.- Optionally
credential.validatefirst. credential.deletethe old key (or just store the new one — store replaces).- Revoke the old key in the provider's console.
Trigger:
- Provider's normal rotation policy (e.g. annual).
- Suspected leak.
- Periodic policy.
Cross-rotation dependencies
| Rotating | Forces rotation of |
|---|---|
| Layer 1 | (none — sessions just invalidate) |
| Layer 2 | Layer 3 + Layer 4 for every linked Device |
| Layer 3 | Layer 4 (atomic with the same op) |
| Layer 4 | Layer 3 (atomic with the same op) |
| Layer 5 | (none — independent) |
Verification after rotation
After rotating any layer, verify:
bash
# Layer 1: a fresh login still works:
curl -fsS https://hivecast.ai/api/auth/login/google
# expect: 302 redirect to Google
# Layer 2: per-Device NATS still resolves to the principal:
matrix invoke system.auth auth.principal.authorityRoot \
'{"principalId":"p_<sha20>"}'
# expect: { ok: true, root: '...' }
# Layer 3 / 4: the Device heartbeats successfully:
matrix invoke system.devices devices.list \
'{"principalId":"p_<sha20>"}'
# expect: source: 'merged', status: 'online'
# Layer 5: provider call works:
matrix invoke system.factotum credential.validate \
'{"provider":"anthropic","type":"oauth","accessToken":"<paste>"}'
# expect: { ok: true, model: '...' }Audit trail
Today, rotation produces:
- Layer 1: a
system.authconfig-change record (operator-side; not tracked bysystem.authitself). - Layer 2: a write to
HostCredentialStorecredentials[]table. - Layer 3 / 4:
lastRefreshedAtupdated on theIHostLinkRecord. - Layer 5:
factotum.credential-storedevent emitted.
Target state would tag each rotation with an explicit audit log entry including actor, principalId, reason, timestamp. See Auditing / Security logs.
See also
- Credential references — how callers use credentials without holding them.
- Factotum / secrets service — Layer 5 detail.
- docs-devices Operations / Rotate credentials — Layer 3/4 from the Devices side.
Source:
projects/matrix-3/packages/system-auth/src/host-auth.ts(HostSessionService.generateSecret,HostLinkStore.rotateHeartbeatToken,auth.hostLink.credentials.refresh);projects/matrix-3/packages/system-factotum/dist/index.js(credential.refresh,credential.store).