Skip to content

Subject naming reference

A consolidated reference for naming inside Matrix subjects, mounts, ops, and URL paths. Each grammar is given as a regex where one applies.

Wire grammar

{root}.{mount}.$facet
TokenAllowed charactersExamples
{root}regex ^[a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?$COM.NIMBLETEC.RICHARD-SANTOMAURO, space.spc-9c3a417e
{mount}dot-delimited identifiers, lowercasechat.conversation, system.gateway.http
$facetone of $inbox, $events, $exit, $reply$inbox

The root regex is the only one enforced by code today (projects/matrix-3/packages/core/src/transport/NatsTransport.ts:108):

typescript
static readonly ROOT_PATTERN = /^[a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?$/;

Mount paths are checked at the application layer (e.g., system.registry).

Mount path conventions

  • Lowercase dot-separated identifiers.
  • Top-level token identifies the package family: system.*, chat.*, flowpad.*, host.*.
  • Children share the parent prefix: chat.conversation, chat.identity.
  • No spaces, no leading/trailing dots, no >/* (NATS wildcards).
  • Hyphens allowed within a token: system.gateway-http is valid (though dots-and-camelCase is the more common style: system.gateway.http).

Op naming

<area>.<verb>             ← most ops, e.g. registry.resolve
<area>.<noun>.<verb>      ← nested namespaces, e.g. devices.runtimes.list
$<verb>                   ← system commands, e.g. $introspect
$<verb>.<modifier>        ← scoped system commands, e.g. $cognitive.set

Verbs follow REST-ish conventions:

VerbUse
listenumerate, with pagination if returning more than a few
getfetch one record
register / unregisterclaim / release
start / stop / cancellifecycle
add / removemutate a collection
claim / releasereservation semantics
resolvelookup with provider info
searchfilter with multiple criteria
heartbeatkeep-alive
validatecheck without side effect
import / exportdata transfer
refreshrenew a token / reload state

Event naming

<area>.<past-tense-verb>
PatternExamples
<area>.added / .removedruntimes.added, runtimes.removed, devices.added
<area>.registered / .unregisteredregistry.registered, service.deregistered
<area>.heartbeatregistry.heartbeat, devices.heartbeat
<area>.<noun>-changedstate.preferences-changed
<area>.<noun>.<verb>factotum.credential-stored

Past tense for "this happened" events. Imperative is reserved for ops.

URL grammars

appName    /^[a-z0-9][a-z0-9-]*$/
spacePath  /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*$/
routeKey   v1 compatibility alias for spacePath (same shape)

Source: WORKSTREAMS/core-and-packaging/MATRIX-DISCOVERY-METADATA-SPEC.md § "Universal Vocabulary".

Examples:

TypeValidInvalid
appNamechat, flowpad, inference-settingsChat, chat_v2, chat.app, 1chat
spacePathacme.support, alt.stories.ghost-stories.funnyACME.support, acme..support, acme.

Forbidden in URLs

  • The internal mount path (chat.conversation) is NOT a URL segment.
  • The wire root (COM.NIMBLETEC.RICHARD-SANTOMAURO) is NOT a URL segment.
  • appName segments are user-typed handles; mount paths are internal.
  • publicNamespace (space.<spacePath>) is internal — never appears in URLs.

URL shape is /<spacePath>/<appName>/... (or /apps/<appName>/... on local Hosts without a Space). The internal mount appears nowhere.

Reserved facet names

FacetDirectionUse
$inboxinboundactor receives ops here
$eventsoutboundpub/sub events
$reply.<cid>actor → callerrequest/reply correlation
$exitchild → parentlifecycle exit signal
$stateactor → watchersKV last-value updates (handled by JetStream)
$stream.<name>actor → consumersdurable stream (target shape)
$blackboard.<fact>sharedmarketplace facts (target)

See Reserved subjects for the full table.

Field name conventions

Inside payloads, JSON field names use camelCase:

json
{
  "logicalMount": "chat.conversation",
  "providerRuntimeId": "rt-chat-dev-01",
  "heartbeatTtlMs": 30000
}

Snake_case (heartbeat_ttl_ms) is not used.

See also