Appearance
Capability tokens
The platform issues five distinct token types. Each has a different audience, lifetime, and revocation path. Confusing them is a security bug; this page enumerates them.
1. mx_session — HMAC JWT, 7 days
The browser session token.
| Property | Value |
|---|---|
| Algorithm | HMAC-SHA256 |
| Issuer | matrix-host:<authorityRoot> |
| Subject | principalId |
| Lifetime | 7 days |
| Storage | HttpOnly cookie |
| Revocation | by jti into HostAuthStateStore.revokedSessions[] |
| Source | HostSessionService.createSession (host-auth.ts:215-217) |
Used for: HTTP requests with a session cookie.
NOT used for: NATS bus traffic (browsers use a Bus token instead).
2. Bus token — short-lived JWT, 5 minutes
A short-lived token a browser uses to authenticate to NATS over the WebSocket gateway.
| Property | Value |
|---|---|
| Algorithm | HMAC-SHA256 (same secret as mx_session) |
| Subject | principalId |
| Lifetime | BUS_TOKEN_DURATION_MS = 5 * 60 * 1000 — 5 minutes |
| Storage | memory only in the browser |
| Refresh | every ~4 minutes from /api/nats-jwt |
| Revocation | not separately revoked (lifetime is the bound) |
| Source | HostSessionService.createBusToken (host-auth.ts:219-221) |
Used for: browser-to-NATS WebSocket auth. Per CLAUDE.md Rule 7, browsers connect via same-origin /nats-ws.
The Bus token's short lifetime is the security property — even if leaked, exposure window is ≤5 minutes.
3. Heartbeat token — opaque random, rotates per refresh
A Device's bearer token for the platform's heartbeat endpoint.
| Property | Value |
|---|---|
| Algorithm | random bytes; HMAC at rest |
| Lifetime | rotates per credential refresh; explicit rotation any time |
| Storage | plaintext on the Device only |
| Revocation | implicit on link.status = 'revoked' |
| Source | HostLinkStore.rotateHeartbeatToken |
Used for: /_auth/host-link/heartbeat and /_auth/host-link/credentials/refresh.
4. NATS user JWT — per-Device
A NATS user JWT signed by the principal's NATS account, scoped to that Device.
| Property | Value |
|---|---|
| Algorithm | Ed25519 NKey signed |
| Issuer | the principal NATS account |
| Subject | the Device's NATS user public key |
| Lifetime | DEVICE_NATS_CREDENTIAL_TTL_SECONDS = 86_400 — 24 hours |
| Storage | plaintext on the Device |
| Refresh | auth.hostLink.credentials.refresh |
| Revocation | account JWT revocation list update |
| Source | createDeviceScopedNatsCredentials (system-auth/src/index.ts:1487-1497) |
Used for: NATS bus traffic from the paired Device. Authenticates the Device's NATS connection via NKey challenge-response (the JWT alone is not a bearer token).
The 24-hour TTL plus daily refresh is the principal rotation cadence. A Device that doesn't refresh for 24 hours loses its bus connection.
5. NATS account JWT — per-principal, server-side
The principal's NATS account JWT. Signed by the platform's NATS operator key.
| Property | Value |
|---|---|
| Algorithm | Ed25519 NKey signed |
| Subject | account public key |
| Lifetime | long-lived |
| Storage | server-side only (in HostCredentialStore) |
| Revocation | rotate the account; manage user revocations within |
| Source | ensurePrincipalNatsAccount (system-auth/src/index.ts:1466-1485) |
Used for: signing per-Device user JWTs. The principal account seed never leaves the server. Per HIVECAST-DEVICE-ENROLLMENT-SPEC.md:
The principal account seed remains server-side for NATS account provisioning.
Operators who suspect compromise of a principal account can rotate the account JWT, which invalidates every Device's user JWT signed under it.
Constant-time compare
Heartbeat-token verification (auth.hostLink.verifyHeartbeat) uses constant-time HMAC compare to avoid timing oracle attacks. Session JWT verification likewise.
What about CSRF tokens
The platform does not issue separate CSRF tokens. Mitigation today:
SameSite=Laxonmx_sessioncookie.- Bus tokens are memory-only and not in the cookie jar.
- Most write paths require a non-cookie credential (the Bus token is sent as a NATS auth payload, not as a cookie).
A target hardening: explicit Origin header validation on POST/PUT/DELETE HTTP routes.
Token cheat-sheet
| Layer | Token | Lifetime | Where it lives | Revocation |
|---|---|---|---|---|
| Browser → HTTP | mx_session | 7d | HttpOnly cookie | revokedSessions[] |
| Browser → NATS | Bus token | 5m | memory | lifetime |
| Device → HTTP | heartbeat token | rotates | local file | link.status |
| Device → NATS | user JWT | 24h | local file | account JWT revocation |
| Server-side | account JWT | long | platform DB | rotate |
See also
- Authentication — issuance flows.
- Authorization — what a holder can do.
- Devices / Device health — refresh protocol.
- Secrets — where secrets live.
Source:
projects/matrix-3/packages/system-auth/src/host-auth.ts:196-380for the session service.projects/matrix-3/packages/system-auth/src/index.ts:1466-1497for NATS account/user issuance.projects/matrix-3/packages/nats-auth/for the NATS NKey/JWT primitives.