86 lines
4.2 KiB
Markdown
86 lines
4.2 KiB
Markdown
---
|
|
topic: what runs on it
|
|
status: accepted
|
|
date: 2026-09-03
|
|
deciders: jochen
|
|
reconstructed: false
|
|
extends: 0040-what-a-module-is.md
|
|
---
|
|
|
|
# 41. Events are a relationship, the lighter sibling of provisioning
|
|
|
|
## Context
|
|
|
|
[ADR 0040](0040-what-a-module-is.md) 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 0002](0002-nodes-communicate-over-a-broker.md)) 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](../01-RESEARCH/011-the-module-graph/00-overview.md)). 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 0002](0002-nodes-communicate-over-a-broker.md) — the broker events ride.
|
|
- [ADR 0040](0040-what-a-module-is.md) — the relationships this extends.
|
|
- [ADR 0039](0039-what-the-sdk-holds-and-refuses.md) — `emit`/`on` are stable sdk surface; the
|
|
broker binding and the runtime are not.
|
|
- [research 011](../01-RESEARCH/011-the-module-graph/00-overview.md) — the graph these edges join.
|