Raise genesis on the bus the mesh runs on, from one bundle (issue 146, ADR 0331) #96

Merged
mesh-admin merged 2 commits from fix/146-genesis-on-the-bus into main 2026-10-12 21:11:52 +00:00
Contributor

What

  • examples/: one genesis bundle, foundation-first-node.lock, with the content of the NATS bundle; the bundle that raised the previous broker is deleted (ADR 0331 §2). Its header and README say what it raises and which parts are placeholders.
  • internal/bootstrap/rootsecrets.go: makes the controller's account on the bus instead of the previous broker's administrator. The password goes into the address placeholder nats://controller:bootstrap@ (MESH_BUS_NATS), and its bcrypt hash goes into the controller's entry in the carried user list. Both are kept at /var/lib/mesh-genesis/bus-controller.secret (plus .bcrypt), so a second run writes the same bundle. It no longer looks for guest:guest@ (§3).
  • BusPlaceholders / CheckTemplate (preflight, step 1): a template whose control plane has no MESH_BUS_NATS, a bus address other than the placeholder, no MESH_BROKER_CERTIFICATE, or no controller entry in the user list is refused before anything is changed. The refusal names the placeholder or variable it looked for (§3, §4). Before this, the verify step waited 3 minutes on a crash-looping controller.
  • The temporary controller's MESH_BUS_NATS is the bus on the machine's loopback (it runs on the host network), with the made account, pinned through MESH_BROKER_CERTIFICATE (§4). A given bus port now moves the bus's published port and this address; the previous broker's AMQP/management rewrites are gone.
  • Broker step (step 18): it adopts nats, not lavinmq. It refuses by name, before registering anything, if the catalogue holds no nats module or if the module's server is not the foundation's container (§1). Ports setting and the adopted-node list name nats.
  • The installer's placing of the user list (enrol.go) is unchanged and runs only while enrolling; a test now asserts that a re-run does not place it (§5).

Not done, split out

Issue 590 covers the in-place adoption at step 18. The catalogue's nats module runs its server as mesh-broker-nats (its own built image, 4222:4222, other paths). The bundle's bus is mesh-broker (5671:4222, docker volumes). So the broker step now stops there with a refusal that names both, instead of raising a second bus. Which side changes needs a decision.

