Event Contracts¶
This document defines the canonical Mozaiks event contract.
Use this for generated apps, module contracts, workflow triggers, UI primitive events, hosted capability packs, and runtime transport.
Canonical Envelope¶
Every durable or cross-layer event must normalize to this shape before it is routed outside its owner:
id: evt_01H...
type: domain.tasks.task_created
version: 1
occurred_at: "2026-04-23T18:00:00Z"
source:
layer: module
app_id: app_123
module_id: tasks
workflow_id: null
capability_id: tasks.create
subject:
type: task
id: task_123
actor:
type: user
id: user_123
tenant:
app_id: app_123
tenant_id: tenant_123
correlation:
correlation_id: corr_123
causation_id: evt_previous
payload:
task_id: task_123
title: Draft contract
visibility: internal
Required Fields¶
| Field | Required | Notes |
|---|---|---|
id | yes | globally unique event id |
type | yes | namespaced event type |
version | yes | integer schema version for this event type |
occurred_at | yes | ISO timestamp |
source.layer | yes | module, workflow, runtime, platform, hosted, or ui |
tenant.app_id | yes | app scope |
payload | yes | JSON-serializable event data |
Optional fields should be present when known: subject, actor, correlation, visibility, and owner-specific source fields.
Frontend transport note¶
The canonical event contract is about ownership and meaning, not one exact frontend payload class.
Current frontend transport uses two distinct shapes:
- runtime/websocket envelopes such as
chat.textandchat.tool_call - direct typed envelopes such as
ui.datatable.refresh
The transport shape may differ from the canonical durable envelope, but the namespace ownership rules still apply.
Namespace Rules¶
domain.*¶
Owner: module or app backend.
Allowed publishers: module handlers and external app backends after deterministic state commits.
Allowed subscribers: platform host, module subscriptions, notification service, workflow trigger resolver, external integrations.
Allowed in generic modules: yes.
Hosted-only: no.
Example:
workflow.*¶
Owner: workflow runtime.
Allowed publishers: workflow runtime and workflow tools.
Allowed subscribers: runtime observers, workflow UI, platform host telemetry, workflow-local automation.
Allowed in generic modules: no.
Hosted-only: no.
Example:
runtime.*¶
Owner: runtime substrate.
Allowed publishers: runtime internals only.
Allowed subscribers: runtime handlers, platform host observers, internal orchestration coordinators.
Allowed in generic modules: no.
Hosted-only: no.
Example:
chat.*¶
Owner: runtime transport.
Allowed publishers: runtime transport and AG2 stream handlers.
Allowed subscribers: frontend chat UI and runtime observers.
Allowed in generic modules: no.
Hosted-only: no.
Example:
artifact.*¶
Owner: runtime or generator workflow.
Allowed publishers: runtime artifact manager, generator workflows, promotion/build services.
Allowed subscribers: Studio, artifact panels, build lifecycle services.
Allowed in generic modules: only when the module owns an artifact domain.
Hosted-only: no.
Example:
ui.*¶
Owner: app UI contract.
Allowed publishers: workflow tools, page actions, platform host UI bridge.
Allowed subscribers: browser primitives and shell components.
Allowed in generic modules: modules may declare UI affordances, but module backend handlers should not depend on UI events for correctness.
Hosted-only: no.
Example:
Current frontend delivery:
- typed websocket envelope with
type: ui.datatable.refresh - payload scoped by
component_idormodal_id - consumed by
chat-ui/src/ui/hooks/useAppEventBus.js
These are primitive update commands, not workflow artifact surfaces. Persistent page launches of workflow sessions are not ui.* events either; they use declarative page actions with action_type: workflow.
notification.*¶
Owner: platform notification service.
Allowed publishers: notification service after deriving notification lifecycle from domain events.
Allowed subscribers: notification UI, delivery adapters, audit/observability.
Allowed in generic modules: modules declare notification rules in notifications.yaml; they do not publish notification.* directly.
Hosted-only: no.
Example:
platform.*¶
Owner: platform/product layer.
Allowed publishers: hosted product workspaces, platform host services, and Mozaiks product services.
Allowed subscribers: platform host, Studio, product analytics, hosted services.
Allowed in generic modules: no.
Hosted-only: sometimes. Hosted product modules may use this namespace; generated generic apps should prefer domain.*.
Example:
hosted.*¶
Owner: hosted capability packs.
Allowed publishers: hosted-only product services such as MozaiksPay.
Allowed subscribers: hosted platform services and product workflows.
Allowed in generic modules: no.
Hosted-only: yes.
Example:
Hosted product examples:
Module Event Files¶
events.yaml¶
Declares events the module may publish.
schema_version: mozaiks.events.v1
events:
- type: domain.tasks.task_created
version: 1
producer: tasks
subject:
type: task
id_path: payload.task_id
payload_schema:
type: object
required: [task_id, title]
properties:
task_id: { type: string }
title: { type: string }
subscriptions.yaml¶
Declares module-owned reactions.
schema_version: mozaiks.subscriptions.v1
subscriptions:
- id: task_created_notify_owner
event_type: domain.tasks.task_created
target:
kind: notification
notification_id: task_created
Targets may be:
handlercapabilitynotification
Current platform support:
notificationtargets are active. The platform host creates a notification intent and emitsnotification.created.capabilitytargets are active. The platform host resolvestarget.capability_idagainst workfloworchestrator.yamltrigger declarations, creates the routed workflow session, and emitsplatform.workflow_capability_started.handlertargets remain reserved reaction contracts. They must stay platform-routed and must not cause modules to import workflow/runtime internals.
Workflow starts must go through capability resolution or workflow trigger resolution. Do not hardcode workflow internals in module code.
notifications.yaml¶
Declares notification derivation rules.
schema_version: mozaiks.notifications.v1
notifications:
- id: task_created
event_type: domain.tasks.task_created
channels: [in_app]
audience:
roles: [owner, admin]
template:
title: "Task created"
body: "{payload.title}"
orchestrator.yaml¶
Declares workflow trigger policy only.
Validation Rules¶
domain.*events must be declared in the publishing module'sevents.yaml.module.yaml.actions[].emitsmust reference declared event types.notifications.yamlandsubscriptions.yamlmust reference declared or imported event types.- Generic generated modules may not publish
platform.*,hosted.*,runtime.*,workflow.*, orchat.*. - Runtime stream events must not be routed as app-domain facts.
- UI events must not be required for durable state correctness.
- Hosted-only events must not be loaded by runtime-only hosts.
Runtime Mapping¶
Current runtime stream names may use existing transport payloads such as kind: text or type: chat.text. These are transport representations. At the contract boundary they map to the namespace rules above.
The canonical contract is the event envelope and namespace ownership, not a specific Python class.
Current frontend mappings¶
Chat/runtime stream¶
Runtime transport currently maps kind values to websocket event names such as:
text->chat.textrun_complete->chat.run_completetool_call->chat.tool_call
This mapping is built in UnifiedEventDispatcher.build_outbound_event_envelope(...).
Workflow UI tool transport¶
Interactive workflow UI currently travels through chat.tool_call, not through the typed ui.* primitive lane.
Current payload shape:
type: chat.tool_call
data:
tool_call_id: evt_123
corr: evt_123
tool_name: save_plan
component_type: AppWorkbench
workflow_name: AppGenerator
interaction_type: ui_tool
awaiting_response: true
display: artifact
display_type: artifact
payload:
workflow_name: AppGenerator
interaction_type: ui_tool
structured_output: {}
Key rules:
component_typeselects the workflow UI componenttool_call_idis the primary UI interaction identifiercorrremains the compatibility alias for existing consumersworkflow_nameshould be available at the envelope level and insidepayloadinteraction_typedistinguishesui_tool,ui_surface,auto_tool, andinput_requestdisplaycontrols whether the surface iscomposer,inline,artifact, orviewcorr/ event id is the response correlation key- this lane is session-scoped and may persist artifact state for resume
- generic
chat.tool_callevents withoutcomponent_typeare not workflow UI mount requests
The frontend stores render metadata in toolCall message/artifact state, but the transport contract is chat.tool_call.
For the exact runtime-to-frontend lifecycle and the AG-UI/CopilotKit mapping, see Tool Event Lifecycle.
Primitive UI transport¶
Primitive UI events remain direct typed ui.* envelopes:
Those are ingested by the app event bus and routed by component_id or modal_id, not by workflow UI component registry lookup.