A one-node mesh, and twelve things that have to be true of it

The common case, and the one that was never tested as a whole. What existed
asked whether four machines converged; it never asked whether ONE machine ends
up holding a mesh.

The order was wrong too. Three machines were enrolled second, into a mesh that
could not yet produce a single module, and that was reported as though something
had been shown. 17-raising-a-mesh is explicit: genesis ends with a mesh that
RUNS, and what remains after the core modules are built is "adding machines".
So the core comes first and machines arrive last — here, not at all, because a
second node is only meaningful once the first is complete.

Three things were missing entirely and nothing complained, because nothing asked:
the mesh never built its own catalogue, never had a store of its own for that
catalogue to use, and never rebuilt its own control plane through the module
path.

And four checks that were absent rather than failing:

  - it can describe itself — status, module list, plan --json, and the
    catalogue's five tools ASKED rather than observed. A container being up was
    being read as the catalogue working, which is the same error as matching a
    container by substring and finding the wrong one.
  - its networking is what the modules asked for — default closed, ssh open,
    declared ports open, .internal names written, module networks present. Left
    out altogether, which is hard to defend given the firewall work this week.
  - a change to a module's source reaches the machine on its own. The capability
    the migration depends on.
  - it comes back after a reboot. Never once tested; the lab had no way to
    restart a machine, because nothing had ever needed one.

Machines are named by role now — anchor, home-server, workstation, laptop — not
after the operator's own nodes, which made test output and real state hard to
tell apart.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
2026-09-14 21:52:24 +02:00
parent 9146f30859
commit 7641bb2059
4 changed files with 859 additions and 84 deletions
+11 -11
View File
@@ -25,9 +25,9 @@
# the gap is a named failure rather than an absence.
#
# hosting (public, routable) home (private, behind the access point)
# novox 192.0.2.20 ── anchor ace 10.99.1.10 home server
# substrate, registry, shanks 10.99.1.20 workstation
# builder, control plane g14 10.99.1.30 workstation
# anchor 192.0.2.20 ── anchor home-server 10.99.1.10 home server
# substrate, registry, workstation 10.99.1.20 workstation
# builder, control plane laptop 10.99.1.30 workstation
#
# EGRESS IS NOT OPTIONAL HERE. With nothing loaded, a sealed machine stops at the installer's first
# pull. Every machine has a way out, and it is a SECOND path: each still reaches the rest of the
@@ -41,7 +41,7 @@
scenario: fresh-mesh
segments:
# The routable segment. novox lives here; its public address is the broker endpoint every token
# The routable segment. anchor lives here; its public address is the broker endpoint every token
# carries and the overlay hub the home nodes dial.
hosting:
kind: public
@@ -63,10 +63,10 @@ machines:
# The anchor. Raised by the installer into a mesh of one, and then asked to build.
#
# Sized for what it actually does here: the store, the broker, the registry, TWO control planes
# during the pivot, the builder, and a build workspace holding a Node toolchain image and an npm
# cache. It is NOT sized for the whole novox service set, because this bed does not run one — it
# during the pivot, the builder, and a build worksphome-server holding a Node toolchain image and an npm
# cache. It is NOT sized for the whole anchor service set, because this bed does not run one — it
# proves the machinery that would produce it.
novox:
anchor:
at: { segment: hosting, address: [192.0.2.20] }
egress: true
inbound: allow
@@ -77,21 +77,21 @@ machines:
# Three machines that JOIN. Host binary and a token, nothing else — no bootstrap, no substrate,
# no registry. They are deliberately small: what they are here to prove is that a joined machine
# can be given a module the mesh built, which is a question about credentials and not about load.
ace:
home-server:
at: { segment: home, address: [10.99.1.10] }
egress: true
inbound: allow
memory: 4GiB
cpus: 2
disk: 25GiB
shanks:
workstation:
at: { segment: home, address: [10.99.1.20] }
egress: true
inbound: allow
memory: 3GiB
cpus: 2
disk: 20GiB
g14:
laptop:
at: { segment: home, address: [10.99.1.30] }
egress: true
inbound: allow
@@ -102,5 +102,5 @@ machines:
# **No `images:` key, and that is the whole point of this file.** Anything a machine holds here, it
# pulled or the mesh built. See the header.
place:
plhome-server:
all: [host, runtime]