Skip to main content
Events are the only way work moves through Swarm. There is no shared memory and no direct calls between actors: a system node or an agent emits a typed event, the platform persists and routes it, whoever subscribed handles it, and the events they emit carry the work forward. Get this one idea and the rest of the runtime follows from it.

What an event is

Every event has a name and a typed payload, declared in events.yaml:
events.yaml
Whoever emits an event must fill every field its payload declares. Nothing is copied automatically from the triggering event, and there are no defaults; we call this producer-complete. An event also carries runtime-stamped context, such as its source and the run it belongs to, which you read through event.* rather than payload.*. You never author that part.

The lifecycle of an event

Every event goes through the same four steps:
1

Validate

The payload is checked against its events.yaml schema. An invalid event is logged and discarded, never delivered.
2

Persist

The event is written to the log before any processing.
3

Resolve subscribers

The platform finds the system nodes and agents that subscribe to the event.
4

Deliver

Each subscribing node runs its handler; each subscribing agent receives the event in its inbox.
The order is the point: an event is persisted before it is delivered. If the runtime crashes after persisting, the event replays on recovery and nothing is lost.

Routing is derived, not chosen

Here is what makes runs reproducible: no LLM decides who acts next. Who receives an event follows entirely from who declared a subscription to it.
  • A system node subscribes in nodes.yaml: its event_handlers keys are its subscriptions. Subscriptions only match inside one flow — see Crossing flow boundaries below.
  • An agent subscribes in agents.yaml (subscriptions).
events.yaml defines payload shapes, never routing. So you change who receives an event by editing a subscription, never by touching its payload, and there is no router in the loop that could send it somewhere unexpected.

Nodes own, agents observe

Who subscribes also decides what the delivery means: A single event is owned by at most one system node (two is a boot error), which keeps state authority unambiguous. Agents are not limited this way: many can observe the same event, because they only react and never own state.

A worked example

Say a ticket flow has a node and an agent that both care about ticket.created:
nodes.yaml
agents.yaml
Nothing here declares routing; it is inferred from the two subscriptions. When ticket.created fires:
  1. It is validated and persisted.
  2. Subscribers resolve to the node and the agent, so this is dual delivery.
  3. The node advances the ticket to triaging and emits ticket.triaged, in one transaction. The agent independently reasons and later emits ticket.classified.
  4. ticket.triaged and ticket.classified each enter the loop as new events and route to their own subscribers.
To send ticket.created somewhere else, you change which node handles it or which agent subscribes. The payload does not move.

Crossing flow boundaries

Inside one flow, routing is just subscription matching, and that is most of what you write. Between flows there is exactly one mechanism. An event leaves a flow through a declared output pin. A connect line in the package wires that output pin to another flow’s input pin. The receiving pin decides which instance handles it — and can create one on the spot if it doesn’t exist yet. swarm verify checks every edge at boot. The full grammar is in Composing flows.

Composing flows

Pins, connect edges, the parent route, and receiver resolution for cross-flow routing.

System nodes and handlers

What a node does with an event once it is delivered.