Appearance
Request envelope
Every message published to a Matrix $inbox is an MxEnvelope. The shape is defined in projects/matrix-3/packages/core/src/engine/messaging/MxEnvelope.ts and it is the canonical request envelope.
The shape (verbatim)
typescript
// projects/matrix-3/packages/core/src/engine/messaging/MxEnvelope.ts
export interface MxEnvelope {
/** Operation name: domain op ("ExpandDir") or system op ("$join", "$reply") */
op: string;
/** The actual message data */
payload: unknown;
/** For contracted interface calls (e.g., "FsDaemon@1") */
iface?: string;
/** For request/reply: unique correlation identifier */
correlationId?: string;
/** For request/reply: reply-to inbox topic */
replyTo?: string;
/** Lamport timestamp for causal ordering */
lamport?: number;
/** Sender mount path */
from?: string;
/** Trace context — propagated across actor messages for distributed tracing */
traceId?: string;
spanId?: string;
parentSpanId?: string;
}Field reference
| Field | Required | Notes |
|---|---|---|
op | yes | Operation name. Domain ops use the convention <area>.<verb> (registry.resolve); system ops are $-prefixed ($introspect, $ping). |
payload | yes | The op's input. JSON-serializable. May be null/{} for ops with no parameters. |
iface | no | The interface contract name when calling a typed contract op (e.g., "FsDaemon@1"). Used by RequestReply.execute for contracted invocations. |
correlationId | yes for request/reply | Unique per request. Used to match the response. Conventionally a short opaque token (UUID, nanoid). See Correlation IDs. |
replyTo | yes for request/reply | The semantic reply subject ($replies.<cid>). The actor publishes its response there. |
lamport | no | Logical clock. Lets the actor reason about message ordering. Increments on every send/receive. |
from | no | Sender's mount path. Useful for logging and tracing. NOT trusted for authorization (per CLAUDE.md Rule 5, identity comes from transport metadata, not envelope payload). |
traceId / spanId / parentSpanId | no | OpenTelemetry-style trace context propagation. |
Concrete example
A request to resolve a logical mount through system.registry:
json
{
"op": "registry.resolve",
"payload": { "logicalMount": "chat.conversation" },
"correlationId": "f12d-7af0-9c3a-417e",
"replyTo": "$replies.f12d-7af0-9c3a-417e",
"from": "system.gateway.http",
"lamport": 4291,
"traceId": "5b8aa5a2d2c872e8321cf37308d69df2",
"spanId": "051581bf3cb55c13",
"parentSpanId": "00f067aa0ba902b7"
}This envelope is published on COM.NIMBLETEC.RICHARD-SANTOMAURO.system.registry.$inbox. The registry actor handles it and publishes its response on COM.NIMBLETEC.RICHARD-SANTOMAURO.$reply.f12d-7af0-9c3a-417e.
Op naming conventions
Source:
WORKSTREAMS/core-and-packaging/MATRIX-DISCOVERY-METADATA-SPEC.mdand observed package code.
| Pattern | Example | Use |
|---|---|---|
<area>.<verb> | registry.resolve, chat.send, inference.start | Domain ops |
<area>.<noun>.<verb> | devices.runtimes.list | Nested namespaces |
$<verb> | $introspect, $ping, $leave | System commands |
$<verb>.<modifier> | $cognitive.set, $skills.add | Scoped system commands |
The op name appears in the actor's static accepts declaration and is what gateway adapters surface as REST POST routes, MCP tool names, and gRPC unary methods.
Fire-and-forget
Not every publish to an inbox is a request/reply call. Pure side-effect ops (e.g., a "log this" notification) may publish without correlationId/ replyTo. The actor handles the message and produces no reply. Most production ops are request/reply, because the caller usually wants to confirm the side effect happened.
$introspect
The most universal op. Every actor handles it without explicit declaration. Default invocation:
json
{
"op": "$introspect",
"payload": {},
"correlationId": "abc-123",
"replyTo": "$replies.abc-123"
}The actor returns:
json
{
"op": "$reply",
"correlationId": "abc-123",
"payload": {
"mount": "chat.conversation",
"canonicalId": "...",
"accepts": { ... },
"emits": { ... },
"subscribes": { ... },
"children": [ ... ]
}
}This is how an LLM agent or a protocol gateway discovers an actor's full surface without prior knowledge.
Note: the
MatrixActorclass declares both'introspect'(no dollar) and'$introspect'as system commands. Both are accepted;$introspectis preferred in new code. SeeMatrixActor.tsline 111.
Wire encoding
The envelope is JSON-serialized by the transport (NatsTransport.encodePayload, jsonMode: true by default — line 132). Empty payloads encode as null. Non-JSON payloads (binary, raw text) are supported when jsonMode: false is set on the transport, but no production code uses that path today.
See also
- Response envelope — the matching reply shape.
- Error envelope — error replies.
- Correlation IDs — how
correlationId/replyTowork together. - Subjects — wire format of
$inbox/$reply.{cid}. - Envelope schemas — full JSON shape.