Skip to content

SDK vs protocol

Matrix has two layers that authors confuse, and the confusion produces fragile code. The protocol is a wire-format and addressing contract that any client could implement. The SDK is one TypeScript implementation of that protocol — the implementation that ships in this repo.

This page draws the line.

The protocol — what is on the wire

The Matrix protocol is the contract every actor and every transport must obey, regardless of language or runtime. It is defined by spec, not by code:

  • Address grammar. {authorityRoot}.{mount}.$facet — dot-delimited NATS subjects, where $facet is one of $inbox, $events, $reply.{correlationId}, $activity, etc. Authority roots derive from email or Space identity (e.g. COM.NIMBLETEC.RICHARD-SANTOMAURO).
  • Six declarations. Every actor declares accepts, emits, streams, state, subscribes, blackboard. Every op and field has a description. See WORKSTREAMS/core-and-packaging/ACTOR-COMMUNICATION-CONTRACT.md.
  • Wire envelope. Messages are JSON MxEnvelope records with op, payload, optional from, lamport, correlationId, replyTo. The envelope shape is fixed; the payload schema is per-op.
  • System ops. $introspect, $ping, $join/$join_ack/$leave, $getState, $getHistory, $prompt, $activity, $membrane, $cognitive.set, $skill. Reserved names; every actor handles them.
  • Identity from transport metadata. Authenticated identity comes from the NATS connection's user credentials, never from the message payload.

A Python client, a Go bridge, or a hand-rolled WebSocket client can all participate in Matrix as long as they obey this protocol. None of them need to depend on @open-matrix/core.

The SDK — what is in this repo

The Matrix SDK is @open-matrix/core plus a small handful of supporting workspace packages. It is the canonical TypeScript implementation of the protocol, and it is what every Matrix package, every webapp, and the Host Service runner are built on. The SDK provides:

ConcernSDK class / function
Headless actor base classMatrixActor (src/core/MatrixActor.ts)
Browser custom-element wrapperMatrixActorHtmlElement (src/framework/MatrixActorHtmlElement.ts)
Runtime containerMatrixRuntime (src/runtime/MatrixRuntime.ts)
In-process transportInMemoryTransport, InMemoryBroker (src/transport/InMemory*.ts)
NATS transport (Node)NatsTransport, createTransport (src/transport/{NatsTransport,createTransport}.ts)
NATS transport (browser, JWT or token)createBrowserNatsTransport (src/transport/createBrowserNatsTransport.ts)
Activity-stream contractActivityFrame, ActivityPhase, isTerminalPhase (src/framework/ActivityFrame.ts)
Package config resolutionresolveConfiguredPackageConfig, deepMergeConfig (src/runtime/PackageConfigResolver.ts)
Wire typesMxEnvelope, IMatrixContext, ITransportAdapter (engine layer)

The SDK does NOT contain protocol-level identity policy, authority-root derivation, JWT issuance, or HiveCast pairing. Those live elsewhere:

  • Authority-root derivation lives in @open-matrix/system-auth and the host service flow (not in core).
  • JWT issuance for browser NATS is a host-service / system-auth concern. The SDK only consumes JWTs via createBrowserNatsTransport({ jwtProvider }).
  • HiveCast pairing and Device-Link approval is system-auth plus the hivecast wrapper, never core.

What lives where, side by side

ConcernProtocol (spec)SDK (@open-matrix/core)
Subject grammar {root}.{mount}.$facetdefinedimplemented in TopicRouter and NatsTransport
MxEnvelope { op, payload, ... }definedexported as a TS type
accepts / emits / streams / state / subscribes / blackboarddefinedstatic fields on MatrixActor
System ops ($introspect, $ping, $join, ...)reservedauto-subscribed by MatrixActor
Authority-root derivation rulesdefined in MATRIX-AUTHORITY-MODEL.mdnot implemented (lives in system-auth)
Identity from transport metadatamandatorythe from field on incoming envelopes is set by transport, not payload
Browser → NATS via WebSocketmandatory same-origin /nats-wscreateBrowserNatsTransport({ wsUrl })
Federation (cross-root invoke)definedMatrixActor.invoke(rootPath, op, data) delegates to transport.invoke (FederationPeer)

What this means in practice

If you are writing a Matrix package, you depend on @open-matrix/core and you obey the protocol. The SDK enforces most of the protocol mechanically — declaring static accepts produces the right $introspect output; calling this.emit('foo', ...) publishes to the right subject; receiving an envelope on $inbox dispatches to on{Op}.

If you are writing a non-TypeScript bridge (a Python tool, a Go service, an embedded device), you do not need this SDK. You implement the protocol directly: open a NATS connection, publish MxEnvelope JSON to {root}.{mount}.$inbox, subscribe to {root}.{mount}.$events, answer $introspect and $ping. That participant is just as much a Matrix actor as anything in this repo.

See also

Source: projects/matrix-3/packages/core/src/index.ts (public exports), projects/matrix-3/packages/core/src/transport/NatsTransport.ts:107-150 (root validation, prefix grammar), WORKSTREAMS/core-and-packaging/ACTOR-COMMUNICATION-CONTRACT.md (six declarations).