Building the bus: the decisions the work needed, and what it taught back #150

Merged
jschoubben merged 45 commits from feat/nats-genesis into main 2026-09-27 17:06:40 +00:00
Showing only changes of commit 0b8e84334f - Show all commits
@@ -103,6 +103,28 @@ seat: telegram-sender
If each consumer could tune it, the mesh's durability would be an emergent property of whichever
manifest was edited last.
**Why a module's own events do not carry their own retention, though the same rule would allow
it.** A seat owns its namespace and gets a stream of its own, so it can say. A module's events
share one `EVENTS` stream, and three facts about JetStream decide that they must:
- **Storage is not a property of a subject.** A subject is only an address; a *stream* is a
separate object that captures subjects matching a filter. So "this topic is durable" is always
really "some stream covers it", and something has to create that stream.
- **Overlapping streams are refused, not merged.** Verified against the server: a per-module
stream beside a shared `mesh.mod.*.event.>` is rejected with *subjects overlap with an existing
stream*. So "one stream by default, its own for a module that wants different retention" is not
available — it is all of one or all of the other, and a filter cannot express an exception
either.
- **A stream per module breaks cross-module consumption.** An audit logger consuming every
module's events is one consumer on one stream today; with a stream each it becomes one consumer
per module, created and destroyed as modules come and go.
So: one stream, and **per-subject caps** for the fairness that actually matters — a noisy emitter
cannot evict a quiet one, which is verified (a cap of three, ten messages on one subject and one
on another, leaves four). What is genuinely unavailable is a different *age* per module, because
JetStream ages per stream and not per subject. A module that truly needs its own retention has a
way to say so: declare a seat, which owns its namespace and gets its own stream.
**Scope gives per-node workers without a new concept.** A module running on three nodes that each
need their own queue declares a node-scoped seat: one holder per node, three queues, same
machinery. Mesh-scoped and node-scoped seats already exist; here they do the work of "one shared