WBS 3.5 done; 4.1 waits on one thing, an enrolment user per token

All three halves of the host's link are through seams, and the reply address in
the payload is now proved from both ends rather than one — the test asserts the
transport's own field held the consumer's ack address by the time the request
arrived, so a server that stopped claiming it fails a test instead of letting the
reason become folklore.

Two things had to be built for the host to hear anything at all: a node's
declaration consumer, which only the controller may create, and the enrolment
user's inbox, which design 25 §6 names and the composer granted none of. Both
were silent gaps — a node with either missing looks correct and hears nothing.

What remains is a single piece: something that composes an enrolment user per
live token. On the old bus that account is made imperatively through the broker's
management API; here there is no management API, so issuing a token has to
recompose the server's configuration. It is the only thing between the two links
and a mesh raised on NATS from nothing, so 4.1 now says so.
This commit is contained in:
2026-09-27 01:32:26 +02:00
parent f555d523c7
commit 0a61531c42
+45 -8
View File
@@ -284,17 +284,51 @@ pays for itself furthest away.
manifest and authority cannot come from a declaration that does not exist.
Still outstanding: a build's own shape, which travels with the builder in step 4.
- [~] 3.5 the host's link on NATS — **the outbound half is through a seam**, mirroring the
- [x] 3.5 the host's link on NATS — **all three halves are through seams**, mirroring the
controller's and still importing nothing of the mesh's own (ADR 0005): the host's own
interface over its own libraries, agreeing with the controller only because a fixture holds
interfaces over its own libraries, agreeing with the controller only because a fixture holds
both to one envelope. A report goes through JetStream because it is the message the
store-window guarantee is about; a heartbeat stays on core, because a heartbeat in a stream
is the mesh's least valuable message competing for retention with its most valuable.
store-window guarantee is about; a heartbeat stays on core, because a heartbeat in a stream is
the mesh's least valuable message competing for retention with its most valuable.
Still on the old client: dialling, the declaration consumer, and enrolment. The host's
**"newest wins" window narrows at the rollout rather than disappearing** — last-per-subject
makes catch-up the stream's and sequence orders definitively, but three pushes to a
connected node are still three deliveries. Recorded in the code where it is read.
`Link` is dialling, hearing and saying in one interface, because **dialling is where the
transport is chosen** and choosing it twice is how one half of a node ends up on a different
bus from the other. `Asking` is the enrolment conversation, and it is separate for the
opposite reason: almost nothing about it is the same, and a node that fails there is not in
the mesh at all.
**What the new bus took away, and what it would not give.** A host declares nothing here: on
the old bus it declares its own queue, because a queue that is not there means a node that
hears nothing, but the object it reads through now is a durable consumer and a host's account
reaches no part of the JetStream API. So it **binds** to one the mesh made, and a missing one
is said as the mesh's to answer rather than quietly created with whatever the client defaults
to. Two things that had to be built for that: a node's declaration consumer (named after the
node, because its ack grant is derived from the node's name, so any other name is a delivery
it cannot acknowledge), and the enrolment user's **inbox** — design 25 §6 names it and the
composer granted none, so an enrolling node would have published its request and waited out
its timeout against a mesh that answered.
**The reply address travels in the payload, and that is now proved from both ends.** The
controller reads it from there (3.4) and the host writes it there and waits on it, and the
test asserts the transport's own reply field held the *consumer's ack address* by the time the
request arrived — so a future server that stopped claiming that field fails a test rather than
letting the reason quietly become folklore.
The host's **"newest wins" window narrows at the rollout rather than disappearing**, and that
is now measured rather than predicted: three declarations pushed to an absent node leave one
on the stream and it is the newest, so the catch-up half is the stream's — but three pushes to
a connected node are still three deliveries, which is the half that stays.
**The pin turned out easier here than in the tool runtime, not harder.** The Go client takes a
`*tls.Config`, so the same pinned configuration with the same verify callback does the work;
the subject-alternative-name constraint recorded under 3.6 is that client's, because it takes
PEM strings with no verify hook. A host checks the fingerprint and nothing else.
Still outstanding: **something that composes an enrolment user per live token.** Nothing does,
on either bus — on the old one the account is made imperatively through the broker's
management API when a token is issued, and here there is no management API, so issuing a token
has to recompose the server's configuration. That is the last piece of enrolment on the new
bus, and it is the only thing between the two links and a mesh raised on NATS from nothing.
- [x] 3.6 the tool runtime's client on NATS, behind the unchanged sdk contract — round-tripped
against a real server: a tool answered across two connections, a throwing handler reaching
the caller as an error rather than a timeout, an event delivered once with its key, body,
@@ -380,6 +414,9 @@ it, and the beds that need a mesh living on NATS can finally run.
held by a `nak`-with-delay cycle still reaches the enrolling node, proving the reply travels
in the payload and not the transport field the consumer's ack has claimed. The server-enforced
permissions were proved at step 1 and are not re-proved here
— **waiting on one thing only**: an enrolment user composed per live token (3.5). Both links
speak NATS and every claim above has a unit test or a check against a running server behind
it; what no test can stand in for is a mesh raising itself, which is what this bed is
- [ ] 4.2 a build source's change reaches the builder over the bus, and the build that follows is
the one the change asked for — **blocked by
[issue 127](../../04-ISSUES/127-a-module-event-derives-a-subject-nothing-publishes/00-report.md)**