How it was tested

  • New tests: TestTheProducedBundleCarriesNoWellKnownCredential (the made account is in both places and the bootstrap hash is gone), TestABundleNamingNoBusConnectionItKnowsIsRefusedByItsPlaceholder, TestTheBusAccountIsKeptWithItsHash, TestATemplateWhoseControlPlaneHasNoBusIsRefusedBeforeAnythingIsRaised, TestTheBrokerStepAdoptsTheBusAsTheNatsModule, TestTheBrokerStepFailsForAModuleTheCatalogueDoesNotHold, TestABusModuleWhoseServerIsNotTheFoundationsIsRefused, examples: TestThereIsOneGenesisBundle, TestTheControlPlaneIsGivenTheBus. On main the examples tests fail (two bundles, lavinmq, no MESH_BUS_NATS) and the bootstrap tests do not compile.
  • sh merge-check.sh: pass (gofmt, vet, full suite with -race; the tests that need a real bus were skipped because g14 has no nats-server).
  • mesh-lab's R92 bed (mesh-lab #85), run with go run ./replays/cmd/prove R92 on g14 with the replay's Fix pinned locally to this head (37947ca), controller 3cfc121, catalogue c5e1bc7. On this commit the bed passes: genesis runs from bare past step 9 (registry) and step 10 (publish). On main (d8e785b) it stops at step 6 of 24, temp-mesh-controller restarting (this issue's symptom). The prover itself prints NOT PROVED for R92, because its "before" side is now main, which never reaches the registry step. That is R92's register to settle in #85, not this change.

Fixes issue 146 (with mesh-lab's PR naming the one bundle).

## What - `examples/`: one genesis bundle, `foundation-first-node.lock`, with the content of the NATS bundle; the bundle that raised the previous broker is deleted (ADR 0331 §2). Its header and README say what it raises and which parts are placeholders. - `internal/bootstrap/rootsecrets.go`: makes the controller's account on the bus instead of the previous broker's administrator. The password goes into the address placeholder `nats://controller:bootstrap@` (MESH_BUS_NATS), and its bcrypt hash goes into the controller's entry in the carried user list. Both are kept at `/var/lib/mesh-genesis/bus-controller.secret` (plus `.bcrypt`), so a second run writes the same bundle. It no longer looks for `guest:guest@` (§3). - `BusPlaceholders` / `CheckTemplate` (preflight, step 1): a template whose control plane has no MESH_BUS_NATS, a bus address other than the placeholder, no MESH_BROKER_CERTIFICATE, or no controller entry in the user list is refused before anything is changed. The refusal names the placeholder or variable it looked for (§3, §4). Before this, the verify step waited 3 minutes on a crash-looping controller. - The temporary controller's MESH_BUS_NATS is the bus on the machine's loopback (it runs on the host network), with the made account, pinned through MESH_BROKER_CERTIFICATE (§4). A given bus port now moves the bus's published port and this address; the previous broker's AMQP/management rewrites are gone. - Broker step (step 18): it adopts `nats`, not `lavinmq`. It refuses by name, before registering anything, if the catalogue holds no nats module or if the module's server is not the foundation's container (§1). Ports setting and the adopted-node list name `nats`. - The installer's placing of the user list (enrol.go) is unchanged and runs only while enrolling; a test now asserts that a re-run does not place it (§5). ## Not done, split out Issue 590 covers the in-place adoption at step 18. The catalogue's nats module runs its server as `mesh-broker-nats` (its own built image, 4222:4222, other paths). The bundle's bus is `mesh-broker` (5671:4222, docker volumes). So the broker step now stops there with a refusal that names both, instead of raising a second bus. Which side changes needs a decision. ## How it was tested - New tests: `TestTheProducedBundleCarriesNoWellKnownCredential` (the made account is in both places and the bootstrap hash is gone), `TestABundleNamingNoBusConnectionItKnowsIsRefusedByItsPlaceholder`, `TestTheBusAccountIsKeptWithItsHash`, `TestATemplateWhoseControlPlaneHasNoBusIsRefusedBeforeAnythingIsRaised`, `TestTheBrokerStepAdoptsTheBusAsTheNatsModule`, `TestTheBrokerStepFailsForAModuleTheCatalogueDoesNotHold`, `TestABusModuleWhoseServerIsNotTheFoundationsIsRefused`, `examples: TestThereIsOneGenesisBundle`, `TestTheControlPlaneIsGivenTheBus`. On main the examples tests fail (two bundles, lavinmq, no MESH_BUS_NATS) and the bootstrap tests do not compile. - `sh merge-check.sh`: pass (gofmt, vet, full suite with -race; the tests that need a real bus were skipped because g14 has no nats-server). - mesh-lab's R92 bed (mesh-lab #85), run with `go run ./replays/cmd/prove R92` on g14 with the replay's Fix pinned locally to this head (37947ca), controller 3cfc121, catalogue c5e1bc7. **On this commit the bed passes**: genesis runs from bare past step 9 (registry) and step 10 (publish). On main (d8e785b) it stops at step 6 of 24, `temp-mesh-controller` restarting (this issue's symptom). The prover itself prints NOT PROVED for R92, because its "before" side is now main, which never reaches the registry step. That is R92's register to settle in #85, not this change. Fixes issue 146 (with mesh-lab's PR naming the one bundle).
mesh-admin added 1 commit 2026-10-12 20:59:28 +00:00
Raise genesis on the bus the mesh runs on, from one bundle (hq issue 146, ADR 0331)
mesh/merge-gate head 37947ca4eed1 merged into trunk d8e785bb4b4b — pass: the change touches no module of the mesh's graph
mesh/repo-check head 37947ca4eed1 merged into trunk d8e785bb4b4b — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
37947ca4ee
The installer could not raise a first node: the old bundle's control plane had no
MESH_BUS_NATS and crash-looped, and the NATS bundle was refused because rootsecrets
only knew the previous broker's guest:guest@ template.

One bundle now, the NATS one. rootsecrets makes the controller's bus account and puts
it in both placeholders (the address and the bcrypt hash in the carried user list),
kept with its hash so a re-run writes the same bundle. Preflight refuses a template
naming no bus connection it knows, naming the placeholder, instead of waiting out the
verify step. The broker step adopts the bus as the nats module and refuses, by name,
a catalogue without it or a server that is not the foundation's container.
Author
Contributor

Delivery novox/mesh-host@cb55a4640050 — delivered since 2026-10-12T21:12:11Z

Delivery plan — builds nothing: the change touches no module of the mesh's graph

Transitions

  • 2026-10-12 21:06 (new) → proposed (announced): novox/mesh-host#96's head announced
  • 2026-10-12 21:09 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
  • 2026-10-12 21:09 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
  • 2026-10-12 21:12 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
  • 2026-10-12 21:12 published → delivered (done): nothing for a walk to move

The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.

<!-- mesh-delivery:view --> **Delivery** `novox/mesh-host@cb55a4640050` — **delivered** since 2026-10-12T21:12:11Z **Delivery plan** — builds nothing: the change touches no module of the mesh's graph **Transitions** - 2026-10-12 21:06 (new) → proposed (announced): novox/mesh-host#96's head announced - 2026-10-12 21:09 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit - 2026-10-12 21:09 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail - 2026-10-12 21:12 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move - 2026-10-12 21:12 published → delivered (done): nothing for a walk to move The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
mesh-admin left a comment
Author
Contributor

Verdict: request changes at 37947ca4ee

Independent review against ADR 0331 (accepted, v4). One finding blocks; the rest holds.

Blocking: a re-run reverts the bus's user list (§5)

The bundle's bus-accounts file resource carries the first user list (the controller's entry only). The apply step (step 4) runs on every genesis run, so a re-run on a machine that has enrolled overwrites /var/lib/mesh-bus-conf/accounts.conf with that one-entry list. The enrol step then skips placeTheBusUsers on a machine that already holds an identity, and the new assertion in TestAMachineThatHasAlreadyEnrolledIsNotEnrolledAgain pins that skip as §5 behaviour.

§5 says the installer's step stops only from the adoption on. In the state this PR leaves, adoption never happens: step 18 refuses (issue 590). So "already enrolled" is being read as "adopted". Since the broker container has no restart-on for the file, nothing breaks at once; at the next restart of mesh-broker the first node's own account is gone from the bus. The re-run is the expected path here, because the operator re-runs genesis once 590 is settled.

What would fix it (pick one): on a re-run, place the controller's composed list again after apply while the bus is not yet the nats module (the condition is "not adopted", not "enrolled"); or make the bundle's list a starting point that is written only when the file is absent. Adjust the enrol test to match. A test for this: a re-run on an enrolled, not-adopted machine leaves the composed list in place.

Clause by clause

  • §1: the broker step registers nats (BusModule), genesisTakes and the ports setting name it, and lavinmq is gone from the installer. Adoption refuses by name before anything is registered when the module's server container is not mesh-broker. I read the catalogue's modules/nats/module.json: its server is mesh-broker-nats, 4222:4222, /var/lib/mesh-broker-nats. So today the step always refuses, as the PR says. Refusing there is right. Raising a second bus beside the foundation's would split the mesh's streams and user list across two servers, which is exactly what §1 and ADR 0131 rule out. Issue 590 is honest: it names both layouts, the 5671/4222 port question, and severity major (genesis cannot complete). It is open with needs: decision.
  • §2: one bundle under examples/, with the NATS content, and the old bundle is deleted. TestThereIsOneGenesisBundle lists the directory.
  • §3: rootsecrets makes a 40-character password at /var/lib/mesh-genesis/bus-controller.secret and its bcrypt hash at .bcrypt. Both are written at 0600 in a 0700 directory (write-then-rename). The password goes into nats://controller:bootstrap@, and the hash replaces the controller's entry in the carried user list. keptHash re-uses the kept hash only while it still verifies against the password, so a re-run writes the same bundle (TestTheBusAccountIsKeptWithItsHash). guest:guest@ is gone. Masking replaces the password in every reported line. The hash is not masked, which is fine. The refusals quote no password (redactURL, and the test checks it). The preflight refusal names MESH_BUS_NATS, the nats://controller:bootstrap@ placeholder, MESH_BROKER_CERTIFICATE, or the user-list placeholder, whichever is missing.
  • §4: the temporary controller gets MESH_BUS_NATS=nats://controller:<made>@127.0.0.1:5671 and MESH_BROKER_CERTIFICATE=/broker-tls/tls.crt. A template without them is refused at preflight instead of at verify, which meets "fails instead of waiting" earlier than the ADR asks. Loopback as the "private address" is a sound reading. The controller runs on the host network on the same machine. Loopback is the narrowest address that reaches the bus. The pin is by exact certificate (ADR 0004), not by name, so the address does not have to match the certificate. The permanent controller is on the same node and is handed the same value by secret accept.
  • §5: see the blocking finding.
  • §6: the PR reports the R92 bed passing past step 9 and step 10 on this head, and stopping at step 6 on main. I did not re-run the bed. Note that steps 11 to 17 are not shown passing anywhere. Issue 590 says they are "unaffected", and that is unproven.

Ports

moduleSettings maps the module's declared 4222 to the node's bus port (5671 by default), so the adopted server would keep the port every enrolment token names. That is the right direction. Two small notes: the comment "a converged genesis on the defaults sets nothing" is no longer true for nats. FoundationPorts also still carries and checks AMQP and Management, which nothing uses now. Both can go with 590.

Running nodes

mesh/merge-gate passes with "the change touches no module of the mesh's graph": only genesis code and examples move, and nothing on a running node changes.

What I ran (scratch clone at this head, GOTMPDIR=/var/tmp)

  • go vet ./...: clean. go test -race -count=1 ./...: all packages ok. sh merge-check.sh: exit 0.
  • Tests fail on main: with the new test files on d8e785b, the examples package fails TestThereIsOneGenesisBundle, TestTheControlPlaneIsGivenTheBus and the image-set test, and internal/bootstrap does not compile (BusModule, CheckTemplate, BusVar, RootCredentials.Bus are missing).
  • Statuses at this head: mesh/merge-gate success, mesh/repo-check success, mesh/delivery success ("ready: it delivers once merged").
Verdict: request changes at 37947ca4eed1838940f381a4ed6fa75bdb80e169 Independent review against ADR 0331 (accepted, v4). One finding blocks; the rest holds. ## Blocking: a re-run reverts the bus's user list (§5) The bundle's `bus-accounts` file resource carries the first user list (the controller's entry only). The apply step (step 4) runs on every genesis run, so a re-run on a machine that has enrolled overwrites `/var/lib/mesh-bus-conf/accounts.conf` with that one-entry list. The enrol step then skips `placeTheBusUsers` on a machine that already holds an identity, and the new assertion in `TestAMachineThatHasAlreadyEnrolledIsNotEnrolledAgain` pins that skip as §5 behaviour. §5 says the installer's step stops only *from the adoption on*. In the state this PR leaves, adoption never happens: step 18 refuses (issue 590). So "already enrolled" is being read as "adopted". Since the broker container has no `restart-on` for the file, nothing breaks at once; at the next restart of `mesh-broker` the first node's own account is gone from the bus. The re-run is the expected path here, because the operator re-runs genesis once 590 is settled. What would fix it (pick one): on a re-run, place the controller's composed list again after apply while the bus is not yet the `nats` module (the condition is "not adopted", not "enrolled"); or make the bundle's list a starting point that is written only when the file is absent. Adjust the enrol test to match. A test for this: a re-run on an enrolled, not-adopted machine leaves the composed list in place. ## Clause by clause - §1: the broker step registers `nats` (BusModule), `genesisTakes` and the ports setting name it, and `lavinmq` is gone from the installer. Adoption refuses by name before anything is registered when the module's server container is not `mesh-broker`. I read the catalogue's `modules/nats/module.json`: its server is `mesh-broker-nats`, `4222:4222`, `/var/lib/mesh-broker-nats`. So today the step always refuses, as the PR says. **Refusing there is right.** Raising a second bus beside the foundation's would split the mesh's streams and user list across two servers, which is exactly what §1 and ADR 0131 rule out. Issue 590 is honest: it names both layouts, the 5671/4222 port question, and severity major (genesis cannot complete). It is open with `needs: decision`. - §2: one bundle under `examples/`, with the NATS content, and the old bundle is deleted. `TestThereIsOneGenesisBundle` lists the directory. - §3: rootsecrets makes a 40-character password at `/var/lib/mesh-genesis/bus-controller.secret` and its bcrypt hash at `.bcrypt`. Both are written at 0600 in a 0700 directory (write-then-rename). The password goes into `nats://controller:bootstrap@`, and the hash replaces the controller's entry in the carried user list. `keptHash` re-uses the kept hash only while it still verifies against the password, so a re-run writes the same bundle (`TestTheBusAccountIsKeptWithItsHash`). `guest:guest@` is gone. `Masking` replaces the password in every reported line. The hash is not masked, which is fine. The refusals quote no password (`redactURL`, and the test checks it). The preflight refusal names `MESH_BUS_NATS`, the `nats://controller:bootstrap@` placeholder, `MESH_BROKER_CERTIFICATE`, or the user-list placeholder, whichever is missing. - §4: the temporary controller gets `MESH_BUS_NATS=nats://controller:<made>@127.0.0.1:5671` and `MESH_BROKER_CERTIFICATE=/broker-tls/tls.crt`. A template without them is refused at preflight instead of at verify, which meets "fails instead of waiting" earlier than the ADR asks. **Loopback as the "private address" is a sound reading.** The controller runs on the host network on the same machine. Loopback is the narrowest address that reaches the bus. The pin is by exact certificate (ADR 0004), not by name, so the address does not have to match the certificate. The permanent controller is on the same node and is handed the same value by `secret accept`. - §5: see the blocking finding. - §6: the PR reports the R92 bed passing past step 9 and step 10 on this head, and stopping at step 6 on main. I did not re-run the bed. Note that steps 11 to 17 are not shown passing anywhere. Issue 590 says they are "unaffected", and that is unproven. ## Ports `moduleSettings` maps the module's declared 4222 to the node's bus port (5671 by default), so the adopted server would keep the port every enrolment token names. That is the right direction. Two small notes: the comment "a converged genesis on the defaults sets nothing" is no longer true for `nats`. `FoundationPorts` also still carries and checks `AMQP` and `Management`, which nothing uses now. Both can go with 590. ## Running nodes `mesh/merge-gate` passes with "the change touches no module of the mesh's graph": only genesis code and examples move, and nothing on a running node changes. ## What I ran (scratch clone at this head, GOTMPDIR=/var/tmp) - `go vet ./...`: clean. `go test -race -count=1 ./...`: all packages ok. `sh merge-check.sh`: exit 0. - Tests fail on main: with the new test files on d8e785b, the examples package fails `TestThereIsOneGenesisBundle`, `TestTheControlPlaneIsGivenTheBus` and the image-set test, and internal/bootstrap does not compile (BusModule, CheckTemplate, BusVar, RootCredentials.Bus are missing). - Statuses at this head: mesh/merge-gate success, mesh/repo-check success, mesh/delivery success ("ready: it delivers once merged").
jschoubben added 1 commit 2026-10-12 21:06:34 +00:00
Place the bus's users again on a re-run until the bus is adopted (hq issue 146, review 102)
mesh/merge-gate head cb55a4640050 merged into trunk 8e95c1d9ed2d — pass: the change touches no module of the mesh's graph
mesh/repo-check head cb55a4640050 merged into trunk 8e95c1d9ed2d — pass: its merge-check.sh passed
mesh/delivery delivered
cb55a46400
A re-run's apply writes the bundle's one-entry user list over the list placed at
enrolment, and an enrolled machine skipped placing it: enrolled was taken for adopted,
against ADR 0331 §5. Until the controller knows the nats module, the installer places
the composed list again. Also drop the previous broker's AMQP and management ports,
which nothing raises any more, and say why the bus always carries a ports setting.
Author
Contributor

Merge check at cb55a464: head cb55a46400 merged into trunk d8e785bb4b, pass

Out of date when it landed: out of date: judged against trunk d8e785bb4b, the trunk is now 8e95c1d9ed2d; judged again on a recheck or when it is merged

**Merge check** at `cb55a464`: head cb55a4640050 merged into trunk d8e785bb4b4b, pass **Out of date when it landed**: out of date: judged against trunk d8e785bb4b4b, the trunk is now 8e95c1d9ed2d; judged again on a recheck or when it is merged
mesh-admin left a comment
Author
Contributor

Verdict: approve at cb55a46400

Re-review against my review 102. The blocking finding is fixed, and the guard change opens nothing new. Before merging, the two required checks must be re-judged and read green: both say "out of date" because trunk moved to 8e95c1d.

Review 102's finding (ADR 0331 §5): fixed

  • On a re-run of an enrolled machine, Enrol now calls placeUntilAdopted. It asks the controller module list and re-places the controller's composed user list, then makes the bus re-read it, unless nats is listed. mentions matches the first word of a line exactly, so a listing like mesh-broker-nats cannot count as adopted. The condition is now "not adopted", not "enrolled", which is what §5 says.
  • TestARerunOnAnEnrolledMachineWhoseBusIsNotAdoptedPlacesTheUsersAgain fails at 37947ca (I ran it there) and passes here. The older enrol tests now answer module list with nats, so they cover the adopted branch.
  • There is no window between the two writes: apply (step 4) writes the bundle's list, and nothing makes the bus re-read it before enrol (step 7) places the composed list and sends the re-read signal.

The node-engine's reconcile (the builder's worry): no rewrite

