1.7 in progress: the list is derived and the keys are kept

What is in: a bus user's hash is recorded and its plaintext returned once, and
the user list is derived from the machines, what each runs, every manifest and
which machines hold a live token. Permissions stay derived rather than stored,
because a stored copy could disagree with the records it came from while both
looked internally consistent.

What is out, with what each needs, so the next person does not rediscover it:
delivery, which has one open question about what a module declares in order to
receive the file — design 29's ground, not this document's; minting, which is
transport-coupled because an enrolment reply carries one password and a node on
the old bus must not be handed a credential for the new one; and calling the
assertions from a start path.
This commit is contained in:
2026-09-27 01:50:04 +02:00
parent 970da74136
commit 8c91ba1cfa
+47 -1
View File
@@ -150,9 +150,55 @@ paper is wrong until there is a second mesh to find out.
and names no broker module anywhere in its source. What remains is naming `nats` instead of
the deprecated broker where a genesis module set is declared, which is scenario and installer
configuration — carried with 1.6 rather than before it.
- [ ] 1.7 **the composition, delivered** — the controller gathering its principals, composing the
- [~] 1.7 **the composition, delivered** — the controller gathering its principals, composing the
file, and asserting the streams and consumers on start.
**In**: the user list is derived from the mesh's records and the credentials are kept.
A bus user's bcrypt hash is now recorded, keyed by the username the file needs, and the
plaintext is returned exactly once. That state is new and the reason is worth stating: on the
bus the mesh runs on today an account is a management call — mint, hand over, seal to the
holder, keep nothing — and that works because the broker remembers. Here the users are one
file rewritten whenever any of it changes, so keeping nothing would mean **the first person's
access change silently blanking every module's password**.
**Permissions are not kept, only credentials.** Authority is derived from what each module
declares every time the file is written ([ADR 0043](../../02-DECISIONS/0043-a-module-broker-account-is-scoped-by-emits-and-consumes.md));
a stored permission list would be a second account of a user's authority, able to disagree
with the records it came from while both looked internally consistent.
The derivation refuses two things where they can still be named: two users with one name — the
server reads the file as one of them and which one depends on the order — and a module
assigned but absent from the catalogue, which would compose a user with no authority and fail
on its first publish with an authorisation error that says nothing about a missing manifest.
A user the mesh has minted no password for is *named* rather than dropped or written as a user
anybody is: an ordinary situation with an obvious remedy, and the caller decides whether a
partial file is worth writing. A seat's protocol is gathered across the whole catalogue, not
from one manifest, because a seat is declared by one module and held by another.
**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.
*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.
**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.
> **This corrects a tick, not a decision.** Tasks 1.3 and 1.4 are ticked and they are honest
> about what they built — the composer, the derivation, the permission model, the stream and
> consumer definitions, the asserter, all pure and held by unit tests and a golden