Skip to content

Threat model

This page enumerates threats explicitly. Each threat is in-scope or out-of-scope, with the design choice or external assumption that puts it there.

In scope

T1 — Cross-tenant traffic leakage on shared NATS

Threat: principal A subscribes to COM.NIMBLETEC.B.chat.$events and sees principal B's chat traffic.

Defense: NATS account isolation. The HiveCast hub uses operator/JWT mode with one NATS account per principal. Browser JWTs are scoped to the principal's account. system_account is reserved for cross-account service routing only. See projects/matrix-3/packages/docs/content/security/nats-account-isolation.md for the canonical spec.

Status: in active rollout. The historical static-account-with-pub: ">" configuration is the known broken case being replaced.

T2 — Compromised cloud → enumerable provider OAuth

Threat: HiveCast cloud is compromised; attackers gain Anthropic / Codex / Google refresh tokens for every user and use them to call provider APIs as those users.

Defense: provider OAuth tokens never travel cloud-side. The local-loopback OAuth flow keeps them on the user's machine, stored by system.factotum. See no-raw-oauth-tokens-on-local-host.

Residual: the cloud holds Google/GitHub OIDC id_token for HiveCast sign-in, but those tokens authenticate the principal to HiveCast, not to Google's broader services. Blast radius is HiveCast sessions, not arbitrary Google data.

T3 — Compromised Device → cross-Device escalation

Threat: an attacker pwns Device A and tries to act as the principal on Device B (via the bus).

Defense: per-Device credentials. Each Device has its own NATS user JWT scoped to its authorityRoot. Device A's JWT cannot subscribe to Device B's NATS subjects (the NATS account permissions don't extend across Device boundaries beyond what the principal account allows). The heartbeat bearer is also Device-bound; presenting Device A's bearer against verifyHeartbeatToken for Device B's link fails.

Status: per-principal NATS isolation is live in scoped scopedJwtCredentialsFromAccountSeed. Cross-Device subject permissions are inherited from the principal account; cross-Device attenuation is target state for an additional layer of defense.

T4 — Stolen heartbeat token used after Device decommission

Threat: an attacker who saw a heartbeat token continues to post heartbeats after the Device is decommissioned.

Defense: rotate-on-pair (each pairing replaces the prior heartbeat-token-hash) and explicit revoke (auth.hostLink.revoke flips status to revoked, which verifyHeartbeatToken checks before hash comparison).

Residual: between revoke and the operator clicking "Disconnect", a short window exists where the token is still valid. Mitigated by explicit revoke being the operator's responsibility, not implicit.

T5 — Body-vs-bearer identity override

Threat: an attacker presents a valid heartbeat bearer for Device A but sets the body's principalId / authorityRoot to Device B's values, hoping the server uses body fields.

Defense: the cloud uses bearer-derived identity, not body identity. Body fields disagreeing with the verified Host Link result in rejection. This is CLAUDE.md Rule 5 in code form.

Status: P1.17 (device-heartbeat-auth-proof) is the open audit proof gate that asserts this end-to-end with adversarial scenarios A-E.

T6 — Forged session JWT

Threat: an attacker forges an mx_session cookie or Bearer token.

Defense: HMAC-SHA256 with a 32+ byte session secret, rejected by HostSessionService.validateSession() if signature mismatch, issuer mismatch, expired exp, or revoked jti. Replay of revoked sessions is blocked by the revokedSessions[] table.

Residual: if the session secret leaks, all current sessions are forgeable until the secret is rotated. Operator responsibility.

T7 — OIDC id_token forgery / replay

Threat: an attacker submits a forged Google id_token to auth.google.callback.

Defense: verifyGoogleIdToken() (in system-auth/src/google-oidc.ts) validates the signature against Google's JWKS, the iss, the aud (configured clientId), exp, and email_verified. Replay is mitigated by the state/verifier PKCE pair stored in the pending-login store.

T8 — Malicious package installed locally

Threat: a user installs a package that includes an actor designed to siphon data via the bus.

Defense: package install requires the principal's install capability. Bus traffic visible to the malicious actor is limited to its own subject permissions (NATS account isolation). The actor cannot read provider credentials directly because Factotum is the only reader; the lease/materialize discipline limits credential exposure to fully formed HTTP headers.

Residual: anything the principal explicitly grants to the package. Package authorship trust is operator responsibility.

T9 — Weak bus secret on dev hosts

Threat: a developer runs with the default 32-char 'auth.sessionSecret' that ships in samples, leaving sessions trivially forgeable.

Defense: HostSessionService constructor throws if the secret has fewer than 32 characters. Default install runs HostSessionService.generateSecret() to mint a per-install random secret.

Residual: if the secret is committed to git, it leaks. CLAUDE.md Rule 8 ("ALWAYS commit EVERYTHING") is balanced by an exclusion for secrets — .gitignore covers credential files.

Out of scope

OS1 — Host machine compromise (root)

If an attacker has root on the user's Device, they can read <host-home>/credentials/* directly. The product cannot defend against this. Mitigation is OS-level (full-disk encryption, physical security, intrusion detection).

OS2 — Browser extension attacks against the local Host UI

The local http://127.0.0.1:3100/ UI is in scope of any malicious browser extension. Defense is up to the user (only run trusted extensions; sandbox via separate browser profiles).

OS3 — Side-channel attacks on the same machine

Spectre-style cache-timing attacks on a shared CPU. Out of scope; OS / hypervisor responsibility.

OS4 — Compromise of an upstream provider

If Anthropic itself is compromised, the user's Anthropic OAuth token is useful to attackers. Mitigation: provider-side, not Matrix-side.

OS5 — Phishing the user's HiveCast OIDC sign-in

Phishing of Google / GitHub credentials is out of Matrix's control. Defense: the IdPs' MFA, hardware tokens, and user education.

OS6 — Quantum-era key recovery

NATS user JWT keys, session HMAC secrets, and OIDC RSA keys are classically secure. Post-quantum migration is a future industry-wide effort, out of scope here.

Trust assumptions made explicit

  • The user's own browser is trusted.
  • The user's OS is trusted (no kernel rootkit).
  • HiveCast's cloud infrastructure is trusted to keep its own state file confidential and integrity-preserving.
  • The IdP (Google, GitHub, …) correctly verifies the user before issuing the OIDC token.
  • NATS server enforces account-level subject permissions correctly.
  • The HMAC-SHA256 session secret is at least 32 bytes of entropy.

See also

Source: WORKSTREAMS/matrix-web/USER-FLOWS-AND-AUTH-BOUNDARIES.md, WORKSTREAMS/product-launch/HIVECAST-DEVICE-ENROLLMENT-SPEC.md § "Correct Security Model"; projects/matrix-3/packages/system-auth/src/host-auth.ts (HostSessionService, HostLinkStore.verifyHeartbeatToken).