examples/routing/parent-connect.) The parent is an orders flow; it has a notify
child (one instance, wired at boot) and a fulfillment child (one instance per order, created
at runtime).
Why crossing a boundary needs an address
Inside a flow, an event just goes to its subscribers. Across a boundary it is not that simple, and the reason is multiplicity: a flow is not a single recipient. A template flow can have thousands of live instances (one per order, each owning its one entity). So “sendfulfillment.completed to the orders flow” is ambiguous: which order’s instance?
Every cross-flow hop therefore has to answer one question — which instance the event is
for; the instance’s entity comes with it. The connect edge, the parent route, and a receiver-side select_entity are
just different ways to answer it. That question is the whole reason wiring across flows takes more
than a subscription, and it is worth keeping in mind as you read the rest of this page.
Declare the children
A parent lists its children inpackage.yaml, each with a mode:
orders/package.yaml
flows/{child}/ with its own full package. Which mode to use is the
first design decision:
Declare the pins
Pins are a flow’s public interface, declared in itsschema.yaml. An event a flow accepts from
outside is an input event pin; an event it sends outward is an output event pin. Entity
fields it exposes are data pins (reads and writes). Everything not listed is private.
notify/schema.yaml
fulfillment/schema.yaml
orders/schema.yaml
Pattern 1: the connect block — every edge declared
Cross-flow delivery has exactly one mechanism: the package root’s connect: block binds
output pins to input pins, edge by edge. Pin addresses are <flow-id>.<pin-name>; the root
flow’s own pins use a leading dot (.pin-name). A pin declared in the short form is named
after its event — outputs: events: [vendor.assessed] creates a pin addressed
evaluation.vendor.assessed (flow id, then the dotted event name; only the leading dot
means root). The long form’s name: overrides that:
package.yaml
swarm verify checks each
edge at boot, so a broken wire is an error you see immediately rather than an event that
quietly goes nowhere:
- a
fromwith no producer - a
towith no pin - an event mismatch across an edge
- an output pin bound nowhere
resolution + carries decide which instance
handles it (next pattern).
Pattern 2: spawn a template instance
Two ways to get an instance: mint it explicitly with
create_flow_instance (below), or let
the receiving pin mint it on first event — often simpler; see Addressing an existing
instance.create_flow_instance action, passing data in through config_from (there is no shared memory
between flows; data crosses through the payload or config, never through direct entity reads):
orders/nodes.yaml
create_flow_instance does:
- validates the template and builds the instance path
- registers the instance’s nodes and agents
- records a parent route — a standing return address so the child’s output events come back here
- creates the child’s entity at its
initial_stateand starts it
auto_emit_on_create in its schema to fire its first event
from that config.
instance_id_from must resolve to an id that is unique per instance you intend to create.
Creating an instance whose id already exists fails closed (the handler errors rather than reusing
or overwriting the live instance), so derive the id from a stable business key like the order id.
Addressing an existing instance. The receiver owns instance selection, not the emitting
handler. The child’s input pin declares how an arriving event finds its instance — by the
identity field the pin carries:
fulfillment/schema.yaml
fulfillment.expedite through its output pin and the connect edge; the
carried order_id does the addressing.
The return path
The child has its own state and its own entity; when it finishes, it sends a result back as an output pin event:fulfillment/nodes.yaml
orders flow, which holds many orders, not
at a specific one. It does that with select_entity, using the key the child carried in the
payload:
orders/nodes.yaml
create_flow_instance and config_from; the
child works in its own state machine; the child emits an output pin carrying the business key;
the parent route delivers it back; the parent re-selects its entity by that key and continues.
Ownership rules to keep straight
- Each flow owns its own entity. A receiving handler must say how it acquires one:
create_entity,select_entity, orselect_or_create_entity. State is flow-local; nothing reads another flow’s entity directly. - Data crosses only through payloads and
config_from. Cross-flow entity reads are prohibited; carry what the other side needs in the event. - One writer per data pin. Two flows may not both write the same entity field (a boot error).
Addressing reference
When you name an event or flow, it resolves by whether it contains a slash:- Local:
order.paid, an event in the current flow. - Absolute:
intake/ticket.ready, navigated from the root. - Wildcard:
fulfillment/*/fulfillment.completedmatches any direct instance;**matches any depth. Wildcards expand to include new dynamic instances as they are created, which is how a parent subscribes to results from instances that did not exist at boot.
Policy inheritance
Child flows inherit parent and root policy and override specific keys. See Policy.Retired spellings. Older bundles may contain these; all fail at load or verify:
scoped subscriptions (
validation/mailbox.review_requested) and cross-flow wildcards,
which fail verify with legacy_qualified_subscription; producer-side
target: { instance_id } / target: { flow, match }; and target: sender for
request/reply.Events and routing
The full routing model: targets, the parent route, wildcards, and the event envelope.
Flows and entities
Flows, instances, entities, and pins as a public interface.

