Skip to content

Platform Event Listeners

This contract defines the modules → platform direction. The platform exposes listeners for platform capabilities and never registers handlers tied to a particular AI, CRM, mailer or other module.

For example, modules publish delivery.account.send, not ai.sendToWebSocket or another module- or transport-specific name.

Processing model

External module
    → MoleculerBroker::emit(platform_event, JSON)
    → addressed MOL.EVENT.<platform-node> subject
    → Router
    → EventsHandlerList
    → EventHandlerRecord selected by pattern
    → envelope and payload validation
    → execution according to EventExecution
    → platform service or transport queue

Incoming events and outgoing platform events use separate registries. An incoming event is not automatically emitted into the public outbound bridge.

Handler registry

The C++ platform declares inbound listeners separately from action handlers:

enum class EventExecution {
    Inline,
    Async,
    Serial
};

struct EventHandlerRecord {
    std::string pattern;
    std::size_t max_payload_size;
    EventExecution execution;
    std::function<int(
        const rapidjson::Value& payload,
        std::string& error
    )> handler;
};
Field Meaning
pattern Exact Moleculer event name or supported wildcard pattern
max_payload_size Maximum accepted JSON payload size
execution Dispatch policy
handler Event validation and hand-off to a platform component

The integer result and error string are internal. They are used for logs, metrics and tests, but are not returned to the event publisher.

Execution policies

Policy Use
Inline Short validation, copying and enqueueing only; no blocking work
Async Independent work executed by a bounded worker pool
Serial Work whose ordering must be preserved for an agreed key

The initial delivery listeners are Inline: they validate, format and enqueue the message into an existing transport. Async and Serial are reserved extension policies until a listener requires them.

Common delivery request

The first two listeners share this request schema:

{
  "event_id": "0192f7d7-8bd5-7e1a-b6cb-0f1e37d1ad11",
  "schema_version": 1,
  "created_at": 1789080000,
  "target": {
    "id": 10001
  },
  "message": {
    "type": "ai.run.delta",
    "payload": {
      "run_id": "run-123",
      "delta": "Generated text"
    }
  }
}

Validation rules:

Field Rule
event_id Required non-empty string, maximum 128 bytes
schema_version Required integer; currently 1
created_at Required positive signed 64-bit Unix timestamp
target.id Required positive integer
message.type Required non-empty string, maximum 128 bytes
message.payload Required JSON object
complete payload Maximum 256 KiB for the initial listeners

Modules send application data, not a pre-serialised WS or TCP frame. The platform owns the client transport format.

Client message

Both initial listeners format the request into the same client object:

{
  "event": "module:event",
  "type": "ai.run.delta",
  "event_id": "0192f7d7-8bd5-7e1a-b6cb-0f1e37d1ad11",
  "schema_version": 1,
  "created_at": 1789080000,
  "data": {
    "run_id": "run-123",
    "delta": "Generated text"
  }
}

The transport is selected by the platform listener, not by a field supplied by the module. This allows the platform to change how a recipient connects without changing the module API.

Available listeners

Event target.id Current internal route Status
delivery.account.send Account login Core::OnSendWs Implemented
delivery.manager.send Manager ID Core::OnSendTcp Implemented

Error semantics

Events do not return errors to the publisher. The current dispatcher records validation, payload-limit and handler failures internally:

  • malformed envelope;
  • unsupported schema version;
  • payload above the configured limit;
  • formatter or handler exception.

Detection and metrics for a missing or inactive target and transport queue overflow are required follow-up work. The current Core::OnSendWs and Core::OnSendTcp boundary does not return those outcomes to the event handler.

Diagnostic logs should include the event pattern and error category and, where safely available, event_id, target ID and message type. They must not include the complete message.payload.

The current transport methods return no usable delivery outcome to the event handler. Therefore neither hand-off nor delivery to a connected client is acknowledged to the module. If a module requires confirmation, it must use an action or a separately agreed delivery-receipt protocol.