Skip to content

Response envelope

The response to a request/reply call is also an MxEnvelope. The actor fills in op: '$reply', echoes the correlationId, and puts the result in payload. The caller, having subscribed on $reply.<cid> before sending, receives this envelope and resolves its await.

The shape

typescript
interface MxResponseEnvelope extends MxEnvelope {
  op: '$reply';
  correlationId: string;     // echoed from the request
  payload: unknown;          // the op's result
}

In practice, every response envelope is just a regular MxEnvelope with op === '$reply' and correlationId set. The transport handles delivery on the reply subject; the actor handler is responsible for populating payload correctly.

Convention: payload shape

Most actor ops follow a { ok: boolean, ...result } convention so callers can quickly distinguish success from failure even when looking at raw bus traffic. Concrete examples from the running code:

typescript
// system.runtimes / runtimes.list (system-runtimes/RuntimeManagerActor.ts:204)
return {
  ok: true,
  count: registered.runtimes.length,
  runtimes: registered.runtimes,
  ...(registered.runtimes.length === 1 ? { runtime: registered.runtimes[0] } : {}),
};

// system.registry / registry.list (ServiceRegistryActor.ts)
return {
  ok: true,
  count: claims.length,
  claims,
};

// system.catalog / catalog.list (SystemCatalogActor.ts:192)
return {
  ok: true,
  count: catalog.length,
  catalog,
  bindings,
  runtimes,
  source: 'system.catalog',
};
FieldConvention
ok: trueThe op succeeded.
ok: falseThe op failed; error: string is also set. See Error envelope.
<domain-specific>Whatever the op returns: list of items, single record, count, etc.

Note: the ok flag is convention, not protocol. Some older ops omit it. New ops should include it. Protocol gateways that expose the op as a typed contract should map ok: false to an error response.

A complete request/reply round-trip

Request:

json
{
  "op": "registry.resolve",
  "payload": { "logicalMount": "chat.conversation" },
  "correlationId": "f12d-7af0",
  "replyTo": "$replies.f12d-7af0"
}

Response:

json
{
  "op": "$reply",
  "correlationId": "f12d-7af0",
  "payload": {
    "ok": true,
    "logicalMount": "chat.conversation",
    "providers": [
      {
        "providerRuntimeId": "rt-chat-dev-01",
        "runtimeWireRoot": "COM.NIMBLETEC.RICHARD-SANTOMAURO",
        "localMount": "chat.conversation",
        "componentId": "chat-conversation-01",
        "status": "live",
        "lastHeartbeatAt": 1714862043221,
        "expiresAt": 1714862073221
      }
    ]
  }
}

Late or duplicate replies

The reply subject is unsubscribed after the first matching response arrives. If the actor publishes a second message for the same correlation, it goes to no subscribers and is dropped. This is by design — duplicate replies are not part of the protocol.

A timed-out caller does not receive any subsequent reply, even if the actor eventually answers. The transport closes the subscription when the timeout fires.

Replies for $introspect

$introspect returns the actor's full declared surface. Concrete shape (from MatrixActor base implementation, paraphrased to match typical output):

json
{
  "op": "$reply",
  "correlationId": "abc-123",
  "payload": {
    "mount": "chat.conversation",
    "canonicalId": "ChatConversationActor:01",
    "description": "Per-conversation chat actor",
    "accepts": {
      "chat.send":  { "description": "Send a message", ... },
      "chat.list":  { "description": "List messages",   ... }
    },
    "emits": {
      "chat.message-added": { "description": "...", "schema": { ... } }
    },
    "subscribes": { ... },
    "children":   [ { "id": "...", "mount": "chat.conversation.42", "type": "..." } ],
    "skills":     { ... },
    "kind":       "service"
  }
}

The full set of fields varies per actor; accepts/emits/subscribes/ children are the most reliable.

Trace context on responses

The actor SHOULD propagate traceId/spanId/parentSpanId from the request to the response so distributed tracing covers the full round-trip. The RequestReply.execute helper handles this automatically when the actor uses framework methods to publish.

See also