I checked reconcileSource in cmd/mesh-host/main.go. Once a node holds a declaration from the mesh, reconcile applies only that declaration, and the bundle "was consumed at genesis". The genesis-produced bundle at /var/lib/mesh-host/foundation.lock is never read by the node-engine. It falls back only to the bundle compiled into the binary, and only while nothing has been declared. The installer's binary carries the small per-system bundles in internal/bundle, not this one. No controller declaration names /var/lib/mesh-bus-conf/accounts.conf before nats is adopted. So after genesis, only an installer re-run rewrites that file, and this change now covers the re-run.

One edge case predates this PR and is not blocking. A host built with make host BUNDLE=examples/foundation-first-node.lock that reconciles before any declaration arrives would apply the template with its bootstrap placeholders. That is the make host path, not genesis.

Guard on an adopted node: no new exposure

The guard now refuses only the store's port from outside the machine and the private network.

  • The AMQP port (5672) and the management port (15672) it guarded before are no longer published by anything in the bundle, so dropping them from the guard opens nothing.
  • The bus (5671:4222) is reachable from anywhere on purpose, because a node enrols over it. The foundation's own filter on a non-adopted node also accepts 5671 from anywhere, so the two node types agree. The bus requires TLS (the pinned certificate) and a per-user password. The carried user list has no anonymous or default user.
  • Monitoring (8222) is published on 127.0.0.1 only. Inside the container, nats.conf binds it to 127.0.0.1:8222, the container's own loopback, so even the loopback publish answers nothing. It is closed at two layers, and Docker's long-standing quirk of loopback publishes being reachable from the same network segment does not apply here.
  • Unchanged by this PR, and worth a line in issue 590: the registry (5000) and the package registry (3000) were not guarded before either.

