Skip to content

Token schema

This page enumerates the four token shapes used in this system: session JWT, bus JWT, heartbeat bearer, and NATS user JWT.

Session JWT (IHostSessionClaims)

Source: projects/matrix-3/packages/system-auth/src/host-auth.ts:12-21.

ts
interface IHostSessionClaims {
  readonly sub: string;          // principalId, e.g. 'p_<sha20>'
  readonly email?: string;
  readonly displayName?: string;
  readonly iss: string;          // configured issuer (e.g. 'hivecast.ai')
  readonly iat: number;          // unix seconds
  readonly exp: number;          // iat + SESSION_DURATION_MS / 1000
  readonly jti: string;          // randomBytes(16).toString('hex')
  readonly purpose?: 'bus';      // present iff this is a bus token
}

Encoded as a compact JWS:

base64url(header) . base64url(payload) . base64url(HMAC-SHA256(header.payload))

Header:

json
{ "alg": "HS256", "typ": "JWT" }

Constants:

ConstantValueUse
SESSION_DURATION_MS7 * 24 * 60 * 60 * 1000 (7 days)session cookie token
BUS_TOKEN_DURATION_MS5 * 60 * 1000 (5 min)bus auth token
SESSION_COOKIE_NAME'mx_session'cookie name
Set-Cookie: mx_session=<JWT>; HttpOnly; Path=/; Expires=<UTC>; SameSite=Lax; [Secure]

Secure is added in production over HTTPS. HttpOnly defends against XSS. SameSite=Lax defends against most CSRF.

Bus token

Same shape as session JWT, with purpose: 'bus' and 5-minute TTL. Used by browser NATS clients as the auth token in the WebSocket handshake. NATS hub maps the JWT (verified against the principal's account) to the principal's account permissions.

Heartbeat bearer token

Source: host-auth.ts:1691-1697.

hlink_<base64url(32 random bytes)>

Length: ~50 characters total. Stored cloud-side as heartbeatTokenHash = base64url(sha256(token)). Verified via safeEqualStrings (constant-time comparison).

The bearer carries no claims — it's an opaque random string. Identity binding is via the cloud-side Host Link record indexed by token hash.

NATS user JWT

Format: standard NATS user JWT (per NATS operator/JWT mode).

Issued by system-auth via @open-matrix/nats-auth helpers (scopedJwtCredentialsFromAccountSeed, rootScopedNatsUserPermissions). Scoped under the principal's NATS account; subject permissions cover <authority-root>.>.

TTL: DEVICE_NATS_CREDENTIAL_TTL_SECONDS = 86_400 (24 hours).

Files written on the Device:

<host-home>/credentials/hivecast-nats.json   (mode 0o600)
<host-home>/credentials.json                  (mirror, used by Host's NATS client)

The file shape (IHiveCastNatsCredentials):

ts
{
  jwt?: string,           // the user JWT
  seed?: string,          // the user's nkey seed
  accountSeed?: string,   // (legacy) account seed; new format does NOT include this
}

New code does not populate accountSeed; it stays cloud-side. Older records may have it.

Token validation summary

TokenValidated byMechanism
session JWTHostSessionService.validateSession()HMAC-SHA256 against the session secret; iss + exp checks; revoked-jti check
bus JWTHostSessionService.validateSession() (same)same; purpose: 'bus' is informational
heartbeat bearerHostLinkStore.verifyHeartbeatToken()SHA-256 hash compare; status === 'active' check
NATS user JWTNATS server (operator/JWT mode)nkey-signed; account permissions

Token issuance summary

TokenIssued byWhen
session JWTHostSessionService.createSession()After successful OIDC sign-in
bus JWTHostSessionService.createBusToken()Browser requests via gateway HTTP
heartbeat bearerHostLinkStore.rotateHeartbeatToken()At pairing exchange + on credential rotation
NATS user JWTsystem-auth via nats-auth helpersAt pairing exchange + on auth.hostLink.credentials.refresh

Revoked sessions

revokedSessions[] in HostAuthStateStore:

ts
Array<{
  jti: string;
  principalId: string;
  expiresAt: number;
}>

HostSessionService.validateSession() rejects any JWT whose jti is in this list. Entries are pruned automatically once expiresAt passes.

See also

Source: projects/matrix-3/packages/system-auth/src/host-auth.ts (IHostSessionClaims at lines 12-21, HostSessionService._createToken at lines 287-308, heartbeat token helpers at lines 1691-1703).