Skip to content

NATS

NATS is Matrix's native transport. Every actor invocation, event, and state record ultimately publishes or subscribes to a NATS subject. All other protocol adapters (HTTP, WebSocket, MCP, REST/OpenAPI, gRPC, AsyncAPI) are translators that turn their wire shape into NATS publishes and back.

Source: projects/matrix-3/packages/core/src/transport/NatsTransport.ts is the canonical transport implementation. projects/matrix-3/packages/docs/content/architecture/nats-topology.md covers connection-mode trade-offs.

Why NATS

NATS is the only widely-adopted transport that natively supports both request/reply and pub/sub with leaf-node federation. The protocol's guarantees come from there:

  • One subscription pattern, one transport library, one set of ACL rules.
  • Federation across providers via leaf nodes — outbound only, no open inbound ports required on each Host.
  • JetStream for durable streams (static streams) and KV state (static state).
  • Token-bucket QoS, per-subject backpressure, optional account isolation.

Three connection modes

Source: projects/matrix-3/packages/docs/content/architecture/nats-topology.md. Modes are configured in <env>.environment.json.

ModeBehaviorWhen to use
embeddedHost runs its own NATS server. All traffic local.First-run before login; isolated dev/test.
externalHost connects to remote NATS as a WebSocket client. No local server.Container workers; lightweight devices.
colocatedHost connects to a separate local NATS process the user manages.Power users sharing one NATS across multiple Hosts.
Leaf nodeHost runs its own NATS AND attaches it as a leaf to a hub.Heavy local traffic + cross-root federation.

The hub topology (hub.hivecast.ai:7422 for HiveCast, or any NATS hub for self-hosting) is the federation backbone. Subscription interest from leaves propagates to the hub; no per-route lookup table.

Subject mapping (verbatim)

semanticToNats translates Matrix's slash-form semantic topics into dot-form NATS subjects. From projects/matrix-3/packages/core/src/transport/NatsTransport.ts:267:

Format: {root}.{mount}.{$facet}
  system.llm/$inbox  →  com.example.system.llm.$inbox
  $replies.abc-123   →  com.example.$reply.abc-123
  system.llm/#       →  com.example.system.llm.>
  #                  →  com.example.>
  generic.topic      →  com.example.generic.topic

Wildcard mapping:

SemanticNATS
+*
#>

Wire payload encoding

Default jsonMode: true (NatsTransport.ts:132). The transport encodes payloads as JSON via TextEncoder/TextDecoder. Empty payloads encode as null. With jsonMode: false, the transport accepts strings and Uint8Array payloads directly. Production code uses JSON exclusively.

Credentials

NatsTransport accepts NATS user credentials via the credentials option (or user / pass aliases):

typescript
interface INatsTransportOptions {
  root?: string;
  topicPrefix?: string;
  jsonMode?: boolean;
  onError?: (err: Error) => void;
  credentials?: { user: string; pass: string };
  user?: string;
  pass?: string;
  // ...
}

The connection itself must already be established with these credentials; the transport stores them for inspection (transport.credentials).

Browser NATS

Browsers connect through the HTTP gateway's /nats-ws endpoint. The gateway proxies the WebSocket and mints a NATS-scoped JWT for the authenticated user. From the browser's perspective, this is just a NATS client over WebSocket; from the server's perspective, every browser session is a separately-authenticated leaf with subject ACL pinned to the principal's authority root.

Each browser tab registers as a runtime under system.runtimes (e.g., RUNTIME-BROWSER-F81354A7). Director shows browser tabs alongside server-side runtimes.

Reply subject handling

NATS supports protocol-native reply subjects (_INBOX.*). Matrix does NOT use them by default — RequestReply.execute publishes its own {root}.$reply.<cid> form so every subject is root-prefixed and NATS-account-ACL-authorizable. The source comment makes this explicit:

Source: projects/matrix-3/packages/core/src/engine/remoting/RequestReply.ts:318. "Do not use natsClient.request() here: protocol reply inboxes (_INBOX.*) bypass our root-prefixed subjects and break ACL enforcement."

Account model

Per-tenant NATS accounts are the strongest isolation tier. The hub account model (target):

Operator (signs all accounts)
├── AI_HIVECAST                           — platform account
├── COM_NIMBLETEC_RICHARD_SANTOMAURO       — Richard's account
├── COM_GMAIL_RICHARDMATHEWSANTOMAURO       — personal account
├── AI_HIVECAST_DEV                          — dev/CI (broad permissions)
└── SYS                                       — internal NATS

Each account can publish/subscribe only to its own root's subjects. Cross-account communication uses NATS service imports/exports.

Status: target state, partial. Today, the hub uses a single shared account with pub/sub: ">" (full traffic). NATS account isolation for free-tier users is documented as a CRITICAL gap in nats-topology.md. Subject-prefix isolation enforced by the Host runtime is the current production posture.

Backpressure

NATS drops to its native backpressure rules under load. The transport exposes mailbox configuration (in MatrixRuntime) with highWaterMark and onFull: 'drop-oldest' defaults (MatrixRuntime.ts:163). Slow subscribers may miss events; ops design should not rely on fan-out delivery to every subscriber.

See also