Building the bus: the decisions the work needed, and what it taught back #150
@@ -7,7 +7,7 @@ code:
|
||||
- mesh-tools src/broker-amqp.ts (to be replaced)
|
||||
- mesh-catalog modules/nats (to be written)
|
||||
- mesh-sdk src (the protocol's NATS binding, step 3)
|
||||
updated: 2026-09-26
|
||||
updated: 2026-09-27
|
||||
decisions:
|
||||
- 02-DECISIONS/0106-the-bus-is-nats.md
|
||||
- 02-DECISIONS/0119-amqp-is-a-provision-not-the-bus.md
|
||||
@@ -213,7 +213,32 @@ expresses this exactly, per subject, and better than a vhost could:
|
||||
- **A person's user** (§7) is a module-shaped user with permissions on the tool subjects it may
|
||||
invoke, issued and revoked by the controller like any account.
|
||||
|
||||
**Accounts are configuration, not API calls.** The controller composes the server's user list and
|
||||
**The server does not verify client certificates, and TLS is still required.** *Revision,
|
||||
2026-09-27, found by building the module's image and connecting to it as a host would.* The first
|
||||
composed configuration said `verify: true`, which makes the server demand a **client** certificate —
|
||||
and nothing in the mesh presents one. A host pins this server's exact certificate and authenticates
|
||||
with the password the mesh minted ([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)),
|
||||
and a module's runtime does the same. With it on, every connection in the mesh dies at the TLS
|
||||
handshake before any password is looked at, and the error — "client didn't provide a certificate" —
|
||||
reads as a fault in the client rather than in the bus's configuration. The `tls` block is what makes
|
||||
TLS required; `verify` only decides whether client certificates are checked. What is given up is a
|
||||
second factor the mesh has no machinery to issue or rotate — a certificate per module per node — and
|
||||
what is kept is stronger than a name check in both directions: an exact pin outward, a password
|
||||
scoped per user inward. **Mutual TLS is a later question and would need that machinery first.**
|
||||
|
||||
**The mesh composes the accounts; the module composes its server.** *Revision, 2026-09-27, while
|
||||
building the composition.* An earlier reading of the paragraph below had the controller writing the
|
||||
whole file. It writes only the user list. A server's ports, its TLS paths and its store directory
|
||||
are properties of the container the module raises — they live in its image and its mounts and change
|
||||
when it does — so the module declares its own configuration and `include`s the mesh's half. A
|
||||
controller that wrote the whole file would have to be kept in step with a Dockerfile it never sees,
|
||||
and a module could not change its own image without the mesh agreeing. Asking for the user list is
|
||||
not enough to receive it: the file holds every user's password hash, so the claim on `mesh-broker` is
|
||||
what authorises it. And the two files share one directory of necessity — an absolute include path is
|
||||
resolved relative to the including file's own directory, so a server given one from elsewhere looks
|
||||
for it underneath that directory and refuses to start.
|
||||
|
||||
**Accounts are configuration, not API calls.** The controller composes the mesh's user list and its
|
||||
permissions into a file the host declares. **How that file reaches the running server is §5's,
|
||||
not this one's** — revision, first review: an earlier draft said "reloads" and cited a precedent
|
||||
that does not apply to a container (see §5). No management API, no credential travelling through a
|
||||
|
||||
@@ -178,23 +178,38 @@ paper is wrong until there is a second mesh to find out.
|
||||
|
||||
**Out, and what each needs.**
|
||||
|
||||
*Delivery.* Resolution already prepends `file` resources whose content comes from the
|
||||
rendering — a certificate does exactly this, and refuses when a module asks for one and none
|
||||
was issued. The composed configuration is the same shape, and the open question is what the
|
||||
module *declares* in order to receive it, which is design 29's ground rather than this
|
||||
document's: a field naming where it wants the file, or nothing at all because the module
|
||||
holding `mesh-broker` is the one that gets it. The server's own values — ports, TLS paths,
|
||||
store directory — are constants of the module's own resources today and would have to be read
|
||||
from one place rather than two.
|
||||
**Delivery is in, and it settled what a module declares.** The mesh writes the *accounts* and
|
||||
the module owns its *server*. The alternative was a manifest field enumerating ports, TLS
|
||||
paths and a store directory so the controller could write a whole configuration — wrong,
|
||||
because those are properties of the container the module raises and the controller would have
|
||||
to be kept in step with a Dockerfile it never sees. So a module declares its own configuration
|
||||
as a file resource and `bus-users` names where the mesh's half goes beside it; **asking is not
|
||||
enough to receive it**, because that file holds every user's password hash, so the claim on
|
||||
`mesh-broker` is what authorises it.
|
||||
|
||||
*Minting, and it is transport-coupled.* A password is minted at enrolment and at assignment,
|
||||
Two things a running server changed. **An absolute include path is resolved relative to the
|
||||
including file's directory** — `include /etc/nats/accounts.conf` from another directory makes
|
||||
the server look for it *under* that directory and refuse to start — so both files share one.
|
||||
And **`verify: true` was refusing every connection in the mesh**: it makes the server demand a
|
||||
*client* certificate, and nothing in the mesh presents one — a host pins this server's exact
|
||||
certificate and authenticates with the password the mesh minted ([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md),
|
||||
design 25 §4). Every connection would have died at the TLS handshake before any password was
|
||||
looked at, with an error that reads as a fault in the client. Removed; TLS is still required,
|
||||
because the block is what requires it and `verify` only decides whether client certificates
|
||||
are checked. **Design 25 §4 should say this**, and says nothing about it today.
|
||||
|
||||
Also collected here: task 1.2's payoff, end to end against the module's own image — the user
|
||||
list rewritten, the module noticing and reloading the server itself with no signal from
|
||||
outside, and the connection the mesh already had still working afterwards.
|
||||
|
||||
*Still out — minting, and it is transport-coupled.* A password is minted at enrolment and at assignment,
|
||||
and an enrolment reply carries exactly one. **A node on the old bus must not be handed a
|
||||
credential for the new one**, so which bus a node is joining has to be a fact the controller
|
||||
holds before it can mint for both — the same switch the host's `Transport` is, from the other
|
||||
end.
|
||||
|
||||
*Asserting on start.* The streams, the controller's consumers and every node's are defined
|
||||
and idempotent; nothing calls them from a start path yet.
|
||||
*Still out — asserting on start.* The streams, the controller's consumers and every node's
|
||||
are defined and idempotent; nothing calls them from a start path yet.
|
||||
|
||||
**People are not in the list**, deliberately: the account model is built and `operator issue`
|
||||
is not (4.4), so there is nobody to derive. Left empty rather than guessed at.
|
||||
|
||||
Reference in New Issue
Block a user