Files
hq/02-DECISIONS/0046-events-are-a-relationship.md
T
jschoubben 4bb6dd26ed ADR 0046 — events are a relationship, provisioning's lighter sibling
A module emits and consumes events, both declared (emits/consumes),
parallel to provides/requires. Events are 1:many, broadcast, credential-
free — no provisioner, just the broker's topic routing — so most inter-
module reaction should be an event, not a provision. Every event carries
source/node/time so it is auditable; the audit logger is just a module
consuming '#', no privilege. A consumes for an event nothing emits is a
dangling edge and refused, like requires. One per-node runtime serves
tools, provisioning and events alike.

Extends ADR 0045; builds on ADR 0001 (the broker) and 0044 (emit/on are
stable sdk surface; the binding and runtime are not).

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-03 23:42:25 +02:00

4.2 KiB

status, date, deciders, reconstructed, extends
status date deciders reconstructed extends
accepted 2026-09-03 jochen false 0045-what-a-module-is.md

46. Events are a relationship, the lighter sibling of provisioning

Context

ADR 0045 names two relationships between modules — seats and provide/require (provisioning). A third is latent in the mesh and worth making first-class: the broker every node already runs (ADR 0001) can carry a module's activity as events, which any other module reacts to. A logger that writes an audit trail, a module that acts when another module acts, observability — all of it is one mechanism, and today it is ambient rather than declared.

Decision

A module emits events and consumes events, and both are declared — parallel to provides / requires, so the mesh knows the event graph the same way it knows the provisioning graph.

Events are provisioning's lighter sibling

provisioning events
shape 1:1, a provider creates a resource for one consumer 1:many, a module emits, any number listen
credential yes — sealed, per consumer none — it is broadcast
machinery a provisioner (the reconcile adapter) nothing but the broker's topic routing
declared as provides / requires emits / consumes

Because an event is broadcast and credential-free, there is no provisioner and no per-consumer setup — only a subscription. That is why it is the lighter relationship, and why most inter-module reaction should be an event, not a provision.

An event carries what an audit needs

Every event carries its type (a dotted topic key, so listeners match by prefix), its source module, the node it came from, and the time. A body follows. The metadata is not optional: a reaction may only need the body, but an audit trail needs to know who did what, where and when, and an event that cannot answer that is not auditable.

The audit logger is just a consumer of everything

A logger that records the whole mesh's activity is not a privileged component — it is an ordinary module that consumes # (every event) and writes them down. It holds no special access; it only listens widely. That it falls out of the model with no new machinery is the check that the model is right.

consumes is validated like requires

A consumes for an event that nothing emits is a dangling edge, and the mesh refuses it before deploy — the same rule that catches a requires for a resource nothing provides (research 011). A listener waiting for an event that can never arrive is a silent failure, and this repository's whole discipline is against silent failure.

One runtime serves all three

The per-node module runtime that serves a module's tools also wires its consumes (subscribe, dispatch to the handler) and lets its code emit. Tools are invoked (request/reply), resources are provisioned (1:1, credentialed), events are emitted and consumed (1:many, broadcast) — three relationships, one broker, one runtime, all declared on the manifest.

Consequences

  • The mesh gains a declared event graph alongside the provisioning graph — visible, validated, reasoned over.
  • Reaction becomes the default coordination: a module acts on another's event without either knowing the other, and without a credentialed link. Coupling drops.
  • An audit trail is a module, not a platform feature — and can be swapped, extended or run more than once (a file logger and a queryable one) with no change to anything that emits.
  • The runtime must dispatch a module's event handlers as well as its tools; that generalisation is small (both arrive by importing the module's entrypoint) but it is real work.

References

  • ADR 0001 — the broker events ride.
  • ADR 0045 — the relationships this extends.
  • ADR 0044 — emit/on are stable sdk surface; the broker binding and the runtime are not.
  • research 011 — the graph these edges join.