Ports

The AMQP and management fields, the two flags, the free-port check and the usage text are all removed. The moduleSettings comment now says why the bus always carries a setting (4222 to 5671).

What I ran (GOTMPDIR=/var/tmp)

  • sh merge-check.sh at this head: exit 0. It reports the tests that need a real bus as not run, because there is no nats-server here.
  • Merged locally onto current trunk 8e95c1d: no conflicts, and the bootstrap, examples and cmd tests pass.
  • Statuses at this head: mesh/merge-gate and mesh/repo-check are both pending ("out of date: judged against trunk d8e785b, the trunk is now 8e95c1d"), and mesh/delivery is pending ("checking").
Verdict: approve at cb55a4640050c4c9d63cc785afff2c911d3588e5 Re-review against my review 102. The blocking finding is fixed, and the guard change opens nothing new. Before merging, the two required checks must be re-judged and read green: both say "out of date" because trunk moved to 8e95c1d. ## Review 102's finding (ADR 0331 §5): fixed - On a re-run of an enrolled machine, `Enrol` now calls `placeUntilAdopted`. It asks the controller `module list` and re-places the controller's composed user list, then makes the bus re-read it, unless `nats` is listed. `mentions` matches the first word of a line exactly, so a listing like `mesh-broker-nats` cannot count as adopted. The condition is now "not adopted", not "enrolled", which is what §5 says. - `TestARerunOnAnEnrolledMachineWhoseBusIsNotAdoptedPlacesTheUsersAgain` fails at 37947ca (I ran it there) and passes here. The older enrol tests now answer `module list` with `nats`, so they cover the adopted branch. - There is no window between the two writes: apply (step 4) writes the bundle's list, and nothing makes the bus re-read it before enrol (step 7) places the composed list and sends the re-read signal. ## The node-engine's reconcile (the builder's worry): no rewrite I checked `reconcileSource` in cmd/mesh-host/main.go. Once a node holds a declaration from the mesh, reconcile applies only that declaration, and the bundle "was consumed at genesis". The genesis-produced bundle at `/var/lib/mesh-host/foundation.lock` is never read by the node-engine. It falls back only to the bundle compiled into the binary, and only while nothing has been declared. The installer's binary carries the small per-system bundles in `internal/bundle`, not this one. No controller declaration names `/var/lib/mesh-bus-conf/accounts.conf` before `nats` is adopted. So after genesis, only an installer re-run rewrites that file, and this change now covers the re-run. One edge case predates this PR and is not blocking. A host built with `make host BUNDLE=examples/foundation-first-node.lock` that reconciles before any declaration arrives would apply the template with its `bootstrap` placeholders. That is the `make host` path, not genesis. ## Guard on an adopted node: no new exposure The guard now refuses only the store's port from outside the machine and the private network. - The AMQP port (5672) and the management port (15672) it guarded before are no longer published by anything in the bundle, so dropping them from the guard opens nothing. - The bus (`5671:4222`) is reachable from anywhere on purpose, because a node enrols over it. The foundation's own filter on a non-adopted node also accepts 5671 from anywhere, so the two node types agree. The bus requires TLS (the pinned certificate) and a per-user password. The carried user list has no anonymous or default user. - Monitoring (8222) is published on `127.0.0.1` only. Inside the container, `nats.conf` binds it to `127.0.0.1:8222`, the container's own loopback, so even the loopback publish answers nothing. It is closed at two layers, and Docker's long-standing quirk of loopback publishes being reachable from the same network segment does not apply here. - Unchanged by this PR, and worth a line in issue 590: the registry (5000) and the package registry (3000) were not guarded before either. ## Ports The AMQP and management fields, the two flags, the free-port check and the usage text are all removed. The `moduleSettings` comment now says why the bus always carries a setting (4222 to 5671). ## What I ran (GOTMPDIR=/var/tmp) - `sh merge-check.sh` at this head: exit 0. It reports the tests that need a real bus as not run, because there is no nats-server here. - Merged locally onto current trunk 8e95c1d: no conflicts, and the bootstrap, examples and cmd tests pass. - Statuses at this head: mesh/merge-gate and mesh/repo-check are both pending ("out of date: judged against trunk d8e785b, the trunk is now 8e95c1d"), and mesh/delivery is pending ("checking").
mesh-admin merged commit 6fe82c61bd into main 2026-10-12 21:11:52 +00:00
mesh-admin deleted branch fix/146-genesis-on-the-bus 2026-10-12 21:11:52 +00:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-host#96