Both lines of work numbered from the same point, so four decision records and one design document existed twice with different content. The trunk keeps its numbers and this branch yields — the only rule that scales, because the trunk's are already cited by what merged before them. 0117 the bus is the only broker -> 0125 0118 a module declares its own seats -> 0126 0119 amqp is a provision, not the bus -> 0127 0120 the mesh bus is required -> 0128 0123 a seat carries its role's protocol -> 0129 0124 the predecessor is ending -> 0130 design 29, what a module declares -> design 32 Applied to the code repositories too, because a stale reference is worse when numbers collide than when they dangle: the reader lands on a real record that decided something else. Two reconciliations the merge forced, both real: **0110 was marked wholly superseded and was not.** Its successor says in as many words that everything 0110 decided about what a seat *is* stands untouched — and two records that landed on the trunk rest on exactly that part. So it is accepted again, extended rather than replaced, with a note saying which of its claims moved and where. **A seat's protocol becomes columns, not fields.** The trunk moved the seat set out of compiled code into a table the controller owns. This branch had added what a role accepts, emits and serves to the Go slice. The decision is unaffected and the mechanism is better for it: giving a role a protocol is now a write rather than a rebuild, which is the trunk's own argument applied to what this branch added. One check still fails and it fails on main too: a record resting on ADR 0112 while that is still 'proposed'. Left alone — it is not this merge's to answer.
104 lines
5.7 KiB
Markdown
104 lines
5.7 KiB
Markdown
---
|
|
topic: the mesh
|
|
status: accepted
|
|
date: 2026-09-26
|
|
deciders: jochen
|
|
reconstructed: false
|
|
supersedes: 02-DECISIONS/0125-the-bus-is-the-only-broker.md
|
|
---
|
|
|
|
# 127. AMQP is a provision, not the bus
|
|
|
|
## Context
|
|
|
|
[ADR 0125](0125-the-bus-is-the-only-broker.md) decided that the bus is the only broker, and went
|
|
one step further than it had grounds for: it also decided that the `amqp` **interface** — a module
|
|
requiring a message broker of its own — "is not carried forward" and "retires with the
|
|
compatibility broker rather than gaining a successor", with the two modules declaring it converted
|
|
to the bus in step 4.
|
|
|
|
The operator's correction: **AMQP is deprecated as the mesh's transport, not abolished as a
|
|
service.** The broker module keeps running and keeps answering `amqp` requirements. It is no
|
|
longer a core part of the mesh — *"it's just a module like mssql now."*
|
|
|
|
**What 0117 conflated** is two different reasons a module might ask for a broker, which look
|
|
identical in a manifest:
|
|
|
|
1. **To talk to other modules.** Wrong under one bus, and the thing 0117 was right to refuse: a
|
|
private broker used as inter-module transport is a second bus, with every guarantee crossing a
|
|
seam and no scoping the mesh can see.
|
|
2. **Because it genuinely needs an AMQP broker**, the way something needs a database — a queue for
|
|
its own internals, or interop with software that speaks AMQP and nothing else. That is a
|
|
backing service, and the mesh has a word for backing services already.
|
|
|
|
0117 saw the first and legislated against both. The second is ordinary, and forbidding it would
|
|
make the mesh unable to run a large class of perfectly normal software while claiming that as
|
|
architecture.
|
|
|
|
## Considered Options
|
|
|
|
1. **Keep 0117 as written** — retire the interface, convert the two modules. Rejected by the
|
|
operator, and wrongly reasoned besides: it treats "needs an AMQP broker" as always a mistake.
|
|
2. **Keep the broker as the predecessor's compatibility module**, as ADR 0106 framed it, with a
|
|
retirement condition. Rejected: it is not single-purpose and its clients are not only the
|
|
predecessor's, so the retirement condition describes a day that will not come.
|
|
3. **The broker is an ordinary provider module of an ordinary provision.** Adopted.
|
|
|
|
## Decision
|
|
|
|
**The mesh's bus is NATS and only NATS.** Everything 0117 decided about *the bus* stands: one bus,
|
|
a module's messaging is subjects on it scoped by what it declares, no module is handed a bus of
|
|
its own, and the `mesh-broker` seat is the NATS server's.
|
|
|
|
**`amqp` remains a provision a module may require**, answered by the broker module the way
|
|
`postgres-database` is answered by the store module or a database is answered by mssql. It is not
|
|
deprecated as an interface; the software behind it is simply no longer the mesh's nervous system.
|
|
|
|
**The broker module stops being foundation.** It claims no seat — `mesh-broker` is the NATS
|
|
server's — it is not raised at genesis, nothing in the mesh requires it, and a mesh that never
|
|
installs it is a complete mesh. It is installed when something wants it, like any other provider.
|
|
|
|
**The rule that survives, stated so it can be applied:** *inter-module communication goes over the
|
|
bus.* A module may hold a broker, a database or a cache as a backing service; it may not use one
|
|
as a channel to another module. The line is not which software is involved, it is whether a second
|
|
module is on the other end.
|
|
|
|
**Neither `amqp-ping` nor `amqp-email-forwarder` needs converting.** 0117 put that work in step 4;
|
|
it is removed. They require a backing service and a provider answers.
|
|
|
|
## Consequences
|
|
|
|
- **The "compatibility broker" framing is wrong and goes.** There is no `lavinmq-compat`, no
|
|
single purpose and no retirement condition. Design 25 §5 is corrected.
|
|
- **[ADR 0106](0106-the-bus-is-nats.md)'s progressive insight was itself wrong** and is corrected
|
|
by a second one there. It said 0117 would make 0106's "one purpose — the predecessor's clients"
|
|
sentence true by moving the mesh's modules off. Nothing moves off; the sentence is simply not
|
|
what the broker is.
|
|
- **The seat change stands**, for a better reason than 0117 gave: not because a broker cannot be
|
|
provisioned, but because *this* broker is not the mesh's bus. The broker module drops its
|
|
`mesh-broker` claim and the `nats` module takes it.
|
|
- **Step 4 loses two conversions**; step 1 and the WBS are otherwise unaffected.
|
|
- **What got harder:** the rule is now a judgement rather than a prohibition. "Is this a backing
|
|
service or a channel to another module?" has to be asked in review, where 0117 could have
|
|
answered it with a parser. That is the honest cost of allowing the legitimate case.
|
|
|
|
## How it is checked
|
|
|
|
- **A module's own messaging needs no `requires`.** The check from 0117, unchanged: a module
|
|
declaring only `emits` and `consumes` reaches its subjects and is refused every other.
|
|
- **The broker holds no seat.** A manifest test: the broker module claims nothing, and a mesh
|
|
raised without it is complete — genesis names it nowhere.
|
|
- **`amqp` resolves like any provision.** A resolution test: a module requiring it is answered by
|
|
the provider, refused when none is assigned, and neither case touches the bus.
|
|
- **What cannot be checked mechanically**, and is said rather than implied: that a module holding
|
|
a broker is not using it to reach another module. Review, not a parser.
|
|
|
|
## References
|
|
|
|
- [ADR 0125](0125-the-bus-is-the-only-broker.md) — superseded; its ruling on the bus is kept
|
|
whole and only its ruling on the interface is reversed.
|
|
- [ADR 0106](0106-the-bus-is-nats.md) — the bus is NATS; its compatibility-broker framing is
|
|
corrected here.
|
|
- [ADR 0126](0126-a-module-declares-its-own-seats.md) — seats, including the one the NATS server
|
|
now holds alone.
|