Modules declare their own seats; the mesh reserves mesh-*
The architecture 0117 opened needs a module to offer a service as a role on the bus — one holder, addressed by what it does. A closed table in the controller cannot express that: a capability a module contributes would require changing the mesh itself. But 0110 closed the set for a good reason — nothing could say what seats a mesh had, and the hand count came out at eleven of thirteen. That argues for enumerable, not hardcoded, and 0110 weighed free-form against a fixed table without considering a third option: closed at any moment and derived from the catalogue. A derived list cannot drift, which is how the count broke. So: the mesh's seats stay the mesh's, reserved by the mesh- prefix so the prefix is the rule and there is no list to maintain; ten seats are renamed to restore 0079's convention; everything 0110 decided about what a seat IS survives untouched. Design 29 carries the declaration model: three namespaces, subjects derived from local names so a manifest survives the wire changing, queues never declared, five relationships (the job and state shapes 0041 had no room for), and the build-publish-deploy lifecycle with hard, soft and build-time dependencies distinguished. 0041 gets a progressive insight: "no per-consumer setup, only a subscription" was a fact about a topic exchange, and a JetStream durable consumer is a real object someone creates. WBS 1.3/1.4 were wrong and say so: streams come at registration and consumers at assignment, so only the foundation set belongs at genesis.
This commit is contained in:
@@ -331,7 +331,15 @@ def check_progressive_insights(failures, records):
|
||||
if any(s <= m.start() and m.end() <= e for s, e in covered):
|
||||
continue
|
||||
line = text.rfind("\n", 0, m.start()) + 1
|
||||
if text[line:m.start()].lstrip().startswith("#"):
|
||||
end = text.find("\n", m.end())
|
||||
whole = text[line:end if end != -1 else len(text)]
|
||||
if whole.lstrip().startswith("#"):
|
||||
continue
|
||||
# A line that also carries a link is discussing the rule, not marking a correction:
|
||||
# a marker never needs to cite anything, and a record that reasons about the policy
|
||||
# must be able to name it. Bare prose with no citation is the informal marking this
|
||||
# is here to catch.
|
||||
if "](" in whole:
|
||||
continue
|
||||
if loose.search(text, line, text.find("\n", m.end()) + 1 or len(text)):
|
||||
continue
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ another — and a mesh you cannot name precisely is a mesh two people describe d
|
||||
- **seat** — a named role at a scope (node / site / mesh), held by a module assignment, from a
|
||||
**closed set** the mesh defines: a claim naming a seat outside the set is refused. A seat may
|
||||
**deliver a provision**, and its holder is then the mesh's answer for it when several modules
|
||||
provide it ([ADR 0110](../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md)).
|
||||
provide it ([ADR 0118](../02-DECISIONS/0118-a-module-declares-its-own-seats.md) (superseding [ADR 0110](../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md))).
|
||||
The set, with who holds each seat, is the overview of what a mesh has
|
||||
([26 — The seats](../03-DESIGN/01-to-be/26-the-seats.md)). A seat has a **capacity**: a
|
||||
capacity-1 seat is exclusive (one holder); a higher-capacity seat is a **bench** (several holders
|
||||
|
||||
@@ -76,6 +76,22 @@ three relationships, one broker, one runtime, all declared on the manifest.
|
||||
- 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.
|
||||
|
||||
## Progressive insight
|
||||
|
||||
> **Progressive insight — 2026-09-26.** *"No provisioner and no per-consumer setup" was a fact
|
||||
> about the transport, and the transport changed.* This record's table says an event's machinery is
|
||||
> "nothing but the broker's topic routing", and the text that an event needs "no per-consumer setup
|
||||
> — only a subscription". That was true of a topic exchange, where a binding cost nothing and the
|
||||
> broker fanned out. On NATS
|
||||
> ([ADR 0106](0106-the-bus-is-nats.md)) a subscription is a **durable consumer**: a real object
|
||||
> with a name, an ack policy, a delivery limit and its own ack subject, created when a module is
|
||||
> assigned and removed when it is not. Per-consumer setup exists, and the controller does it.
|
||||
>
|
||||
> The decision is untouched — events are declared on both sides, 1:many, credential-free, and
|
||||
> still provisioning's lighter sibling; the lightness is now relative rather than absolute.
|
||||
> [ADR 0118](0118-a-module-declares-its-own-seats.md) adds the relationship this record's two
|
||||
> columns had no room for: work addressed to a role, where exactly one holder must act.
|
||||
|
||||
## References
|
||||
|
||||
- [ADR 0002](0002-nodes-communicate-over-a-broker.md) — the broker events ride.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
topic: what runs on it
|
||||
status: accepted
|
||||
status: superseded
|
||||
superseded-by: 02-DECISIONS/0118-a-module-declares-its-own-seats.md
|
||||
date: 2026-09-25
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
---
|
||||
topic: the tiers
|
||||
status: accepted
|
||||
date: 2026-09-26
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
supersedes: 02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md
|
||||
---
|
||||
|
||||
# 118. A module declares its own seats; the mesh reserves its own
|
||||
|
||||
## Context
|
||||
|
||||
[ADR 0110](0110-a-seat-is-a-module-assignment-from-a-closed-set.md) closed the set of seats. Its
|
||||
evidence was strong and still is: nothing could answer *which seats does this mesh have, and who
|
||||
holds each*. Answering it meant reading every manifest in two repositories and then the
|
||||
controller's own code, and when that enumeration was done by hand while writing the record, **it
|
||||
reported eleven claims where there were thirteen.** The fix was a table in the controller, and
|
||||
adding a seat became a decision.
|
||||
|
||||
What that table cannot express is the architecture [ADR 0117](0117-the-bus-is-the-only-broker.md)
|
||||
opened. With one bus and no private brokers, a module offering a service to other modules offers
|
||||
it as **a role on the bus**: a set of subjects, exactly one holder, addressed by what it does
|
||||
rather than by which module or node provides it. A telegram sender, a licensing master, anything
|
||||
a mesh might want one of. Under a closed table, adding any of those means editing the controller
|
||||
— so a capability contributed by a module would require a change to the mesh itself, which is the
|
||||
coupling the module system exists to prevent.
|
||||
|
||||
**The two requirements look opposed and are not.** 0110 needs the set *enumerable*. The
|
||||
architecture needs it *extensible*. Those conflict only if enumerable means *written down in one
|
||||
place by hand* — which is exactly the property that let the count drift in the first place.
|
||||
|
||||
## Considered Options
|
||||
|
||||
1. **Keep the closed table, add each new seat by decision.** Rejected. Every capability a module
|
||||
contributes would need a change to the controller and a record before it could be offered, and
|
||||
the mesh would carry the names of services it does not itself implement.
|
||||
2. **Free-form seats, as before 0110.** Rejected for 0110's own reason, unchanged: nothing can
|
||||
say what a mesh has, and a name invented at a claim site is a name nobody can explain later.
|
||||
3. **A set that is closed at any moment and derived rather than maintained**, with the mesh's own
|
||||
seats reserved by name. Adopted. 0110 weighed options 1 and 2 and never considered this one.
|
||||
|
||||
## Decision
|
||||
|
||||
**A seat may be declared by a module, and the set of seats a mesh has is derived: the mesh's own,
|
||||
plus those declared by every module it has registered.** The set is still closed — a seat named
|
||||
nowhere is refused — but it is computed from the catalogue rather than written in the controller.
|
||||
|
||||
Everything 0110 decided about what a seat *is* stands untouched: one holder at its scope; a
|
||||
definition says which seats a module *can* hold and an assignment says which it *does*; holding
|
||||
one may deliver a provision; a seat makes a role singular, never a module.
|
||||
|
||||
**Enumeration is a query, not an inventory.** The catalogue knows every registered manifest, so
|
||||
"which seats does this mesh have, and who holds each" is answered by asking it. This is a
|
||||
stronger answer than the table gave, not a weaker one: a derived list cannot drift from reality,
|
||||
and drift is how the hand-made count came out at eleven of thirteen.
|
||||
|
||||
**The mesh's own seats are reserved by prefix.** Every seat the mesh itself defines is named
|
||||
`mesh-*`, and a module declaring any `mesh-*` name is refused at registration. The prefix *is*
|
||||
the reservation rule — no list of reserved names to maintain, and no way for the mesh's own
|
||||
namespace to be colonised by a manifest. This requires renaming the seats that drifted from
|
||||
[ADR 0079](0079-the-foundation-seats-are-named-after-their-servers.md)'s convention: `the-catalogue`
|
||||
becomes `mesh-catalog`, `git` becomes `mesh-git`, and the node-scoped `the-build-machine`,
|
||||
`the-dns-port`, `the-intrusion-prevention`, `the-packet-filter`, `the-private-network`,
|
||||
`the-resolver-configuration`, `the-showcase` take the same prefix.
|
||||
|
||||
The mesh's seats stay the mesh's for a reason that does not apply to a module's: **the mesh's own
|
||||
code looks them up by name.** The resolver *is* the thing that finds the store. `mesh-store` is
|
||||
not a convention the controller follows, it is an identifier the controller dereferences.
|
||||
|
||||
**A declared seat carries a protocol.** A module declaring a seat says what may be sent to it,
|
||||
what it emits, and what it serves. The holder must satisfy it; a module may not claim a seat whose
|
||||
protocol it does not implement. Callers declare that they use the *seat*, never the module, so
|
||||
replacing the implementation changes nothing for any caller.
|
||||
|
||||
**A seat is for a role; an event stays addressed to its emitter.** The two are not
|
||||
interchangeable and the choice is not stylistic. An event is *this happened to me* — the emitter's
|
||||
identity is the meaning, which is why the envelope carries source, node and time
|
||||
([ADR 0042](0042-the-shape-of-an-event-on-the-wire.md)); routing it through a role would erase the
|
||||
provenance an audit needs. A seat is *this capability, whoever provides it* — where not knowing
|
||||
the holder is the point. Publish an event when the fact is about you; declare a seat when you are
|
||||
offering something another module could offer instead.
|
||||
|
||||
**Two modules declaring the same seat name is refused at registration**, second one loses.
|
||||
Registration is the last moment the mesh can still say no, and a seat name meaning two different
|
||||
protocols is the failure nobody could diagnose afterwards.
|
||||
|
||||
## Consequences
|
||||
|
||||
- **The controller's seat table stops being the set** and becomes the mesh's own reserved entries.
|
||||
Resolution reads the catalogue for the rest.
|
||||
- **Ten seats are renamed.** A rename is a migration, not an edit: existing assignments hold the
|
||||
old names, so the change carries a mapping and is applied once, and the lab beds that name seats
|
||||
are updated with it.
|
||||
- **A `uses` naming an undeclared seat is refused at registration**, which is where 0110's
|
||||
guarantee lands under this model — the same refusal, at the same moment, from a derived set.
|
||||
- **Adding a capability stops requiring a decision record.** That is a real loss of governance and
|
||||
the intended trade: the argument for a seat's existence moves into the module that declares it,
|
||||
where it is reviewed as part of the manifest. The mesh's own seats keep the old bar.
|
||||
- **[ADR 0041](0041-events-are-a-relationship.md)'s machinery claim is already stale** for a
|
||||
different reason, and is corrected in place there under the rule in
|
||||
[`README.md`](README.md) — a progressive insight: on JetStream a subscription is a durable
|
||||
consumer, a real object someone must create.
|
||||
- **What got harder:** a seat's protocol is now a compatibility surface between modules that do
|
||||
not know each other. Changing one breaks callers already bound to it, and nothing here says how
|
||||
that is versioned. It is the first thing to answer in the design, and the thing most likely to
|
||||
hurt later rather than now.
|
||||
|
||||
## How it is checked
|
||||
|
||||
- **The overview answers, and is right.** A command lists every seat, its scope, its protocol and
|
||||
its holder, derived from the catalogue — and a test asserts the count against a fixture mesh,
|
||||
because an enumeration nobody checks is how thirteen became eleven.
|
||||
- **`mesh-*` is refused to a module.** A registration test: a manifest declaring `mesh-anything`
|
||||
is refused, naming the prefix as the reason.
|
||||
- **An undeclared seat is refused.** A registration test on `uses`, and a resolution test that
|
||||
nothing reaches runtime unresolved.
|
||||
- **A second declarer loses.** A registration test: two manifests, same seat name, the second
|
||||
refused and the first untouched.
|
||||
- **A holder must satisfy the protocol.** A claim whose module does not serve what the seat
|
||||
declares is refused at assignment, not discovered when a caller times out.
|
||||
|
||||
## References
|
||||
|
||||
- [ADR 0110](0110-a-seat-is-a-module-assignment-from-a-closed-set.md) — superseded here; its
|
||||
requirement is kept and only its mechanism replaced.
|
||||
- [ADR 0117](0117-the-bus-is-the-only-broker.md) — one bus, which is what makes a role addressable.
|
||||
- [ADR 0079](0079-the-foundation-seats-are-named-after-their-servers.md) — the naming convention
|
||||
the reserved prefix restores.
|
||||
- [ADR 0041](0041-events-are-a-relationship.md) — the event half of the boundary drawn here.
|
||||
@@ -164,6 +164,7 @@ python3 00-META/checks/index.py fail if stale
|
||||
- **0098** — [A fact a provider makes at first start is fetched from it, not carried in its manifest](0098-a-fact-a-provider-makes-at-first-start-is-fetched-from-it.md)
|
||||
- **0108** — [A route carries the policy applied to a request, and names a secret rather than holding one](0108-a-route-carries-the-policy-applied-to-a-request.md)
|
||||
- **0109** — [A package registry seat is one per ecosystem, not one for all of them](0109-a-package-registry-seat-is-one-per-ecosystem.md)
|
||||
- **0118** — [A module declares its own seats; the mesh reserves its own](0118-a-module-declares-its-own-seats.md)
|
||||
|
||||
### What runs on them, and how it gets there
|
||||
|
||||
@@ -195,7 +196,7 @@ python3 00-META/checks/index.py fail if stale
|
||||
- **0087** — [A seeded file is created once, and what grows in it is not the mesh's](0087-a-seeded-file-is-created-once.md)
|
||||
- **0091** — [A mount is declared, and there are three things it can be](0091-a-mount-is-declared-three-ways.md)
|
||||
- **0099** — [A step that runs once names what it reads, and runs again when it changed](0099-a-step-that-runs-once-names-what-it-reads.md)
|
||||
- **0110** — [A seat is held by one assignment, from a closed set, and it may deliver a provision](0110-a-seat-is-a-module-assignment-from-a-closed-set.md)
|
||||
- **0110** — [A seat is held by one assignment, from a closed set, and it may deliver a provision](0110-a-seat-is-a-module-assignment-from-a-closed-set.md) *(superseded)*
|
||||
- **0112** — [A module definition names no node, no mesh and no path: everything it needs is a requirement the mesh resolves](0112-a-module-definition-names-no-node-mesh-or-path.md) *(proposed)*
|
||||
- **0113** — [The vault makes every shared secret, a provider makes resources and data, and the mesh carries both](0113-the-vault-makes-every-secret.md) *(proposed)*
|
||||
- **0114** — [A credential two parties hold rotates over two credentials; one a single party holds rotates in place, staged; and retiring a credential never removes what it reached](0114-a-shared-credential-rotates-over-two-credentials.md) *(proposed)*
|
||||
|
||||
@@ -6,7 +6,7 @@ updated: 2026-09-25
|
||||
decisions:
|
||||
- 02-DECISIONS/0084-which-provider-serves-a-consumer.md
|
||||
- 02-DECISIONS/0027-a-provision-names-what-the-consumer-is-coupled-to.md
|
||||
- 02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md
|
||||
- 02-DECISIONS/0118-a-module-declares-its-own-seats.md
|
||||
---
|
||||
|
||||
# 23 — Choosing a provider
|
||||
@@ -54,7 +54,7 @@ to that provider and not to whichever one is nearest.
|
||||
**A seat names the mesh's one provider of a kind.** Where a seat delivers the provision, its holder
|
||||
answers for it when several providers exist and the consumer named none. That is not picking: the
|
||||
choice was made once, mesh-wide, by assigning the holder, rather than once per consumer by naming it
|
||||
([ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md),
|
||||
([ADR 0118](../../02-DECISIONS/0118-a-module-declares-its-own-seats.md) (superseding [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md)),
|
||||
[26 — The seats](26-the-seats.md)). A named provider still wins over the seat, because a consumer
|
||||
coupled to particular contents has said so.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ code:
|
||||
- mesh-catalog modules/gitea/module.json
|
||||
updated: 2026-09-26
|
||||
decisions:
|
||||
- 02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md
|
||||
- 02-DECISIONS/0118-a-module-declares-its-own-seats.md
|
||||
- 02-DECISIONS/0111-a-build-source-is-on-the-git-seat-or-external.md
|
||||
- 02-DECISIONS/0109-a-package-registry-seat-is-one-per-ecosystem.md
|
||||
---
|
||||
@@ -47,27 +47,42 @@ nobody argued for is an entry nobody can explain.
|
||||
|
||||
## The set
|
||||
|
||||
| seat | scope | delivers | typically held by |
|
||||
|---|---|---|---|
|
||||
| `mesh-controller` | mesh | — | the controller |
|
||||
| `mesh-store` | mesh | — | the store the mesh's own records live in |
|
||||
| `mesh-broker` | mesh | — | the broker carrying the mesh's own bus |
|
||||
| `mesh-vault` | mesh | `secret`, reserved | the vault |
|
||||
| `the-artifact-store` | mesh | `artifact-store` | the artifact registry |
|
||||
| `the-catalogue` | mesh | — | the catalogue |
|
||||
| `npm-package-registry` | mesh | `npm-package-registry` | the forge |
|
||||
| `git` | mesh | `git` | the forge |
|
||||
| `the-build-machine` | node | — | a builder |
|
||||
| `the-dns-port` | node | — | the local resolver |
|
||||
| `the-intrusion-prevention` | node | — | an intrusion-prevention service |
|
||||
| `the-packet-filter` | node | — | the packet filter |
|
||||
| `the-private-network` | node | — | the private network the mesh runs over |
|
||||
| `the-resolver-configuration` | node | — | whichever of the alternative resolver configurations is chosen |
|
||||
| `the-showcase` | node | — | the showcase module |
|
||||
**The set is derived, and only the mesh's half is written here.** Revision, 2026-09-26
|
||||
([ADR 0118](../../02-DECISIONS/0118-a-module-declares-its-own-seats.md), superseding
|
||||
[ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md)): a module
|
||||
declares its own seats with their protocols, so the seats a mesh has are the mesh's own **plus
|
||||
every registered module's**. The set is still closed — a seat named nowhere is refused — but it is
|
||||
computed from the catalogue rather than maintained by hand, which is the property 0110 actually
|
||||
needed and the table could not keep.
|
||||
|
||||
The controller holds this set in code, and a test asserts both its size and that every entry names
|
||||
the record that made it a seat. **This table and [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md)
|
||||
govern, and code that disagrees is what is wrong.** The implementation in progress predates several
|
||||
**Every seat below is named `mesh-*`, and the prefix is the reservation rule**: a module declaring
|
||||
any `mesh-*` name is refused at registration, so there is no reserved-names list to drift. Ten of
|
||||
these are renamed to restore [ADR 0079](../../02-DECISIONS/0079-the-foundation-seats-are-named-after-their-servers.md)'s
|
||||
convention, which later seats departed from.
|
||||
|
||||
| seat | was | scope | delivers | typically held by |
|
||||
|---|---|---|---|
|
||||
| `mesh-controller` | — | mesh | — | the controller |
|
||||
| `mesh-store` | — | mesh | — | the store the mesh's own records live in |
|
||||
| `mesh-broker` | — | mesh | — | the broker carrying the mesh's own bus |
|
||||
| `mesh-vault` | — | mesh | `secret`, reserved | the vault |
|
||||
| `mesh-artifact-store` | `the-artifact-store` | mesh | `artifact-store` | the artifact registry |
|
||||
| `mesh-catalog` | `the-catalogue` | mesh | — | the catalogue |
|
||||
| `mesh-npm-package-registry` | `npm-package-registry` | mesh | `npm-package-registry` | the forge |
|
||||
| `mesh-git` | `git` | mesh | `git` | the forge |
|
||||
| `mesh-build-machine` | `the-build-machine` | node | — | a builder |
|
||||
| `mesh-dns-port` | `the-dns-port` | node | — | the local resolver |
|
||||
| `mesh-intrusion-prevention` | `the-intrusion-prevention` | node | — | an intrusion-prevention service |
|
||||
| `mesh-packet-filter` | `the-packet-filter` | node | — | the packet filter |
|
||||
| `mesh-private-network` | `the-private-network` | node | — | the private network the mesh runs over |
|
||||
| `mesh-resolver-configuration` | `the-resolver-configuration` | node | — | whichever of the alternative resolver configurations is chosen |
|
||||
| `mesh-showcase` | `the-showcase` | node | — | the showcase module |
|
||||
|
||||
The controller holds **the mesh's own** entries in code, and a test asserts their size and that
|
||||
every one names the record that made it a seat. A module's seats are not here and never will be —
|
||||
they are read from the catalogue. **This table and
|
||||
[ADR 0118](../../02-DECISIONS/0118-a-module-declares-its-own-seats.md) govern, and code that
|
||||
disagrees is what is wrong.** The implementation in progress predates several
|
||||
things here: seats held by assignments rather than claimed by definitions, the `mesh-vault` seat and
|
||||
its reservation, and the foundation's seats delivering nothing. It is brought to this table before it
|
||||
merges.
|
||||
@@ -155,16 +170,22 @@ still to take.
|
||||
|
||||
## How it is checked
|
||||
|
||||
The rules here are [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md)'s
|
||||
and [ADR 0111](../../02-DECISIONS/0111-a-build-source-is-on-the-git-seat-or-external.md)'s, and each is
|
||||
The rules here are [ADR 0118](../../02-DECISIONS/0118-a-module-declares-its-own-seats.md)'s —
|
||||
which supersedes [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md)
|
||||
and keeps every rule below except how the set is formed — and
|
||||
[ADR 0111](../../02-DECISIONS/0111-a-build-source-is-on-the-git-seat-or-external.md)'s. Each is
|
||||
checked as their tables say:
|
||||
|
||||
| Rule | Checked by |
|
||||
|---|---|
|
||||
| The set is closed, and every entry names its decision | 0110: a unit test on the set's size and decisions; manifest tests refusing an unknown seat or the wrong scope. |
|
||||
| A seat is held by one assignment, and only by one whose module can hold it | 0110: resolution tests for a second holder and for a seat the definition does not name. |
|
||||
| A requirement naming a seat is answered by its holder; a foundation seat cannot be named | 0110: resolution tests with a second provider on the consumer's node, with the seat unheld, and naming `mesh-store`. |
|
||||
| Several providers and none local is a person's choice | 0110: an assignment test listing candidates with the seat's holder first and recording the pin. |
|
||||
| `secret` is reserved | 0110: the parser and resolution refusals for another provider and a pin. |
|
||||
| Holdings are derived, and the overview lists every seat | 0110: the `seats` command test, including an unheld seat. |
|
||||
| The set is closed, and every mesh entry names its decision | 0118: a unit test on the mesh's own entries; manifest tests refusing an unknown seat or the wrong scope. |
|
||||
| The set is derived, and enumerating it is a query | 0118: the overview lists the mesh's own plus every registered module's, asserted against a fixture mesh. |
|
||||
| `mesh-*` is the mesh's, and a module may not declare one | 0118: a registration test refusing a manifest that declares any `mesh-*` seat, naming the prefix. |
|
||||
| Two modules cannot declare the same seat | 0118: a registration test; the second is refused and the first untouched. |
|
||||
| A holder satisfies the seat's protocol | 0118: a claim whose module does not serve what the seat declares is refused at assignment. |
|
||||
| A seat is held by one assignment, and only by one whose module can hold it | 0118: resolution tests for a second holder and for a seat the definition does not name. |
|
||||
| A requirement naming a seat is answered by its holder; a foundation seat cannot be named | 0118: resolution tests with a second provider on the consumer's node, with the seat unheld, and naming `mesh-store`. |
|
||||
| Several providers and none local is a person's choice | 0118: an assignment test listing candidates with the seat's holder first and recording the pin. |
|
||||
| `secret` is reserved | 0118: the parser and resolution refusals for another provider and a pin. |
|
||||
| Holdings are derived, and the overview lists every seat | 0118: the `seats` command test, including an unheld seat. |
|
||||
| A build source on the seat records no address; an unheld seat refuses only self-hosted builds | 0111's tests. |
|
||||
|
||||
@@ -8,7 +8,7 @@ decisions:
|
||||
- 02-DECISIONS/0115-one-assignment-of-a-module-per-node.md
|
||||
- 02-DECISIONS/0113-the-vault-makes-every-secret.md
|
||||
- 02-DECISIONS/0114-a-shared-credential-rotates-over-two-credentials.md
|
||||
- 02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md
|
||||
- 02-DECISIONS/0118-a-module-declares-its-own-seats.md
|
||||
- 02-DECISIONS/0111-a-build-source-is-on-the-git-seat-or-external.md
|
||||
- 02-DECISIONS/0084-which-provider-serves-a-consumer.md
|
||||
- 02-DECISIONS/0046-a-module-configuration-is-its-assignments-not-its-manifest.md
|
||||
@@ -64,7 +64,7 @@ Which module answers, in order:
|
||||
1. **the holder of the seat the requirement names.** A requirement may name a seat instead of leaving
|
||||
the provider open. It asks for *the mesh's* one, and the mesh answers with whichever assignment
|
||||
holds that seat, with nothing asked of anyone. Unheld, the requirement is refused, naming the seat
|
||||
([ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md),
|
||||
([ADR 0118](../../02-DECISIONS/0118-a-module-declares-its-own-seats.md) (superseding [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md)),
|
||||
[26 — The seats](26-the-seats.md)). Only a seat that delivers a provision can be named; naming a
|
||||
foundation seat is refused, because it delivers nothing. A `secret` requirement always names
|
||||
`mesh-vault`, because that provision is reserved;
|
||||
@@ -199,7 +199,7 @@ containers, login, broker account and settings are keyed by it, as today, and a
|
||||
tightest backend ([ADR 0049](../../02-DECISIONS/0049-a-consumers-identity-fits-the-tightest-backend.md)).
|
||||
|
||||
**A module may run on many nodes, and one assignment may hold a seat**
|
||||
([ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md)). The definition
|
||||
([ADR 0118](../../02-DECISIONS/0118-a-module-declares-its-own-seats.md) (superseding [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md))). The definition
|
||||
says which seats the module can hold; the assignment says which it does. So the store module can run
|
||||
on every node, one of those assignments holds `mesh-store`, and moving that role changes an
|
||||
assignment, not a definition.
|
||||
|
||||
@@ -10,6 +10,7 @@ decisions:
|
||||
- 02-DECISIONS/0116-the-bus-is-built-in-five-steps.md
|
||||
- 02-DECISIONS/0106-the-bus-is-nats.md
|
||||
- 02-DECISIONS/0117-the-bus-is-the-only-broker.md
|
||||
- 02-DECISIONS/0118-a-module-declares-its-own-seats.md
|
||||
- 02-DECISIONS/0074-the-wire-is-specified-not-the-types.md
|
||||
- 02-DECISIONS/0079-the-foundation-seats-are-named-after-their-servers.md
|
||||
- 02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md
|
||||
@@ -115,6 +116,15 @@ step 5 the rollout
|
||||
|
||||
## Step 1 — the module, and genesis raises it
|
||||
|
||||
> **Revised 2026-09-26** ([ADR 0118](../../02-DECISIONS/0118-a-module-declares-its-own-seats.md),
|
||||
> [design 29](29-what-a-module-declares.md)). Tasks 1.3 and 1.4 said the controller composes every
|
||||
> account and creates *the four streams* at genesis, from a fixed set. That is only the mesh's own
|
||||
> half. A module declares seats with their protocols, so streams are created **at registration**
|
||||
> and durable consumers **at assignment** — neither of which has happened at genesis. The fixed
|
||||
> foundation set stays here; the derived machinery moves to step 3, where the declaration model it
|
||||
> reads from is specified. Tasks 1.1 and 1.2, already done, are untouched by this: the module and
|
||||
> its reload mechanism do not care what the configuration says.
|
||||
|
||||
**Why here.** Everything else needs a server to talk to, and genesis is where the foundation is
|
||||
defined. The mesh this is for will never travel this path — it is already running, and takes step 2
|
||||
— but genesis is the definition every other path is measured against, and one that exists only on
|
||||
@@ -126,11 +136,14 @@ paper is wrong until there is a second mesh to find out.
|
||||
- [ ] 1.2 the composed configuration as a **directory** resource, and the entrypoint that watches
|
||||
the one file and signals the server itself — design 25 §5's correction, kept inside the module
|
||||
because a container has no reload and a recreate would drop every connection the mesh has
|
||||
- [ ] 1.3 the controller composes that file: accounts, permissions, TLS, JetStream — permissions
|
||||
derived from `emits` and `consumes` and nothing else, plus each user's own ack subject and its
|
||||
own inbox prefix (design 25 §4)
|
||||
- [ ] 1.4 the four streams, created at genesis and asserted idempotently on start, by the controller
|
||||
as their only writer
|
||||
- [ ] 1.3 the controller composes that file: accounts, permissions, TLS, JetStream — a user's
|
||||
permissions derived from its declaration and nothing else, over the three namespaces of
|
||||
[design 29](29-what-a-module-declares.md) §2, plus its own ack subject and its own inbox
|
||||
prefix (design 25 §4)
|
||||
- [ ] 1.4 the mesh's own streams, created at genesis and asserted idempotently on start, by the
|
||||
controller as their only writer — **the mesh's own, not all of them**: a seat's streams are
|
||||
created when the module declaring it is registered, and a module's durable consumers when it
|
||||
is assigned, so this task is the fixed foundation set and 3.x carries the derived rest
|
||||
- [ ] 1.5 genesis raises it as foundation, claiming the seat **`mesh-broker`** — the seat is the
|
||||
server's role, not the product
|
||||
- [ ] 1.6 the genesis-broker bed
|
||||
@@ -183,6 +196,15 @@ pays for itself furthest away.
|
||||
- [ ] 3.5 the host's link on NATS — mirroring, still importing nothing
|
||||
- [ ] 3.6 the tool runtime's client on NATS, behind the unchanged sdk contract
|
||||
- [ ] 3.7 the sdk's three stale comments, and nothing else in it
|
||||
- [ ] 3.8 **the declaration model** of [design 29](29-what-a-module-declares.md): local names
|
||||
derived to subjects, the three namespaces, permissions computed from a declaration, and a
|
||||
manifest that contains no subject
|
||||
- [ ] 3.9 **seats declared by modules** — registration creates a seat's streams and refuses a
|
||||
`mesh-*` name, a duplicate declarer, an undeclared `uses`, and a holder that does not
|
||||
satisfy the protocol; assignment creates the holder's work-queue consumer and refuses a
|
||||
second holder
|
||||
- [ ] 3.10 **the ten seat renames**, carried as a migration with a mapping rather than an edit,
|
||||
and the beds that name seats moved with them
|
||||
|
||||
**Done when.** The fixtures are produced and consumed byte for byte by every implementation that
|
||||
claims the capability, and a module built before any of this serves its tools unchanged on the new
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
---
|
||||
layer: to-be
|
||||
status: proposed
|
||||
code: []
|
||||
updated: 2026-09-26
|
||||
decisions:
|
||||
- 02-DECISIONS/0118-a-module-declares-its-own-seats.md
|
||||
- 02-DECISIONS/0117-the-bus-is-the-only-broker.md
|
||||
- 02-DECISIONS/0106-the-bus-is-nats.md
|
||||
- 02-DECISIONS/0041-events-are-a-relationship.md
|
||||
- 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md
|
||||
- 02-DECISIONS/0083-one-push-leaves-the-mesh-consistent.md
|
||||
---
|
||||
|
||||
# 29. What a module declares, and what the bus makes of it
|
||||
|
||||
**The bus is ambient.** No module requires it, the way no module requires a filesystem. Every
|
||||
module gets a connection and an identity whether it asks or not. What a module declares are
|
||||
*relationships*; subjects, streams, consumers and permissions are all derived from those, and a
|
||||
manifest never contains one.
|
||||
|
||||
This document is the declaration model. [Design 25](25-the-bus-on-nats.md) is the bus itself —
|
||||
subjects, streams, accounts, enrolment — and stays the authority on the wire.
|
||||
[Design 19](19-the-module-protocol.md) is the specification an SDK implements, and is rewritten
|
||||
onto this in step 3 of [ADR 0116](../../02-DECISIONS/0116-the-bus-is-built-in-five-steps.md).
|
||||
|
||||
## 1. A module names locally; the mesh derives the subject
|
||||
|
||||
This is the load-bearing rule.
|
||||
[ADR 0112](../../02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md) says a module
|
||||
definition names no node, mesh or path. A transport address is the same class of thing: if
|
||||
manifests held literal subjects, reorganising the subject space would mean editing every module in
|
||||
the catalogue, and the mesh would have hundreds of copies of a decision it made once.
|
||||
|
||||
| declared | derived |
|
||||
|---|---|
|
||||
| `emits: order.placed` | publish on `mesh.mod.<module>.order.placed` |
|
||||
| `consumes: billing.order.placed` | durable consumer on `mesh.mod.billing.order.placed` |
|
||||
| `serves: status` | queue-group subscription on `mesh.mod.<module>.tool.status` |
|
||||
| seat `telegram-sender`, `accepts: send` | work-queue consumer on `mesh.seat.telegram-sender.send` |
|
||||
| `uses: telegram-sender` | publish on that seat's `accepts` subjects, and nothing else |
|
||||
|
||||
**The test this must pass: the manifest survives the wire changing.** Reorganise the subject space
|
||||
and every manifest in the catalogue is still correct. That is the property
|
||||
[ADR 0039](../../02-DECISIONS/0039-what-the-sdk-holds-and-refuses.md) gave the sdk, applied to
|
||||
declarations.
|
||||
|
||||
## 2. Three namespaces, and nothing else
|
||||
|
||||
**Its own** — `mesh.mod.<module>.>`. Its events and its tools. Nothing else may publish into it,
|
||||
so an event's source is a fact the bus enforces rather than a claim in the body.
|
||||
|
||||
**Seats it holds** — `mesh.seat.<seat>.>`. Full participation: consume what the seat accepts,
|
||||
publish what it emits, serve what it serves.
|
||||
|
||||
**Seats it uses** — publish only, and only on the `accepts` half. A sender cannot subscribe to a
|
||||
seat's inbound subject and watch other modules' traffic, and cannot publish the seat's outbound
|
||||
events and lie about outcomes.
|
||||
|
||||
A module naming anything outside these three is refused at registration. The whole permission set
|
||||
is derivable from the declaration; nobody writes an access rule.
|
||||
|
||||
## 3. Queues are derived, never declared
|
||||
|
||||
A module says what it reacts to, not how delivery works. Each `consumes` becomes one durable
|
||||
consumer; a seat's `accepts` becomes one work-queue consumer with a queue group named for the
|
||||
seat. The module does not name them, does not know their names, and cannot misconfigure them —
|
||||
and the controller stays the only writer of stream and consumer definitions
|
||||
([design 25](25-the-bus-on-nats.md) §3).
|
||||
|
||||
**Retention belongs to whoever owns the namespace, not to a consumer.** A seat declares how long
|
||||
its inbound backlog survives, because that is a property of the service:
|
||||
|
||||
```
|
||||
seat: telegram-sender
|
||||
scope: mesh
|
||||
accepts: send retain 7d
|
||||
emits: delivered, failed
|
||||
serves: status
|
||||
```
|
||||
|
||||
If each consumer could tune it, the mesh's durability would be an emergent property of whichever
|
||||
manifest was edited last.
|
||||
|
||||
**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
|
||||
service" versus "one worker per machine".
|
||||
|
||||
## 4. Five relationships
|
||||
|
||||
| | provision | event | job | state | tool |
|
||||
|---|---|---|---|---|---|
|
||||
| shape | 1:1 resource | 1:many | N:1 | 1:1 | 1:1 |
|
||||
| addressed to | a provider | the emitter's own namespace | a **seat** | one node | a module or seat |
|
||||
| who must act | the provider | nobody | exactly one holder | that node | the server |
|
||||
| credential | sealed, per consumer | none | none | none | none |
|
||||
| reply | — | none | none, or an event later | a report | awaited |
|
||||
| retention | — | age and size | work queue, explicit ack | **last per subject** | none |
|
||||
| declared | `provides`/`requires` | `emits`/`consumes` | seat `accepts` / `uses` | the mesh's own | `serves` |
|
||||
|
||||
**Job** is the one [ADR 0041](../../02-DECISIONS/0041-events-are-a-relationship.md) had no room
|
||||
for. Its table has events at 1:many and provisions at 1:1; a module submitting work to a service
|
||||
is neither. It is not an event, because an event is a broadcast nobody is obliged to act on and a
|
||||
second holder would do the work twice. It is not a provision, because there is no resource and no
|
||||
credential. What makes it safe is not cleverness in the subscribe call but the seat: exactly one
|
||||
holder, so exactly one worker, by construction.
|
||||
|
||||
**State** is the shape the deploy path needs and nothing else uses. A declaration is not an event
|
||||
— replaying yesterday's is actively harmful — and not a job. Only the newest matters, which is
|
||||
last-per-subject retention, and a node that has seen sequence *n* refuses *n−1* by construction.
|
||||
That is the wire-level answer to
|
||||
[issue 107](../../04-ISSUES/107-a-declaration-carries-no-order/00-report.md).
|
||||
|
||||
## 5. Seats
|
||||
|
||||
A module declares a seat with its protocol, and the mesh enforces one holder at its scope
|
||||
([ADR 0118](../../02-DECISIONS/0118-a-module-declares-its-own-seats.md)). A caller declares that
|
||||
it uses the *seat*, never the module, so the implementation can be replaced under it.
|
||||
|
||||
- The set of seats is **derived** — the mesh's own, plus every registered module's — so it is both
|
||||
closed and extensible, and enumerating it is a query rather than an inventory.
|
||||
- `mesh-*` is **reserved**: the prefix is the reservation rule, and a module declaring one is
|
||||
refused at registration.
|
||||
- Two modules declaring the same name: the second is refused.
|
||||
- A module may not claim a seat whose protocol it does not implement.
|
||||
- **Nobody holding a seat is not an error.** The stream exists from registration, so work queues
|
||||
until a holder appears. Install the telegram module a week later and the backlog flushes.
|
||||
|
||||
## 6. The lifecycle: build, publish, deploy
|
||||
|
||||
Every shape above appears once, in order, and no step knows where the next one runs.
|
||||
|
||||
**A change lands.** The module holding `mesh-git` emits `pushed` — repository, ref, commit. An
|
||||
event, because it is a fact about git and git's identity is the meaning.
|
||||
|
||||
**The change becomes work.** The controller consumes `pushed`, asks the catalogue which modules
|
||||
are built from that repository and path
|
||||
([ADR 0069](../../02-DECISIONS/0069-a-module-is-a-repository-and-a-path.md)), and submits one
|
||||
**job** per affected module to the `mesh-build-machine` seat. The builder stays simple: it builds
|
||||
what it is handed, and never resolves anything. A builder that dies mid-build has its job
|
||||
redelivered, because a work queue with explicit ack is what that means.
|
||||
|
||||
**The artifact is published.** The builder pushes to the registry seats and emits `built` —
|
||||
module, version, digest. An event again: a fact about the builder.
|
||||
|
||||
**The build cascade is that event fanning out through a graph the mesh already has.** A module
|
||||
whose image is built *on* another's artifact declares that in `build.on`. So `built` reaches the
|
||||
controller, which walks the declared graph and submits rebuild jobs for everything downstream. A
|
||||
dependency cascade is not special machinery — it is one event, one derived graph, and the same job
|
||||
queue.
|
||||
|
||||
**Deployment is state, not a message.** The controller composes each affected node's declaration
|
||||
and publishes it last-per-subject. A node that was away gets exactly the current one, never a
|
||||
queue of superseded ones, and a replayed older one is refused by sequence.
|
||||
|
||||
**Applying is reported to a role.** The host applies and reports to the `mesh-controller` seat —
|
||||
not to an address it was given at genesis. Held and retried while the store restarts
|
||||
([ADR 0083](../../02-DECISIONS/0083-one-push-leaves-the-mesh-consistent.md)).
|
||||
|
||||
What disappears across that chain is every address. No webhook URL, no registered callback, no
|
||||
"which node is the builder on", no controller endpoint baked into a joining node. That is the
|
||||
class of bug
|
||||
[issue 102](../../04-ISSUES/102-an-address-recorded-at-genesis-or-build-does-not-follow-the-nodes-ports/00-report.md)
|
||||
names, dissolved rather than fixed.
|
||||
|
||||
## 7. Modules depending on each other
|
||||
|
||||
Three kinds, and conflating them is how deployment ordering goes wrong.
|
||||
|
||||
**Build-time** — A's image is built on B's artifact. Resolved by the cascade above; nothing at
|
||||
runtime cares.
|
||||
|
||||
**Provision** — A requires a database from B. A **hard** dependency: the credential must exist
|
||||
before A can start, so resolution gates delivery and A is shown as waiting until B has answered
|
||||
([design 27](27-a-module-requires-the-mesh-resolves.md)).
|
||||
|
||||
**Seat** — A uses B's seat. A **soft** dependency, and this is the one the bus changes. A starts
|
||||
whether or not anyone holds the seat, because the stream absorbs the gap. Deployment order stops
|
||||
mattering for everything expressed this way, and a service being restarted, moved or upgraded is
|
||||
not an outage for its callers — it is latency.
|
||||
|
||||
That difference is worth choosing on purpose. A dependency expressed as a provision must be
|
||||
ordered; the same dependency expressed as a seat need not be.
|
||||
|
||||
## 8. Open
|
||||
|
||||
**Protocol versioning.** A seat's protocol is a compatibility surface between modules that do not
|
||||
know each other, and nothing here says what happens when it changes under callers already bound to
|
||||
it. This is the first thing to answer and the one most likely to hurt in year two rather than week
|
||||
one.
|
||||
|
||||
**Whether a module may declare a seat it does not itself claim** — the contract as one thing, the
|
||||
implementation as another, which is how two competing implementations would ever exist.
|
||||
|
||||
**Whether `consumes` naming another module couples too tightly.** It is kept here deliberately —
|
||||
an event's provenance is its meaning — but a consumer of `billing.order.placed` does depend on
|
||||
billing existing under that name.
|
||||
|
||||
## 9. How it is checked
|
||||
|
||||
- **A manifest holds no subject.** A catalogue test: no manifest contains a string matching the
|
||||
subject grammar. The rule is worthless if it is followed by convention.
|
||||
- **Permissions are exactly the three namespaces.** A composition test per module: the derived
|
||||
permission set equals what its declaration implies, and a hand-written addition to it fails.
|
||||
- **A sender cannot read the queue it writes to.** A bed: a module declaring `uses` is refused
|
||||
subscribe on that seat's inbound subject.
|
||||
- **One holder, one delivery.** A bed: a seat's job delivered once with the holder running, and
|
||||
a second claim of the seat refused.
|
||||
- **A queued job survives no holder.** A bed: submit with the seat unheld, assign the holder,
|
||||
the job is delivered.
|
||||
- **The cascade rebuilds exactly the dependents.** A bed: publish an artifact two modules build
|
||||
on, and exactly those two are rebuilt.
|
||||
- **A stale declaration is refused.** A bed: replay sequence *n−1* after *n*, and the node refuses
|
||||
it rather than applying it.
|
||||
@@ -35,10 +35,12 @@ document is written and this one's status becomes `implemented`.
|
||||
| [`23-choosing-a-provider.md`](23-choosing-a-provider.md) | Which of several providers of a kind serves a consumer, and when a module carries its own instead | [ADR 0084](../../02-DECISIONS/0084-which-provider-serves-a-consumer.md), [ADR 0027](../../02-DECISIONS/0027-a-provision-names-what-the-consumer-is-coupled-to.md) |
|
||||
| [`24-the-secrets-vault.md`](24-the-secrets-vault.md) | The module that owns a secret — a `secret` provision, and the boundary of what it owns | [ADR 0085](../../02-DECISIONS/0085-a-secret-is-a-provision.md), [ADR 0031](../../02-DECISIONS/0031-the-control-plane-authenticates-nobody.md), [ADR 0048](../../02-DECISIONS/0048-a-provider-creates-the-credential-the-mesh-minted.md) |
|
||||
| [`25-the-bus-on-nats.md`](25-the-bus-on-nats.md) | **Proposed.** The architecture of the mesh's bus on NATS: what rides which subject under which guarantee and whose account, how a node joins, how a person reaches a tool, and how the mesh moves from the bus it has | [ADR 0106](../../02-DECISIONS/0106-the-bus-is-nats.md), [ADR 0116](../../02-DECISIONS/0116-the-bus-is-built-in-five-steps.md), [ADR 0043](../../02-DECISIONS/0043-a-module-broker-account-is-scoped-by-emits-and-consumes.md), [ADR 0083](../../02-DECISIONS/0083-one-push-leaves-the-mesh-consistent.md) |
|
||||
| [`26-the-seats.md`](26-the-seats.md) | **Proposed.** What a mesh can have one of, who fills each, and a seat's holder answering for the provision it delivers — including the `git` seat a build's source can live on | [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md), [ADR 0111](../../02-DECISIONS/0111-a-build-source-is-on-the-git-seat-or-external.md), [ADR 0109](../../02-DECISIONS/0109-a-package-registry-seat-is-one-per-ecosystem.md) |
|
||||
| [`27-a-module-requires-the-mesh-resolves.md`](27-a-module-requires-the-mesh-resolves.md) | **Proposed.** One concept for everything a module needs: a requirement with a contract, answered by one of four kinds of provider, resolved at assignment or refused. Retires settings, placeholders, facts and paths in definitions | [ADR 0112](../../02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md), [ADR 0113](../../02-DECISIONS/0113-the-vault-makes-every-secret.md), [ADR 0114](../../02-DECISIONS/0114-a-shared-credential-rotates-over-two-credentials.md), [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md) |
|
||||
| [`26-the-seats.md`](26-the-seats.md) | **Proposed.** What a mesh can have one of, who fills each, and a seat's holder answering for the provision it delivers — including the `git` seat a build's source can live on | [ADR 0118](../../02-DECISIONS/0118-a-module-declares-its-own-seats.md) (superseding [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md)), [ADR 0111](../../02-DECISIONS/0111-a-build-source-is-on-the-git-seat-or-external.md), [ADR 0109](../../02-DECISIONS/0109-a-package-registry-seat-is-one-per-ecosystem.md) |
|
||||
| [`27-a-module-requires-the-mesh-resolves.md`](27-a-module-requires-the-mesh-resolves.md) | **Proposed.** One concept for everything a module needs: a requirement with a contract, answered by one of four kinds of provider, resolved at assignment or refused. Retires settings, placeholders, facts and paths in definitions | [ADR 0112](../../02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md), [ADR 0113](../../02-DECISIONS/0113-the-vault-makes-every-secret.md), [ADR 0114](../../02-DECISIONS/0114-a-shared-credential-rotates-over-two-credentials.md), [ADR 0118](../../02-DECISIONS/0118-a-module-declares-its-own-seats.md) (superseding [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md)) |
|
||||
| [`28-building-the-bus.md`](28-building-the-bus.md) | **Proposed.** The five steps of the bus work in the order their dependencies allow, each ending at a bed — with the surface measured, so no step's size is a guess | [ADR 0116](../../02-DECISIONS/0116-the-bus-is-built-in-five-steps.md), [ADR 0106](../../02-DECISIONS/0106-the-bus-is-nats.md), [ADR 0074](../../02-DECISIONS/0074-the-wire-is-specified-not-the-types.md) |
|
||||
|
||||
| [`29-what-a-module-declares.md`](29-what-a-module-declares.md) | **Proposed.** What a module declares and what the bus derives from it: three namespaces, subjects from local names, queues never declared, the five relationships, and the build-publish-deploy lifecycle on one bus | [ADR 0118](../../02-DECISIONS/0118-a-module-declares-its-own-seats.md), [ADR 0117](../../02-DECISIONS/0117-the-bus-is-the-only-broker.md), [ADR 0041](../../02-DECISIONS/0041-events-are-a-relationship.md) |
|
||||
|
||||
## Not yet written
|
||||
|
||||
- **The remaining six contexts.**
|
||||
|
||||
Reference in New Issue
Block a user