From 5d6e8fbe7aab2f12ee8ce3fe0aae663abfc10e65 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 16 Sep 2026 18:40:40 +0200 Subject: [PATCH] Rename mesh-control -> mesh-controller, substrate -> foundation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One name per thing, per the HQ glossary: the module/container/image/binary/repo becomes mesh-controller, the seat the-controller, and the store+broker pair the foundation (embedded base bundles, default template and example lock renamed with their go:embed directives). No behaviour change — a pure vocabulary rename. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx --- README.md | 14 +-- one-node-mesh-report.json | 34 +++--- provisioners/README.md | 2 +- scenarios/anthropic-bed.yml | 6 +- scenarios/audit-node.yml | 4 +- scenarios/catalogue-apps.yml | 8 +- scenarios/catalogue-media.yml | 6 +- scenarios/catalogue-mqtt.yml | 4 +- scenarios/catalogue-small.yml | 8 +- scenarios/first-node.yml | 4 +- scenarios/fresh-mesh.yml | 8 +- scenarios/genesis-single.yml | 10 +- scenarios/grafana-node.yml | 2 +- scenarios/growing-mesh.yml | 2 +- scenarios/lavinmq-bed.yml | 16 +-- scenarios/local-model-bed.yml | 6 +- scenarios/minio-node.yml | 2 +- scenarios/model-usage-bed.yml | 16 +-- scenarios/one-node-mesh.yml | 10 +- scenarios/openai-bed.yml | 4 +- scenarios/plex-node.yml | 4 +- scenarios/postgres-node.yml | 2 +- scenarios/redis-node.yml | 2 +- scenarios/route-forwarding.yml | 8 +- scenarios/schedule-tick.yml | 4 +- scenarios/sonarr-node.yml | 2 +- scenarios/tools-confluence.yml | 4 +- scenarios/tools-gitlab.yml | 4 +- scenarios/two-node-db.yml | 12 +- scenarios/two-nodes.yml | 4 +- scenarios/whole-mesh-ace.yml | 14 +-- scenarios/whole-mesh-full.yml | 24 ++-- scenarios/whole-mesh-novox.yml | 18 +-- scripts/build-route-proxy-image.sh | 8 +- src/declaration/types.ts | 4 +- src/lifecycle/egress.ts | 2 +- src/lifecycle/place.ts | 4 +- src/lifecycle/raise.ts | 2 +- src/lifecycle/supported.ts | 2 +- src/pinning.ts | 4 +- src/rebuild.ts | 6 +- src/repos.ts | 2 +- test/integration/anthropic-bed.test.ts | 34 +++--- test/integration/assigned-audit.test.ts | 30 ++--- .../assigned-catalogue-apps.test.ts | 26 ++--- .../assigned-catalogue-media.test.ts | 28 ++--- .../assigned-catalogue-mqtt.test.ts | 30 ++--- .../assigned-catalogue-small.test.ts | 28 ++--- test/integration/assigned-grafana.test.ts | 24 ++-- test/integration/assigned-model-usage.test.ts | 38 +++--- test/integration/assigned-plex.test.ts | 30 ++--- test/integration/assigned-redis.test.ts | 26 ++--- .../assigned-schedule-tick.test.ts | 28 ++--- test/integration/assigned-sonarr.test.ts | 22 ++-- .../assigned-tools-confluence.test.ts | 26 ++--- .../integration/assigned-tools-gitlab.test.ts | 26 ++--- test/integration/assigned-two-node-db.test.ts | 60 +++++----- test/integration/builds.test.ts | 16 +-- test/integration/canary.test.ts | 12 +- test/integration/certificates.test.ts | 2 +- test/integration/events.test.ts | 28 ++--- test/integration/fresh-mesh.test.ts | 22 ++-- test/integration/genesis-single.test.ts | 10 +- test/integration/genesis.ts | 40 +++---- test/integration/harness.ts | 12 +- test/integration/lavinmq-bed.test.ts | 48 ++++---- test/integration/local-model-bed.test.ts | 22 ++-- .../integration/mesh-grant-end-to-end.test.ts | 24 ++-- test/integration/mesh.test.ts | 94 +++++++-------- .../minio-grant-end-to-end.test.ts | 24 ++-- test/integration/objectstore.test.ts | 2 +- test/integration/one-node-mesh.test.ts | 50 ++++---- test/integration/openai-bed.test.ts | 28 ++--- .../postgres-grant-end-to-end.test.ts | 26 ++--- .../provider-on-backend-network.test.ts | 20 ++-- .../provider-uses-mesh-credential.test.ts | 24 ++-- test/integration/provisioner.test.ts | 2 +- test/integration/route-forwarding.test.ts | 28 ++--- .../runtime-restart-on-config.test.ts | 24 ++-- test/integration/whole-mesh-ace.test.ts | 26 ++--- test/integration/whole-mesh-full.test.ts | 110 +++++++++--------- test/integration/whole-mesh-novox.test.ts | 42 +++---- test/lastrun.test.ts | 6 +- test/pinning.test.ts | 20 ++-- test/place.test.ts | 30 ++--- test/rebuild.test.ts | 4 +- test/supported.test.ts | 8 +- test/validate.test.ts | 6 +- test/warm.test.ts | 2 +- 89 files changed, 785 insertions(+), 785 deletions(-) diff --git a/README.md b/README.md index d0b878f..e69ad53 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ test. | | **Bootstrap** | **Full** | |---|---|---| -| Contains | virtual machines, the node host, a pinned substrate bundle | a complete mesh: forge, control plane, delivery, modules | +| Contains | virtual machines, the node host, a pinned foundation bundle | a complete mesh: forge, control plane, delivery, modules | | Verdict from | what the host reports about the state it reconciled | a pipeline result ending in verify | | Exercises | tiers 0 and 1 | tier 2 and above, and modules | | Exists to | **develop the mesh** | **test what runs on it** | @@ -140,23 +140,23 @@ is written down here rather than reconstructed a third time. ```sh export MESH_LAB_HOST_BINARY=/mesh-host -export MESH_LAB_BUNDLE=/examples/substrate-first-node.lock -export MESH_LAB_MODULES=/examples/modules -export MESH_LAB_BUILDER=/build/mesh-builder # build/, which is git-ignored +export MESH_LAB_BUNDLE=/examples/foundation-first-node.lock +export MESH_LAB_MODULES=/examples/modules +export MESH_LAB_BUILDER=/build/mesh-builder # build/, which is git-ignored # The installer. `suite` builds it with mesh-host's `make bootstrap`, which embeds a `docker save` # of the control-plane image — so it is built AFTER that image, in the same run, or it carries a # stale one sealed inside a binary where nothing would ever notice. The whole-mesh bed raises its -# anchor by RUNNING this, rather than by applying a substrate bundle itself (novox/hq ADR 0067). +# anchor by RUNNING this, rather than by applying a foundation bundle itself (novox/hq ADR 0067). export MESH_LAB_BOOTSTRAP_BINARY=/mesh-bootstrap -export MESH_LAB_CONTROL_IMAGE=mesh-control:development # optional; what it carries +export MESH_LAB_CONTROL_IMAGE=mesh-controller:development # optional; what it carries # A checkout of the mesh's catalogue. The installer reads the registry's and the control plane's # manifests from a copy of it ON THE MACHINE, because at genesis there is no forge, no build # machine and — until the registry is up — nothing serving anything. export MESH_LAB_CATALOG=/modules -# Built with `go build -o ./examples/` in mesh-control. +# Built with `go build -o ./examples/` in mesh-controller. export MESH_LAB_PROVISIONER=/postgres-provisioner export MESH_LAB_OBJECTSTORE_PROVISIONER=/objectstore-provisioner export MESH_LAB_ROUTE_PROXY=/route-proxy diff --git a/one-node-mesh-report.json b/one-node-mesh-report.json index f8756e9..512c53c 100644 --- a/one-node-mesh-report.json +++ b/one-node-mesh-report.json @@ -1,18 +1,18 @@ { "scenario": "one-node-mesh", - "established": 21, + "established": 22, "of": 22, "steps": [ { "code": "R1", "title": "a bare machine becomes a mesh of one, raised by the installer", "status": "pass", - "seconds": 122, + "seconds": 316, "why": "" }, { "code": "R2", - "title": "the substrate is up — a store and a broker of the mesh's own", + "title": "the foundation is up — a store and a broker of the mesh's own", "status": "pass", "seconds": 1, "why": "" @@ -56,63 +56,63 @@ "code": "P1", "title": "the mesh builds the shared base from source", "status": "pass", - "seconds": 85, + "seconds": 3, "why": "" }, { "code": "P2", "title": "the mesh builds and runs a store of its own", "status": "pass", - "seconds": 38, + "seconds": 7, "why": "" }, { "code": "P3", "title": "the mesh builds and runs its own catalogue", "status": "pass", - "seconds": 31, + "seconds": 4, "why": "" }, { "code": "P4", "title": "the mesh rebuilds its own control plane from source", "status": "pass", - "seconds": 32, + "seconds": 30, "why": "" }, { "code": "N1", "title": "the mesh puts itself on a private network, and its machine has a name", "status": "pass", - "seconds": 6, + "seconds": 4, "why": "" }, { "code": "N2", "title": "the machine has a packet filter, loaded from what modules declared", "status": "pass", - "seconds": 7, + "seconds": 2, "why": "" }, { "code": "U1", "title": "the mesh builds a module standing on that base", "status": "pass", - "seconds": 13, + "seconds": 14, "why": "" }, { "code": "U2", "title": "the mesh runs a broker for that module to talk to", "status": "pass", - "seconds": 24, + "seconds": 26, "why": "" }, { "code": "U3", "title": "the anchor runs the module the mesh built", "status": "pass", - "seconds": 6, + "seconds": 7, "why": "" }, { @@ -140,22 +140,22 @@ "code": "V4", "title": "every resource the mesh declared is true on the machine", "status": "pass", - "seconds": 11, + "seconds": 13, "why": "" }, { "code": "E1", "title": "a change to a module's source reaches the machine on its own", "status": "pass", - "seconds": 13, + "seconds": 12, "why": "" }, { "code": "E2", "title": "the mesh comes back after the machine reboots", - "status": "fail", - "seconds": 31, - "why": "every container came back and no host agent did, so the machine is running the right things and can no longer be told anything:\namqp-ping\tUp 3 seconds\nmesh-lavinmq\tUp 3 seconds\nlavinmq\tUp 4 seconds (health: starting)\nmesh-control\tUp 3 seconds\nmesh-catalog\tUp 1 second\nmesh-postgres\tUp 4 seconds\npostgres\tUp 3 seconds\nmesh-builder\tUp 2 seconds\nmesh-registry\tUp 3 seconds\nmesh-broker\tUp 3 seconds (health: starting)\nmesh-store\tUp 3 seconds\n" + "status": "pass", + "seconds": 33, + "why": "" } ] } \ No newline at end of file diff --git a/provisioners/README.md b/provisioners/README.md index 13f5acc..1b97057 100644 --- a/provisioners/README.md +++ b/provisioners/README.md @@ -39,7 +39,7 @@ must reach the same state from wherever it starts. That means, in order: Step 2 is the half usually missing, and it is the same rule the host follows about removing what it declared and no longer declares. -The reference implementation lives in `mesh-control/examples/postgres-provisioner`, because that +The reference implementation lives in `mesh-controller/examples/postgres-provisioner`, because that is where the contract is defined and where the language is already set up to read it. The lab's job is the other half: raising a real PostgreSQL and proving that what the mesh delivered becomes a login that works, a rotation that takes effect, and a revocation that bites. diff --git a/scenarios/anthropic-bed.yml b/scenarios/anthropic-bed.yml index 02199c6..c2529a0 100644 --- a/scenarios/anthropic-bed.yml +++ b/scenarios/anthropic-bed.yml @@ -8,10 +8,10 @@ # The flow the test drives (OAuth stubbed, so it is the FLOW that is proven, not the vendor): # the manager module seals the refresh token to the node's PUBLIC key -> the host unseals it and # mounts the cleartext at the manager's bound path -> the manager calls the stub token endpoint -> -# submits back only { access token, re-sealed box } -> mesh-control seals the access token per +# submits back only { access token, re-sealed box } -> mesh-controller seals the access token per # consumer holder -> the consumer runtime writes ~/.claude/.credentials.json, access-token-only. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # Build BOTH runtime images into the local daemon first (the scenario stocks and serves them by # digest, which is where the host pulls them from): # scripts/build-module-runtime.sh anthropic-manager /tmp/anthropic-manager.tar @@ -35,7 +35,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development # The two model-access runtimes, built by scripts/build-module-runtime.sh into the local daemon and # loaded onto the machine, which holds them by their own image IDs. - mesh-runtime-anthropic-manager:development diff --git a/scenarios/audit-node.yml b/scenarios/audit-node.yml index 8107155..1eba0e2 100644 --- a/scenarios/audit-node.yml +++ b/scenarios/audit-node.yml @@ -1,6 +1,6 @@ # One machine that becomes a mesh and then assigns itself the audit logger. # -# The substrate is first-node's — a store, a broker, the control plane — and one module image on +# The foundation is first-node's — a store, a broker, the control plane — and one module image on # top: the tool runtime carrying the audit-logger (mesh-catalog). The node enrols itself and the # mesh assigns it the audit logger, so its events account is one the mesh delivered, not the # broker's own (novox/hq ADR 0048). @@ -20,7 +20,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development # The tool runtime with the audit-logger, built by scripts/build-runtime-image.sh into the local # daemon and loaded onto the machine, which holds it by its own image ID. - mesh-runtime-audit:development diff --git a/scenarios/catalogue-apps.yml b/scenarios/catalogue-apps.yml index 9a46c1d..5c92fdc 100644 --- a/scenarios/catalogue-apps.yml +++ b/scenarios/catalogue-apps.yml @@ -1,6 +1,6 @@ # One machine that becomes a mesh and is then assigned a SECOND wave of modules at once — the ones # converted this session (novox/hq ADR 0039/0048/0052): mongodb, unifi and marrytts, with postgres -# carried along as a known-good control. This is catalogue-small's sibling: same first-node substrate, +# carried along as a known-good control. This is catalogue-small's sibling: same first-node foundation, # same one-push co-residence, a different (and heavier) set of modules. # # The four exercise the three converted shapes: @@ -14,7 +14,7 @@ # None of the four share a directory, so the shared-workspace refusal (novox/hq 04-ISSUES/012) does # not bite; that class stays for a later bed. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # scripts/build-module-runtime.sh {mongodb,unifi,postgres} build the runtime images into the local # daemon (mongodb carries mongosh; unifi and postgres carry their CLIs). The service images # (mongo, unifi-controller, marytts, postgres) must be in the local daemon to be stocked. @@ -31,13 +31,13 @@ machines: egress: true inbound: allow # Sized up past catalogue-small's 6GiB: this wave carries two heavy JVM/embedded-DB service - # containers (the UniFi controller and MaryTTS) on top of the substrate, mongodb, postgres and + # containers (the UniFi controller and MaryTTS) on top of the foundation, mongodb, postgres and # three node runtimes — a dozen containers, two of them memory-hungry at startup. memory: 8GiB cpus: 4 images: - - mesh-control:development + - mesh-controller:development # The runtimes built by scripts/build-module-runtime.sh and stocked here. marrytts needs none. - mesh-runtime-mongodb:development - mesh-runtime-unifi:development diff --git a/scenarios/catalogue-media.yml b/scenarios/catalogue-media.yml index 9dc1534..1921ec9 100644 --- a/scenarios/catalogue-media.yml +++ b/scenarios/catalogue-media.yml @@ -16,7 +16,7 @@ # after enrol and before the push. Each module additionally OWNS its own config directory # (/services/{sonarr,radarr}/config), which the mesh does create. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # scripts/build-module-runtime.sh {sonarr,radarr} build the two runtime images into the local daemon # (they speak HTTP and need no CLI added). The service images lscr.io/linuxserver/{sonarr,radarr} # must be in the local daemon to be stocked. @@ -32,14 +32,14 @@ machines: at: { segment: hosting, address: [192.0.2.10] } egress: true inbound: allow - # Two *arr apps (server + runtime each) on top of the first-node substrate — seven containers. + # Two *arr apps (server + runtime each) on top of the first-node foundation — seven containers. # The Servarr images are lighter than catalogue-apps' JVM pair, so catalogue-small's 6GiB is # ample headroom. memory: 6GiB cpus: 4 images: - - mesh-control:development + - mesh-controller:development # The two runtimes built by scripts/build-module-runtime.sh and stocked here. - mesh-runtime-sonarr:development - mesh-runtime-radarr:development diff --git a/scenarios/catalogue-mqtt.yml b/scenarios/catalogue-mqtt.yml index 866b7c3..6da3e98 100644 --- a/scenarios/catalogue-mqtt.yml +++ b/scenarios/catalogue-mqtt.yml @@ -11,7 +11,7 @@ # the broker — so "the broker came up" is itself the proof the seed ran, because an unseeded store # crash-loops the broker. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # scripts/build-module-runtime.sh mosquitto builds mesh-runtime-mosquitto:development (carrying # mosquitto_ctrl and the compiled bootstrap entrypoint) into the local daemon, which this scenario # pulls from the internet over its uplink. eclipse-mosquitto:2 must be in the local @@ -32,7 +32,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development - mesh-runtime-mosquitto:development place: diff --git a/scenarios/catalogue-small.yml b/scenarios/catalogue-small.yml index f45c5f1..555be7a 100644 --- a/scenarios/catalogue-small.yml +++ b/scenarios/catalogue-small.yml @@ -3,14 +3,14 @@ # # The per-backend beds each assign one module: postgres (a database provider), minio (an object-store # provider), redis (a cache provider + tools), plex (a tools module). This raises the same first-node -# substrate and then assigns all four to the one anchor in a single push, so the proof is that they +# foundation and then assigns all four to the one anchor in a single push, so the proof is that they # resolve and come up TOGETHER on one node — nothing new about any single module, everything new about # their co-residence. # # The four are chosen because none of them share a directory, so the shared-workspace refusal # (novox/hq 04-ISSUES/012 — the media stack) does not bite here; that class stays for a later bed. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # scripts/build-module-runtime.sh {postgres,redis,minio,plex} build the four runtime images into the # local daemon (postgres carries psql, minio carries mc), which this scenario stocks and serves by # the internet over its uplink. Only the mesh's own images come from the local daemon. @@ -26,14 +26,14 @@ machines: at: { segment: hosting, address: [192.0.2.10] } egress: true inbound: allow - # Sized up: this anchor runs the substrate (store, broker, control) plus four modules — three of + # Sized up: this anchor runs the foundation (store, broker, control) plus four modules — three of # which are a server container and a runtime container each — so a dozen containers at once. The # single-module beds run at 3GiB; co-residence needs the headroom. memory: 6GiB cpus: 4 images: - - mesh-control:development + - mesh-controller:development # The four per-module runtimes, built by scripts/build-module-runtime.sh and stocked here. plex # needs no server image in the lab — its runtime serves tools with no Plex to reach. - mesh-runtime-postgres:development diff --git a/scenarios/first-node.yml b/scenarios/first-node.yml index 4065e23..56f7a07 100644 --- a/scenarios/first-node.yml +++ b/scenarios/first-node.yml @@ -1,4 +1,4 @@ -# One machine, raising a substrate from the bundle its host carries. +# One machine, raising a foundation from the bundle its host carries. # # This is the bootstrap class (novox/hq ADR 0009): no forge, no control plane to talk to, no # delivery. It exists to develop the steps of raising a mesh on a machine that has nothing but a @@ -24,7 +24,7 @@ machines: # is what pinning asks for (novox/hq ADR 0006). The store and the broker are ordinary third-party # images, and the machine pulls them from the internet like anything else. images: - - mesh-control:development + - mesh-controller:development place: all: [host, runtime] diff --git a/scenarios/fresh-mesh.yml b/scenarios/fresh-mesh.yml index 2a7f5c9..e5c0d85 100644 --- a/scenarios/fresh-mesh.yml +++ b/scenarios/fresh-mesh.yml @@ -11,7 +11,7 @@ # runtime and the host binary, which are prerequisites of the machine rather than parts of the # mesh, and after that the mesh is on its own: # -# - the substrate, the registry and the builder's own dependencies are PULLED from the internet, +# - the foundation, the registry and the builder's own dependencies are PULLED from the internet, # which is where a bare machine gets them; # - the control plane is BUILT, by the builder the installer carries, from a repository and a # commit it is told to use; @@ -26,7 +26,7 @@ # # hosting (public, routable) home (private, behind the access point) # anchor 192.0.2.20 ── anchor home-server 10.99.1.10 home server -# substrate, registry, workstation 10.99.1.20 workstation +# foundation, 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 @@ -35,7 +35,7 @@ # the public images, and the forge the control plane is cloned from. # # MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BOOTSTRAP_BINARY=.../mesh-bootstrap -# MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # MESH_LAB_CATALOG=.../mesh-catalog/modules # MESH_LAB_SOURCE= MESH_LAB_SOURCE_REF= scenario: fresh-mesh @@ -74,7 +74,7 @@ machines: cpus: 6 disk: 60GiB - # Three machines that JOIN. Host binary and a token, nothing else — no bootstrap, no substrate, + # Three machines that JOIN. Host binary and a token, nothing else — no bootstrap, no foundation, # 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. home-server: diff --git a/scenarios/genesis-single.yml b/scenarios/genesis-single.yml index da12b61..898a281 100644 --- a/scenarios/genesis-single.yml +++ b/scenarios/genesis-single.yml @@ -2,10 +2,10 @@ # # The smallest thing that proves a mesh can be raised. One machine on a public segment with a way # out to the internet, the host placed, and nothing else — the installer carries the control -# plane's image and the substrate's own images are pulled over the uplink, exactly as they are on +# plane's image and the foundation's own images are pulled over the uplink, exactly as they are on # a bare machine. # -# The address matters: the substrate template names the broker at 192.0.2.10, and a token carries +# The address matters: the foundation template names the broker at 192.0.2.10, and a token carries # that address verbatim as the endpoint an enrolling node dials. With one machine, that machine # must BE it, or the mesh would hand out an endpoint nothing answers on. scenario: genesis-single @@ -23,7 +23,7 @@ machines: # bare machine. A scenario that needs no images can omit this; genesis cannot. egress: true inbound: allow - # Enough for the substrate (store, broker), the registry, and two control planes during the + # Enough for the foundation (store, broker), the registry, and two control planes during the # pivot. Smaller than the four-node bed's anchor, which also carries a whole service set. memory: 8GiB cpus: 4 @@ -33,8 +33,8 @@ machines: # # The runtime is not a mesh tier — it is a prerequisite of the machine, and the installer's first # step refuses to go on without one. Placing it here is the lab preparing a machine, not the lab -# describing an installation. Everything above tier 0 — the substrate, the registry, the control +# describing an installation. Everything above tier 0 — the foundation, the registry, the control # plane — is the installer's, and the lab places none of it. That is the whole point of this bed: -# if the lab placed the substrate, it would be describing installing all over again. +# if the lab placed the foundation, it would be describing installing all over again. place: all: [host, runtime] diff --git a/scenarios/grafana-node.yml b/scenarios/grafana-node.yml index e58814c..f3260f3 100644 --- a/scenarios/grafana-node.yml +++ b/scenarios/grafana-node.yml @@ -20,7 +20,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development - mesh-runtime-grafana:development place: diff --git a/scenarios/growing-mesh.yml b/scenarios/growing-mesh.yml index 0ab0f04..542b8fd 100644 --- a/scenarios/growing-mesh.yml +++ b/scenarios/growing-mesh.yml @@ -28,7 +28,7 @@ machines: inbound: allow images: - - mesh-control:development + - mesh-controller:development place: all: [host, runtime] diff --git a/scenarios/lavinmq-bed.yml b/scenarios/lavinmq-bed.yml index f20fcdc..a7f7745 100644 --- a/scenarios/lavinmq-bed.yml +++ b/scenarios/lavinmq-bed.yml @@ -1,28 +1,28 @@ -# Two machines: one is the substrate, the other carries a lavinmq PROVIDER and a consumer of it. +# Two machines: one is the foundation, the other carries a lavinmq PROVIDER and a consumer of it. # # lavinmq is the mesh's own control-plane broker (mesh-broker), and it is ALSO offered as a # user-facing capability: a module that needs a message queue gets its OWN broker — a scoped vhost and # user on a lavinmq PROVIDER — not an account on the control broker. That makes lavinmq the sharp -# two-node case, for the same reason two-node-db is: the substrate's broker publishes 5672 on the node +# two-node case, for the same reason two-node-db is: the foundation's broker publishes 5672 on the node # it runs on, and a lavinmq provider must publish 5672 too for its consumers to reach it — so the two # cannot share a machine. The moment a real amqp provider must own a node's 5672, it collides with the # control broker already there, and the chain is blocked single-node. # -# This is the split that unblocks it. `anchor` runs the substrate (store, broker, control) and NOTHING +# This is the split that unblocks it. `anchor` runs the foundation (store, broker, control) and NOTHING # else. `laptop` runs the whole chain: the lavinmq PROVIDER and the amqp-ping CONSUMER that requires # it. Provider and consumer are co-located on laptop, so the grant never crosses a node boundary; only -# enrolment crosses to anchor, over the underlay both machines share. And because the substrate broker +# enrolment crosses to anchor, over the underlay both machines share. And because the foundation broker # is on the OTHER node, the provider owns laptop's 5672 uncontested. # -# It needs a host binary and the substrate bundle: +# It needs a host binary and the foundation bundle: # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # # HELPER — stock the two runtimes into the local daemon before the run (some may already be there): # scripts/build-module-runtime.sh lavinmq /tmp/lavinmq.tar # scripts/build-module-runtime.sh amqp-ping /tmp/amqp-ping.tar # The service image cloudamqp/lavinmq:latest must be in the local daemon too — it is already stocked as -# the substrate's own broker image; each node pulls what it runs from the internet, over its own +# the foundation's own broker image; each node pulls what it runs from the internet, over its own # uplink. scenario: lavinmq-bed @@ -46,7 +46,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development # The lavinmq provider's runtime (reused for its run-once bootstrap and its provisioner) and the # amqp-ping consumer's runtime, both built by scripts/build-module-runtime.sh into the local daemon # and loaded onto the machines, which hold them by their own image IDs. diff --git a/scenarios/local-model-bed.yml b/scenarios/local-model-bed.yml index b76b292..b1ef7f5 100644 --- a/scenarios/local-model-bed.yml +++ b/scenarios/local-model-bed.yml @@ -11,9 +11,9 @@ # openai.env carries OPENAI_BASE_URL=http://:/v1. The test asserts that URL and that a # request to it reaches the running model server. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # Both modules are pure declaration (a server container + a templated file) — no module runtime image -# is built; the bed only stocks the substrate and the ollama server image. +# is built; the bed only stocks the foundation and the ollama server image. scenario: local-model-bed segments: @@ -31,7 +31,7 @@ machines: disk: 40GiB images: - - mesh-control:development + - mesh-controller:development place: all: [host, runtime] diff --git a/scenarios/minio-node.yml b/scenarios/minio-node.yml index 6347a7c..7163cf1 100644 --- a/scenarios/minio-node.yml +++ b/scenarios/minio-node.yml @@ -21,7 +21,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development # minio's runtime, built by scripts/build-module-runtime.sh minio (it carries mc), loaded onto # the machine. - mesh-runtime-minio:development diff --git a/scenarios/model-usage-bed.yml b/scenarios/model-usage-bed.yml index bd067b0..0e4790d 100644 --- a/scenarios/model-usage-bed.yml +++ b/scenarios/model-usage-bed.yml @@ -1,7 +1,7 @@ # The usage context store, proved end to end (novox/hq ADR 0054). A postgres PROVIDER and the -# model-usage CONSUMER ride one node; the substrate (store, broker, control) owns the other. As in -# two-node-db, the app-postgres provider and the mesh's own substrate store both want host port 5432, -# so they cannot share a machine — the substrate lives on `anchor` and NOTHING else, and `laptop` +# model-usage CONSUMER ride one node; the foundation (store, broker, control) owns the other. As in +# two-node-db, the app-postgres provider and the mesh's own foundation store both want host port 5432, +# so they cannot share a machine — the foundation lives on `anchor` and NOTHING else, and `laptop` # runs postgres plus model-usage. Provider and consumer are co-located on laptop, so only enrolment # crosses to anchor, over the underlay both machines already share. # @@ -9,7 +9,7 @@ # provisioned postgres store, at BOTH grains (licence and session, differing only in `consumer`), # LATEST-per-key, and IN THE CLEAR — an ordinary select returns the numeric value and its raw payload. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # Build BOTH runtime images into the local daemon first (the scenario stocks and serves them by # digest, which is where the host pulls them from): # scripts/build-module-runtime.sh postgres /tmp/postgres.tar @@ -22,7 +22,7 @@ segments: cidr: [192.0.2.0/24] machines: - # The substrate ONLY: store, broker, control — three containers. + # The foundation ONLY: store, broker, control — three containers. anchor: at: { segment: hosting, address: [192.0.2.10] } egress: true @@ -30,8 +30,8 @@ machines: memory: 4GiB cpus: 4 # The postgres PROVIDER (server + broker-bound runtime) and the model-usage CONSUMER (its run-once - # migrate and its long-lived event runtime). The 5432-vs-substrate conflict is gone because the - # substrate store is on the OTHER node. The runtime images plus postgres:17-alpine are pulled from + # migrate and its long-lived event runtime). The 5432-vs-foundation conflict is gone because the + # foundation store is on the OTHER node. The runtime images plus postgres:17-alpine are pulled from # the internet over the uplink; forty gigabytes holds them with room to spare. laptop: at: { segment: hosting, address: [192.0.2.20] } @@ -42,7 +42,7 @@ machines: disk: 40GiB images: - - mesh-control:development + - mesh-controller:development # The per-module runtimes, built by scripts/build-module-runtime.sh and stocked here. Each carries # its module's code — postgres its provisioner, model-usage its consumer, tools and run-once migrate. - mesh-runtime-postgres:development diff --git a/scenarios/one-node-mesh.yml b/scenarios/one-node-mesh.yml index fb62b03..6568191 100644 --- a/scenarios/one-node-mesh.yml +++ b/scenarios/one-node-mesh.yml @@ -2,7 +2,7 @@ # # The common case, and the one worth getting right first: a person with a single machine runs the # installer and ends up with a mesh that works. Not a mesh that *runs* — `17-raising-a-mesh` is -# careful about that difference, and so is this scenario. Genesis ends with a substrate, a registry, +# careful about that difference, and so is this scenario. Genesis ends with a foundation, a registry, # a built control plane and a builder, and a mesh in that state cannot produce anything and holds no # record of what it has. Calling that "up" is how the catalogue came to be missing from a test for # weeks without anything complaining. @@ -10,11 +10,11 @@ # So the test driving this asks the harder question: can this machine, given nothing but a container # runtime and the host binary, end up holding # -# - a substrate and a registry it pulled from the internet, +# - a foundation and a registry it pulled from the internet, # - a control plane it BUILT, and then rebuilt from its own repository through the module path, # - a builder that takes work over the broker, # - the shared base every module with code of its own stands on, -# - a store of its own — the substrate's is the control plane's own plumbing, not a provider, +# - a store of its own — the foundation's is the control plane's own plumbing, not a provider, # - a catalogue, so it can say what it has and what a change reaches, # - and a module of its own, built, provisioned and running. # @@ -26,7 +26,7 @@ # EGRESS IS NOT OPTIONAL. With nothing loaded, a sealed machine stops at the installer's first pull. # # MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BOOTSTRAP_BINARY=.../mesh-bootstrap -# MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # MESH_LAB_CATALOG=.../mesh-catalog/modules # MESH_LAB_SOURCE= MESH_LAB_SOURCE_REF= scenario: one-node-mesh @@ -37,7 +37,7 @@ segments: cidr: [192.0.2.0/24] machines: - # The address matters: the substrate template names the broker at a fixed address, and a token + # The address matters: the foundation template names the broker at a fixed address, and a token # carries that verbatim as the endpoint an enrolling node dials. With one machine, that machine # must BE it, or the mesh hands out an endpoint nothing answers on. # diff --git a/scenarios/openai-bed.yml b/scenarios/openai-bed.yml index ffac75a..16023ff 100644 --- a/scenarios/openai-bed.yml +++ b/scenarios/openai-bed.yml @@ -11,7 +11,7 @@ # OPENAI_API_KEY (env file + the Codex auth.json). The test asserts the written key equals the one # the operator set — through the sealed delivery path, unchanged. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # Build the consumer runtime image into the local daemon first (raise() stocks it from there): # scripts/build-module-runtime.sh openai-consumer /tmp/openai-consumer.tar scenario: openai-bed @@ -30,7 +30,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development # The static-key consumer runtime, built by scripts/build-module-runtime.sh into the local daemon and # loaded onto the machine, which holds it by its own image ID. - mesh-runtime-openai-consumer:development diff --git a/scenarios/plex-node.yml b/scenarios/plex-node.yml index dc72b8d..e2ffee2 100644 --- a/scenarios/plex-node.yml +++ b/scenarios/plex-node.yml @@ -1,7 +1,7 @@ # One machine that becomes a mesh and then assigns itself plex's tool runtime. # # The audit-node bed proved an assigned *consumer* (novox/hq ADR 0048). This proves an assigned -# module that *serves tools* (ADR 0052): the same first-node substrate, plus plex's tool runtime on +# module that *serves tools* (ADR 0052): the same first-node foundation, plus plex's tool runtime on # top. The node enrols itself, the mesh issues plex a broker account scoped to serve.plex.* and # assigns it, the host runs the runtime container, and a caller invokes plex.plex_reachable over the # mesh — proof the module runs its own code as its own process under its own scoped account. @@ -21,7 +21,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development # Plex's tool runtime, built by scripts/build-module-runtime.sh plex into the local daemon and # loaded onto the machine, which holds it by its own image ID. - mesh-runtime-plex:development diff --git a/scenarios/postgres-node.yml b/scenarios/postgres-node.yml index 913fa3f..3429b3e 100644 --- a/scenarios/postgres-node.yml +++ b/scenarios/postgres-node.yml @@ -20,7 +20,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development # postgres's runtime, built by scripts/build-module-runtime.sh postgres (it carries psql), loaded # onto the machine. - mesh-runtime-postgres:development diff --git a/scenarios/redis-node.yml b/scenarios/redis-node.yml index d075371..b8b4441 100644 --- a/scenarios/redis-node.yml +++ b/scenarios/redis-node.yml @@ -20,7 +20,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development # Redis's tool+provisioner runtime, built by scripts/build-module-runtime.sh redis into the local # daemon and loaded onto the machine, which holds it by its own image ID. - mesh-runtime-redis:development diff --git a/scenarios/route-forwarding.yml b/scenarios/route-forwarding.yml index 75da3b8..af84f47 100644 --- a/scenarios/route-forwarding.yml +++ b/scenarios/route-forwarding.yml @@ -14,9 +14,9 @@ # publicly-trusted certificate and answering an HTTP-01 challenge at the name — is proven separately # by certificates.test.ts against a real ACME server (Pebble), driving the same proxy binary. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # scripts/build-route-proxy-image.sh builds mesh-route-proxy:development into the local daemon -# (from mesh-control/examples/route-proxy, via mesh-catalog/modules/route-proxy/Dockerfile). +# (from mesh-controller/examples/route-proxy, via mesh-catalog/modules/route-proxy/Dockerfile). # alpine:latest must be in the local daemon — hello-web's backend is a bare alpine that serves a # fixed page over a busybox nc loop. Both images are stocked and served by digest. scenario: route-forwarding @@ -35,8 +35,8 @@ machines: cpus: 2 images: - - mesh-control:development - # The route-proxy's image, built from the canonical Go proxy in mesh-control by + - mesh-controller:development + # The route-proxy's image, built from the canonical Go proxy in mesh-controller by # scripts/build-route-proxy-image.sh, and hello-web's backend, a bare alpine nc loop. - mesh-route-proxy:development diff --git a/scenarios/schedule-tick.yml b/scenarios/schedule-tick.yml index e345da4..627e7d3 100644 --- a/scenarios/schedule-tick.yml +++ b/scenarios/schedule-tick.yml @@ -14,7 +14,7 @@ # - the container fires when the cron is due (top of the next minute); # - it fires AGAIN on the following minute — recurrence, not a one-shot. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_MODULES=.../mesh-control/examples/modules +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_MODULES=.../mesh-controller/examples/modules # alpine:latest must be in the local daemon; the machine pulls it from the internet over its # uplink, and the scheduled container declares it exactly as the catalogue writes it. # There is no runtime image: schedtest carries no code of its own — the scheduled container is a @@ -35,7 +35,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development place: # Only the host — schedtest has no mesh-runtime to place. The tick image is pulled from the diff --git a/scenarios/sonarr-node.yml b/scenarios/sonarr-node.yml index 8313f6e..70dcbeb 100644 --- a/scenarios/sonarr-node.yml +++ b/scenarios/sonarr-node.yml @@ -20,7 +20,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development - mesh-runtime-sonarr:development place: diff --git a/scenarios/tools-confluence.yml b/scenarios/tools-confluence.yml index 7fcc150..83b5d43 100644 --- a/scenarios/tools-confluence.yml +++ b/scenarios/tools-confluence.yml @@ -9,7 +9,7 @@ # built lazily and never throws at registration, so the runtime logs `[mesh-tools] serving 3 tool(s)` # and binds its serve queues regardless; a tool would only fail if it were actually invoked. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # scripts/build-module-runtime.sh confluence builds mesh-runtime-confluence:development into the # local daemon, which the machine pulls from the internet over its uplink. confluence # needs no service image — it is tools-only. @@ -29,7 +29,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development # confluence's runtime, built by scripts/build-module-runtime.sh confluence into the local daemon # and loaded onto the machine, which holds it by its own image ID. There is no # service image: confluence is tools-only and outbound-only. diff --git a/scenarios/tools-gitlab.yml b/scenarios/tools-gitlab.yml index e797999..ad5d92c 100644 --- a/scenarios/tools-gitlab.yml +++ b/scenarios/tools-gitlab.yml @@ -10,7 +10,7 @@ # throws at registration, so the runtime logs `[mesh-tools] serving 23 tool(s)` and binds its serve # queues regardless; a tool would only fail if it were actually invoked without real creds. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # scripts/build-module-runtime.sh gitlab builds mesh-runtime-gitlab:development into the local # daemon, which the machine pulls from the internet over its uplink. gitlab needs no # service image — it is tools-only. @@ -30,7 +30,7 @@ machines: cpus: 2 images: - - mesh-control:development + - mesh-controller:development # gitlab's runtime, built by scripts/build-module-runtime.sh gitlab into the local daemon and # loaded onto the machine, which holds it by its own image ID. There is no # service image: gitlab is tools-only and outbound-only. diff --git a/scenarios/two-node-db.yml b/scenarios/two-node-db.yml index 84b1313..dc923b6 100644 --- a/scenarios/two-node-db.yml +++ b/scenarios/two-node-db.yml @@ -1,13 +1,13 @@ # The DB-consumer chain a single node cannot host, proved across two machines. # -# The app-postgres provider and the mesh's own substrate store both want host port 5432, so they -# cannot share a machine — the collision that blocked this chain single-node. Here the substrate +# The app-postgres provider and the mesh's own foundation store both want host port 5432, so they +# cannot share a machine — the collision that blocked this chain single-node. Here the foundation # (store, broker, control) lives on `anchor` and NOTHING else; `laptop` runs the whole chain — # postgres and redis PROVIDERS plus the baserow and letta CONSUMERS that require them. Both # machines sit on one shared segment and enrol into the one mesh; only enrolment crosses to anchor, # over the underlay both machines already share. Provider and consumers are co-located on laptop, so -# no cross-node module comms and no overlay are needed — and the 5432-vs-substrate conflict is gone -# because the substrate store is on the OTHER node. +# no cross-node module comms and no overlay are needed — and the 5432-vs-foundation conflict is gone +# because the foundation store is on the OTHER node. scenario: two-node-db segments: @@ -16,7 +16,7 @@ segments: cidr: [192.0.2.0/24] machines: - # The substrate ONLY: store, broker, control — three containers. Four gigabytes is plenty for a + # The foundation ONLY: store, broker, control — three containers. Four gigabytes is plenty for a # node that hosts no modules; the thrash the two-nodes bed warns of comes from stacking eleven # containers on a node, which this one never does. anchor: @@ -43,7 +43,7 @@ machines: disk: 60GiB images: - - mesh-control:development + - mesh-controller:development # The per-module runtimes, built by scripts/build-module-runtime.sh and stocked here. Each carries # its module's provisioner, so no separate mesh-provision-* image is listed — the runtime is the # provisioner (ADR 0048). diff --git a/scenarios/two-nodes.yml b/scenarios/two-nodes.yml index 563ea5f..c046332 100644 --- a/scenarios/two-nodes.yml +++ b/scenarios/two-nodes.yml @@ -17,7 +17,7 @@ machines: at: { segment: hosting, address: [192.0.2.10] } egress: true inbound: allow - # The whole substrate, the registry, the builder, an adopted workload and the modules under + # The whole foundation, the registry, the builder, an adopted workload and the modules under # test all land here — eleven containers before the forge arrives. At the 1GiB default this # machine thrashes, and it presents as "the mesh hangs": every exec slows from 15s to 105s # and the forge test fails on a status poll that is merely queued behind page-outs. @@ -30,7 +30,7 @@ machines: memory: 2GiB images: - - mesh-control:development + - mesh-controller:development # And the builder, because it is a module the mesh assigns rather than a program somebody # starts by hand — which is the only way its credential can be one the mesh delivered. - mesh-builder:development diff --git a/scenarios/whole-mesh-ace.yml b/scenarios/whole-mesh-ace.yml index 6be3329..3ecb8f1 100644 --- a/scenarios/whole-mesh-ace.yml +++ b/scenarios/whole-mesh-ace.yml @@ -1,15 +1,15 @@ # The whole `ace` server's converted service set, installed together on ONE node behind the mesh -# substrate — the media/home-automation half of the whole-mesh rehearsal (novox/hq). Sibling of +# foundation — the media/home-automation half of the whole-mesh rehearsal (novox/hq). Sibling of # scenarios/whole-mesh-novox.yml; same topology, a different (larger, media-heavy) module set. # -# Substrate (store, broker, control) rides `anchor` and NOTHING else; ALL of ace's services ride the +# Foundation (store, broker, control) rides `anchor` and NOTHING else; ALL of ace's services ride the # `ace` node. An overlay is placed so the two DB consumers (baserow, letta) reach the postgres/redis # providers co-located with them. The media stack (sonarr/radarr/lidarr/plex/bazarr/nzbget/ # qbittorrent/bookshelf) shares the operator-owned library directories under /services/media (ADR # 0051 `accesses`); the test pre-creates them on the node, as the operator would, before the push — # the mesh confirms the paths exist and mounts them, but creates and chowns none of it. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # The runtimes are built by scripts/build-module-runtime.sh (one per module) and must be in the local # daemon, because nothing serves them and nothing can. Every media/app image is pulled from the # internet by the node itself, over its uplink, by the digest its module.json already pins. The test @@ -30,9 +30,9 @@ machines: memory: 4GiB cpus: 4 disk: 20GiB - # The substrate only, so the control plane's image only. The runtimes belong on the node that + # The foundation only, so the control plane's image only. The runtimes belong on the node that # runs the modules, and a 20GiB disk has no room for them anyway. - images: [mesh-control:development] + images: [mesh-controller:development] # The whole ace service set — 24 modules, ~50 containers, several heavy (Plex, Home Assistant, # Letta ~1.8GiB, Baserow ~1.5GiB, the UniFi controller's JVM, mssql ~2GiB). Sized past novox. ace: @@ -42,7 +42,7 @@ machines: memory: 18GiB cpus: 8 disk: 120GiB - # Every runtime. Not mesh-control: the control plane runs on the anchor. + # Every runtime. Not mesh-controller: the control plane runs on the anchor. images: - mesh-runtime-postgres:development - mesh-runtime-redis:development @@ -70,7 +70,7 @@ machines: - mesh-runtime-unifi:development images: - - mesh-control:development + - mesh-controller:development # The per-module runtimes (built by scripts/build-module-runtime.sh). - mesh-runtime-postgres:development - mesh-runtime-redis:development diff --git a/scenarios/whole-mesh-full.yml b/scenarios/whole-mesh-full.yml index 1c9df35..5ea45bd 100644 --- a/scenarios/whole-mesh-full.yml +++ b/scenarios/whole-mesh-full.yml @@ -1,19 +1,19 @@ # The FULL mesh in its REAL production shape: two segments, one access point, one overlay. # # This is the first multi-segment whole-mesh bed. The earlier flat whole-mesh-full sat every node -# on one public segment with a SEPARATE `anchor` carrying the substrate. Production is not flat, and +# on one public segment with a SEPARATE `anchor` carrying the foundation. Production is not flat, and # there is no separate anchor: `novox` IS the anchor. It sits on the routable `hosting` segment, -# runs the substrate (store, broker, control) AND its own service set AND is the overlay hub and the +# runs the foundation (store, broker, control) AND its own service set AND is the overlay hub and the # public ingress. `ace`, `shanks` and `g14` sit on the household `home` segment BEHIND a NAT gateway # — the access point — reachable from the outside only through what they dial out to. # # hosting (public, routable) home (private, behind the access point) # novox 192.0.2.20 ── anchor ace 10.99.1.10 home server, media/IoT set -# substrate + novox set shanks 10.99.1.20 workstation (light) +# foundation + novox set shanks 10.99.1.20 workstation (light) # overlay hub, ingress g14 10.99.1.30 workstation (light) # # The `home` gateway masquerades v4 outbound and forwards inbound (an ordinary household router). -# Home nodes reach novox's public 192.0.2.20 by dialling OUT through it: the substrate broker (5671), +# Home nodes reach novox's public 192.0.2.20 by dialling OUT through it: the foundation broker (5671), # the mesh's own artifact store, and — the thing this bed exists to prove — the WireGuard overlay hub # (51820/udp). The hub keepalive holds the NAT hole open so the tunnel, once formed, stays up. novox # cannot initiate to a home node at all; every home↔novox path is either the overlay or a forwarded @@ -35,20 +35,20 @@ # the gateway's masquerade? The driving test verifies the WireGuard handshake and cross-segment # reachability over the overlay explicitly, and reports form-vs-break as its headline. # -# Substrate-on-novox collides on two host ports the separate-anchor beds never hit: the substrate -# store binds 127.0.0.1:5432 and novox's postgres provider publishes 5432; the substrate broker binds +# Foundation-on-novox collides on two host ports the separate-anchor beds never hit: the foundation +# store binds 127.0.0.1:5432 and novox's postgres provider publishes 5432; the foundation broker binds # 5671 + 127.0.0.1:5672 and novox's lavinmq provider publishes 5672. The driving test REMAPS those two -# provider host publishes off the substrate's ports (consumers reach the providers over the mesh +# provider host publishes off the foundation's ports (consumers reach the providers over the mesh # network on the container port, so the host side is free to move). Reported as a topology finding. # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # MESH_LAB_BOOTSTRAP_BINARY=.../mesh-bootstrap MESH_LAB_CATALOG=.../mesh-catalog/modules # # GENESIS AND JOINING ARE TWO DIFFERENT ACTS, and this bed distinguishes them. novox is brought # into existence by `mesh-bootstrap` — the same program a bare machine runs — and is afterwards a # working mesh of one, with a registry and a control plane that is an ordinary module pinned to an # image that registry serves. ace, shanks and g14 then JOIN it: host binary, token, enrol, run. No -# bootstrap, no substrate, no registry. novox is never enrolled twice, because the installer +# bootstrap, no foundation, no registry. novox is never enrolled twice, because the installer # already did it. # # The images: are the UNION of the novox set (feat/novox-conversions @ 431310f: the slug + roundcube @@ -76,8 +76,8 @@ segments: mapping_ttl: 120s machines: - # The anchor: substrate (store, broker, control) + the whole novox service set + overlay hub + - # public ingress. Bigger than the flat bed's novox, because it now carries the substrate too. + # The anchor: foundation (store, broker, control) + the whole novox service set + overlay hub + + # public ingress. Bigger than the flat bed's novox, because it now carries the foundation too. novox: at: { segment: hosting, address: [192.0.2.20] } egress: true @@ -90,7 +90,7 @@ machines: # because they carry no runtime image of their own — what they run is third-party or is the # node itself. # - # **mesh-control is NOT here, and its absence is the point** (novox/hq ADR 0067). The anchor is + # **mesh-controller is NOT here, and its absence is the point** (novox/hq ADR 0067). The anchor is # brought into existence by the installer, and the installer carries the control plane's image # inside itself — that is the whole reason a machine that can reach no registry can still raise # a mesh. Handing it over from the workstation as well would mean the bed never found out diff --git a/scenarios/whole-mesh-novox.yml b/scenarios/whole-mesh-novox.yml index 45ef3d5..8662ed1 100644 --- a/scenarios/whole-mesh-novox.yml +++ b/scenarios/whole-mesh-novox.yml @@ -1,10 +1,10 @@ # The whole `novox` server's converted service set, installed together on ONE node behind the mesh -# substrate — the whole-catalogue install the rebuild has never actually run. First stage of a +# foundation — the whole-catalogue install the rebuild has never actually run. First stage of a # whole-mesh rehearsal (novox/hq). # -# Topology, proven by test/integration/assigned-two-node-db.test.ts: the substrate (store, broker, +# Topology, proven by test/integration/assigned-two-node-db.test.ts: the foundation (store, broker, # control) rides `anchor` and NOTHING else; ALL of novox's services ride the `novox` node — its own -# postgres provider owns 5432 there, so it cannot co-locate with the substrate store on 5432. Both +# postgres provider owns 5432 there, so it cannot co-locate with the foundation store on 5432. Both # machines sit on one public segment and enrol into the one mesh; an overlay is placed so a # consumer's binding `at` resolves to novox's private address and every consumer reaches the # providers co-located with it. @@ -15,7 +15,7 @@ # apps portainer verdaccio registry route-proxy mailu # node-level firewall fail2ban # -# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock +# MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock # The runtimes are built by scripts/build-module-runtime.sh (one per module that has code) and the # route-proxy image by scripts/build-route-proxy-image.sh; those must be in the local daemon, # because nothing serves them and nothing can. Every third-party image is pulled from the internet @@ -29,7 +29,7 @@ segments: cidr: [192.0.2.0/24] machines: - # The substrate ONLY: store, broker, control. Nothing else lands here. + # The foundation ONLY: store, broker, control. Nothing else lands here. anchor: at: { segment: hosting, address: [192.0.2.10] } egress: true @@ -37,9 +37,9 @@ machines: memory: 4GiB cpus: 4 disk: 20GiB - # The substrate only, so the control plane's image only. Handing this machine the whole set of + # The foundation only, so the control plane's image only. Handing this machine the whole set of # runtimes would fill a 20GiB disk with images nothing on it will ever start. - images: [mesh-control:development] + images: [mesh-controller:development] # The whole novox service set — ~38 containers (five providers with runtimes, six consumers with # runtimes, portainer/verdaccio/registry/route-proxy, the nine-container Mailu stack and its # runtime) plus two node-level modules. mssql alone wants ~2GiB; Mailu, Nextcloud and Keycloak are @@ -55,7 +55,7 @@ machines: # layers and the runtimes. A hundred gigabytes holds the whole set without exhausting the disk # mid-apply. disk: 100GiB - # Every runtime, and the proxy. Not mesh-control: the control plane runs on the anchor. + # Every runtime, and the proxy. Not mesh-controller: the control plane runs on the anchor. images: - mesh-runtime-postgres:development - mesh-runtime-redis:development @@ -73,7 +73,7 @@ machines: - mesh-route-proxy:development images: - - mesh-control:development + - mesh-controller:development # The per-module runtimes (built by scripts/build-module-runtime.sh). registry, route-proxy, # invoicing, firewall and fail2ban carry no mesh-runtime image; route-proxy ships its own. - mesh-runtime-postgres:development diff --git a/scripts/build-route-proxy-image.sh b/scripts/build-route-proxy-image.sh index ef5985e..f660349 100755 --- a/scripts/build-route-proxy-image.sh +++ b/scripts/build-route-proxy-image.sh @@ -1,11 +1,11 @@ #!/usr/bin/env bash # Build the route-proxy module's runtime image: the reference reverse proxy (novox/hq -# 08-connectivity §3), compiled from its canonical Go source in mesh-control into a container the +# 08-connectivity §3), compiled from its canonical Go source in mesh-controller into a container the # lab can stock and serve by digest. # # Unlike the TypeScript modules (built by build-module-runtime.sh into a node tool runtime), the # proxy is a Go program. The module ships only the packaging — a Dockerfile in mesh-catalog whose -# build context is the mesh-control repository root — and this script runs that build into the local +# build context is the mesh-controller repository root — and this script runs that build into the local # docker daemon, which a scenario's registry then stocks and serves by digest. # # build-route-proxy-image.sh @@ -13,7 +13,7 @@ set -euo pipefail HERE="$(cd "$(dirname "$0")/.." && pwd)"; ROOT="$(cd "$HERE/.." && pwd)" -MESH_CONTROL="${MESH_CONTROL:-$ROOT/mesh-control}" +MESH_CONTROL="${MESH_CONTROL:-$ROOT/mesh-controller}" MESH_CATALOG="${MESH_CATALOG:-$ROOT/mesh-catalog}" TAG="${ROUTE_PROXY_TAG:-mesh-route-proxy:development}" DOCKERFILE="$MESH_CATALOG/modules/route-proxy/Dockerfile" @@ -22,7 +22,7 @@ DOCKERFILE="$MESH_CATALOG/modules/route-proxy/Dockerfile" [ -f "$MESH_CONTROL/examples/route-proxy/main.go" ] || { echo "no proxy source at $MESH_CONTROL/examples/route-proxy" >&2; exit 1; } -# Context is the mesh-control repository root: the proxy compiles against that module's go.mod and +# Context is the mesh-controller repository root: the proxy compiles against that module's go.mod and # its examples/route-proxy package. docker build -f "$DOCKERFILE" -t "$TAG" "$MESH_CONTROL" echo "built $TAG (from $MESH_CONTROL/examples/route-proxy)" diff --git a/src/declaration/types.ts b/src/declaration/types.ts index ae71d9f..96c5a7b 100644 --- a/src/declaration/types.ts +++ b/src/declaration/types.ts @@ -103,7 +103,7 @@ export interface Machine { * and a handful of containers. * * Declared, because it is a fact about the machine the scenario describes — the node that runs - * the whole substrate is bigger than the laptop that joins it, and a test that starves its + * the whole foundation is bigger than the laptop that joins it, and a test that starves its * anchor at the default answers questions about memory pressure, not about the mesh. The forge * test failed three times as "status hangs" before anyone counted the containers in 1GiB * (novox/hq 04-ISSUES/024 is the same lesson about a different resource). @@ -158,7 +158,7 @@ export interface Scenario { * **The mesh's own images** — the ones that exist in no registry and are put onto a machine by * whoever built them. * - * mesh-control, mesh-builder, mesh-route-proxy, the per-module runtimes and the provisioners are + * mesh-controller, mesh-builder, mesh-route-proxy, the per-module runtimes and the provisioners are * built from source and published nowhere. A machine gets them the way an operator's machine * does: they are built on the workstation, loaded onto the machine, and named by the digest of * their own image configuration. Written as tags, because a tag is what `docker save` can diff --git a/src/lifecycle/egress.ts b/src/lifecycle/egress.ts index ebda7fa..256ab41 100644 --- a/src/lifecycle/egress.ts +++ b/src/lifecycle/egress.ts @@ -7,7 +7,7 @@ * from the internet, and that is now the thing worth proving before a raise says it is finished. * * The failure it exists to stop is the same one, in the same shape: `raise` returns, the caller - * applies a substrate, the first pull fails, no node enrols, and the instance is left a bare + * applies a foundation, the first pull fails, no node enrols, and the instance is left a bare * shell — with the cause several steps back and looking like a mesh fault rather than a lab one. * * Two things are checked, in this order, because they fail differently and the difference is the diff --git a/src/lifecycle/place.ts b/src/lifecycle/place.ts index 90076de..1002a9a 100644 --- a/src/lifecycle/place.ts +++ b/src/lifecycle/place.ts @@ -5,7 +5,7 @@ * the thing it exists to test did not exist (novox/hq 03-DESIGN/00-as-is/11-the-lab.md). Tier * 0 now does, so this is the seam where the lab acquires a consumer. * - * Only `host` is placeable. Everything else in the placement vocabulary — the substrate, a + * Only `host` is placeable. Everything else in the placement vocabulary — the foundation, a * control plane, a forge — is still refused by name rather than ignored, because a scenario * that declares something and raises without it is the fault this lab was built to catch * (novox/hq 04-ISSUES/003). @@ -515,7 +515,7 @@ export async function loadHeldImages( throw new Error( `${requested} is not on this workstation, so there is nothing to hand the machines.\n` + ` It is one of the mesh's own images and exists in no registry — nothing can pull it.\n` + - ` Build it first (mesh-control's \`make image …\`, or scripts/build-module-runtime.sh).`, + ` Build it first (mesh-controller's \`make image …\`, or scripts/build-module-runtime.sh).`, ); } } diff --git a/src/lifecycle/raise.ts b/src/lifecycle/raise.ts index 96e402e..908237f 100644 --- a/src/lifecycle/raise.ts +++ b/src/lifecycle/raise.ts @@ -338,7 +338,7 @@ export async function raise( // **Only now is "this machine can reach the outside" a true statement.** The route, the // gateway and the machine's own filtering are all in place, so this is the path a pull takes. // A raise that returned without checking would hand the next step a fact it depends on and - // has no way to test — which is how a substrate apply used to die on its first pull. + // has no way to test — which is how a foundation apply used to die on its first pull. enter("confirming egress reaches the internet"); await confirmEgress(scenario, byMachine, log); diff --git a/src/lifecycle/supported.ts b/src/lifecycle/supported.ts index 55b07d8..545d922 100644 --- a/src/lifecycle/supported.ts +++ b/src/lifecycle/supported.ts @@ -36,7 +36,7 @@ export function assertSupported(scenario: Scenario): void { // `host`, `runtime` and `image:` work. Everything else in the vocabulary is named // individually rather than refused as a whole, so a scenario that places a host and a - // substrate is told exactly which half the lab cannot do. + // foundation is told exactly which half the lab cannot do. const unplaceable = new Set(); for (const { artifacts } of planPlacements(scenario)) { for (const artifact of artifacts) { diff --git a/src/pinning.ts b/src/pinning.ts index 3197cfd..2f63f1a 100644 --- a/src/pinning.ts +++ b/src/pinning.ts @@ -3,7 +3,7 @@ * * **A digest is not knowable until something is built** (novox/hq 04-ISSUES/025). A manifest in a * repository can pin a third-party image, because somebody can ask a registry what a tag points - * at. It cannot pin an image the mesh builds itself: mesh-control, mesh-builder, mesh-route-proxy, + * at. It cannot pin an image the mesh builds itself: mesh-controller, mesh-builder, mesh-route-proxy, * the per-module runtimes and the provisioners exist in no registry, so there is no manifest * digest to write down. The catalogue ships sixty-four zeros for them, which parses, resolves, * composes — and stops on the machine. @@ -54,7 +54,7 @@ export function repositoryOf(reference: string): string { * * **Derived from the shape the build produces, not from a list of names.** `make image * builder-image provisioner-image objectstore-image redis-provisioner-image proxy-image` in - * mesh-control and `scripts/build-module-runtime.sh` here both tag their output `mesh-` + * mesh-controller and `scripts/build-module-runtime.sh` here both tag their output `mesh-` * with no registry host and no upstream organisation — that is what "built here, published * nowhere" looks like, and a hardcoded list would go stale the first time a module is added. * diff --git a/src/rebuild.ts b/src/rebuild.ts index 13e5baf..ea160f2 100644 --- a/src/rebuild.ts +++ b/src/rebuild.ts @@ -45,7 +45,7 @@ export function planned(env: NodeJS.ProcessEnv = process.env): Build[] { env: { CGO_ENABLED: "0" }, }); } - const control = where["mesh-control"]; + const control = where["mesh-controller"]; if (control) { // **Every image the lab runs, not only the control plane's.** // @@ -84,7 +84,7 @@ export function planned(env: NodeJS.ProcessEnv = process.env): Build[] { // // It is built here at all because the bed now bootstraps THROUGH it (novox/hq ADR 0067): the // anchor is brought into existence by running the same program a bare machine runs, rather than - // by the bed applying a substrate bundle by hand and calling that an install. An installer that + // by the bed applying a foundation bundle by hand and calling that an install. An installer that // was stale would be a bed proving something about last week's procedure. const installer = env["MESH_LAB_BOOTSTRAP_BINARY"]; if (installer && where["mesh-host"]) { @@ -104,7 +104,7 @@ export function planned(env: NodeJS.ProcessEnv = process.env): Build[] { * thing it was going to run and now carries the thing that makes it, so a raised mesh holds a * control plane it built from a repository and a commit rather than one it was handed. * - * `mesh-builder:development` is what mesh-control's `make builder-image` tags — one tag, said in + * `mesh-builder:development` is what mesh-controller's `make builder-image` tags — one tag, said in * one place. Overridable because a release installer carries a release image, and nothing about * that is the lab's business. */ diff --git a/src/repos.ts b/src/repos.ts index dadabc9..c0330e0 100644 --- a/src/repos.ts +++ b/src/repos.ts @@ -23,6 +23,6 @@ export function repositories(env: NodeJS.ProcessEnv = process.env): Repositories const host = env["MESH_LAB_HOST_BINARY"]; if (host) found["mesh-host"] = dirname(host); const modules = env["MESH_LAB_MODULES"]; - if (modules) found["mesh-control"] = dirname(dirname(modules)); + if (modules) found["mesh-controller"] = dirname(dirname(modules)); return found; } diff --git a/test/integration/anthropic-bed.test.ts b/test/integration/anthropic-bed.test.ts index 85a4c9a..c9d3409 100644 --- a/test/integration/anthropic-bed.test.ts +++ b/test/integration/anthropic-bed.test.ts @@ -10,7 +10,7 @@ * **What changed from the earlier cut, and why this is simpler.** The refresh token no longer rides a * bespoke at-rest envelope the module opens with a node private key the mesh must somehow place — a * module is never given a node's private key, so that path could not exist. It rides the ORDINARY - * sealed-delivery path instead: mesh-control (via the manager module at adoption) seals it to the + * sealed-delivery path instead: mesh-controller (via the manager module at adoption) seals it to the * manager node's PUBLIC key, and the HOST unseals it with that node's real private key and mounts the * cleartext at the manager module's bound secret path — exactly as a consumer's db password arrives. * So there is no fake node key pair mounted here any more; the host's own real sealing key does the @@ -32,11 +32,11 @@ * ~/.claude/.credentials.json, access-token-only. * * STUBBED, and flagged in the report: (a) the vendor OAuth endpoint (a node stub); (b) the submit - * transport (the test invokes `mesh-control licence submit-refresh` on the manager's output, standing + * transport (the test invokes `mesh-controller licence submit-refresh` on the manager's output, standing * in for the authenticated cross-node call a manager node would make). The node-private-key stub of * the earlier cut is GONE — the host uses its own real key. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * Build both runtime images into the local daemon first: * scripts/build-module-runtime.sh anthropic-manager /tmp/anthropic-manager.tar * scripts/build-module-runtime.sh anthropic-consumer /tmp/anthropic-consumer.tar @@ -49,7 +49,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -61,7 +61,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "anthropic-bed"; @@ -96,22 +96,22 @@ async function must(command: string, timeoutMs?: number): Promise { } async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } // The manager's adopt/refresh runtime writes its outputs as root, mode 0600 (secret files). To hand -// one to `mesh-control` — whose process runs as a non-root user — the test relaxes the mode on the +// one to `mesh-controller` — whose process runs as a non-root user — the test relaxes the mode on the // anchor host (where `must` is root) and then copies it in: `docker cp` preserves the source mode, so -// the file lands 0644 and mesh-control (a distroless image with no `chmod` of its own) can read it. +// the file lands 0644 and mesh-controller (a distroless image with no `chmod` of its own) can read it. // What is staged this way is a sealed box or the access token, never a cleartext refresh token, so a // world-readable copy discloses nothing the control plane does not already hold. In production the // operator who ran adopt owns the file and this does not arise. async function stageIntoControl(hostPath: string, dest: string): Promise { - await must(`chmod 0644 ${hostPath} && docker cp ${hostPath} mesh-control:${dest}`); + await must(`chmod 0644 ${hostPath} && docker cp ${hostPath} mesh-controller:${dest}`); } async function meshTry(command: string): Promise<{ out: string; ok: boolean }> { - return on(`docker exec mesh-control /mesh-control ${command}`); + return on(`docker exec mesh-controller /mesh-controller ${command}`); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -120,7 +120,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -173,11 +173,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } await mesh(`node add ${MACHINE}`); @@ -239,7 +239,7 @@ test("model access refreshes on the manager node and delivers only the access to }, ], }); - await must(`printf %s ${quote(managerManifest)} > /tmp/anthropic-manager.json && docker cp /tmp/anthropic-manager.json mesh-control:/anthropic-manager.json`); + await must(`printf %s ${quote(managerManifest)} > /tmp/anthropic-manager.json && docker cp /tmp/anthropic-manager.json mesh-controller:/anthropic-manager.json`); await mesh(`module add /anthropic-manager.json`); await mesh(`module issue anthropic-manager --node ${MACHINE}`); await mesh(`assign ${MACHINE} anthropic-manager`); @@ -355,7 +355,7 @@ test("model access refreshes on the manager node and delivers only the access to }, ], }); - await must(`printf %s ${quote(consumerManifest)} > /tmp/anthropic-consumer.json && docker cp /tmp/anthropic-consumer.json mesh-control:/anthropic-consumer.json`); + await must(`printf %s ${quote(consumerManifest)} > /tmp/anthropic-consumer.json && docker cp /tmp/anthropic-consumer.json mesh-controller:/anthropic-consumer.json`); await mesh(`module add /anthropic-consumer.json`); await mesh(`module issue anthropic-consumer --node ${MACHINE}`); await mesh(`assign ${MACHINE} anthropic-consumer`); diff --git a/test/integration/assigned-audit.test.ts b/test/integration/assigned-audit.test.ts index 9c4a412..22603fb 100644 --- a/test/integration/assigned-audit.test.ts +++ b/test/integration/assigned-audit.test.ts @@ -7,10 +7,10 @@ * container that connects over amqps with that account — never the broker's own. The trail filling * is the proof the delivered, scoped credential authenticated and the subscription bound. * - * It needs the host binary, the substrate bundle, and the runtime image the scenario loads: + * It needs the host binary, the foundation bundle, and the runtime image the scenario loads: * * MESH_LAB_HOST_BINARY=.../mesh-host - * MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-runtime-image.sh builds mesh-runtime-audit:development into the local daemon, * which scenarios/audit-node.yml stocks — so no MESH_LAB_RUNTIME here; the host pulls it. */ @@ -22,7 +22,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -34,7 +34,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "audit-node"; @@ -65,7 +65,7 @@ async function must(command: string, timeoutMs?: number): Promise { /** The control plane, a container on the node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The reference a manifest should carry, once this scenario has been raised. */ @@ -74,9 +74,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -89,7 +89,7 @@ async function settled(withinMs = 480_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -121,12 +121,12 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - // Raise the substrate — store, broker, control — from the bundle. - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + // Raise the foundation — store, broker, control — from the bundle. + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } // The node joins its own mesh, so it is a node the mesh can assign to, and start the host so it @@ -165,7 +165,7 @@ test("the mesh assigns the audit logger, and it consumes over the account the me }, ], }); - await must(`printf %s ${quote(manifest)} > /tmp/audit.json && docker cp /tmp/audit.json mesh-control:/audit.json`); + await must(`printf %s ${quote(manifest)} > /tmp/audit.json && docker cp /tmp/audit.json mesh-controller:/audit.json`); await mesh("module add /audit.json"); // The mesh issues its scoped account and seals it to this machine, then assigns and pushes it. @@ -209,7 +209,7 @@ test("the mesh assigns the audit logger, and it consumes over the account the me // And the account the mesh made for it is a real one on the broker — the trail above already // proved it authenticated and read its queue. That it reaches no further than its own queue is - // the scope CreateModuleAccount applies, checked as patterns in mesh-control's own tests. + // the scope CreateModuleAccount applies, checked as patterns in mesh-controller's own tests. const users = await must(`docker exec mesh-broker lavinmqctl list_users 2>&1`); assert.match(users, /anchor-audit-logger/, `the scoped account is not on the broker:\n${users}`); }); diff --git a/test/integration/assigned-catalogue-apps.test.ts b/test/integration/assigned-catalogue-apps.test.ts index aa583c6..d163449 100644 --- a/test/integration/assigned-catalogue-apps.test.ts +++ b/test/integration/assigned-catalogue-apps.test.ts @@ -17,7 +17,7 @@ * * All are assigned to the one anchor, pushed ONCE, and the node converges ONCE with every one up. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh {mongodb,unifi,postgres} build the runtime images into the local * daemon; scenarios/catalogue-apps.yml stocks them. mongo:7, lscr.io/linuxserver/unifi-controller * and synesthesiam/marytts must be in the local daemon to be stocked. @@ -30,7 +30,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -42,7 +42,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "catalogue-apps"; @@ -73,7 +73,7 @@ async function must(command: string, timeoutMs?: number): Promise { /** The control plane, a container on the node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The reference a manifest should carry, once this scenario has been raised. */ @@ -82,9 +82,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -97,7 +97,7 @@ async function settled(withinMs = 600_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -129,12 +129,12 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - // Raise the substrate — store, broker, control — from the bundle. - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + // Raise the foundation — store, broker, control — from the bundle. + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } // The node joins its own mesh, so it is a node the mesh can assign to, and start the host so it @@ -310,7 +310,7 @@ test("the mesh assigns mongodb, unifi, marrytts and postgres to one node in one // --- add, issue (those that serve/emit), assign, then ONE push ---------------------------------- async function add(name: string, manifest: string): Promise { - await must(`printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-control:/${name}.json`); + await must(`printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); } diff --git a/test/integration/assigned-catalogue-media.test.ts b/test/integration/assigned-catalogue-media.test.ts index c75fad6..3830201 100644 --- a/test/integration/assigned-catalogue-media.test.ts +++ b/test/integration/assigned-catalogue-media.test.ts @@ -23,7 +23,7 @@ * * All is assigned to the one anchor, pushed ONCE, and the node converges ONCE with both modules up. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh {sonarr,radarr} build the runtime images into the local daemon; * scenarios/catalogue-media.yml stocks them. lscr.io/linuxserver/{sonarr,radarr} must be in the * local daemon; the service images are pulled from the internet. Each *arr runtime is given a lab @@ -38,7 +38,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -50,7 +50,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "catalogue-media"; @@ -81,7 +81,7 @@ async function must(command: string, timeoutMs?: number): Promise { /** The control plane, a container on the node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The reference a manifest should carry, once this scenario has been raised. */ @@ -90,9 +90,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -105,7 +105,7 @@ async function settled(withinMs = 600_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -137,12 +137,12 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - // Raise the substrate — store, broker, control — from the bundle. - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + // Raise the foundation — store, broker, control — from the bundle. + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } // The node joins its own mesh, so it is a node the mesh can assign to, and start the host so it @@ -248,7 +248,7 @@ test("sonarr and radarr, both accessing one operator-owned directory, co-resolve // --- add, issue (both emit events → each gets a scoped broker account), assign ------------------ async function add(name: string, manifest: string): Promise { - await must(`printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-control:/${name}.json`); + await must(`printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); } @@ -274,7 +274,7 @@ test("sonarr and radarr, both accessing one operator-owned directory, co-resolve // read as two modules owning one path. Now each ACCESSES it, so the pair co-resolves and both are // sent in a single declaration. A refusal here (nonzero, or "could not be resolved") is the // regression this bed exists to catch. - const pushed = await on(`docker exec mesh-control /mesh-control push ${MACHINE}`); + const pushed = await on(`docker exec mesh-controller /mesh-controller push ${MACHINE}`); assert.ok(pushed.ok, `the co-resident push was REFUSED — the shared-access collision ADR 0051 removed is back:\n${pushed.out}`); assert.doesNotMatch(pushed.out, /could not be resolved|both declare the path|shared data is the operator/, diff --git a/test/integration/assigned-catalogue-mqtt.test.ts b/test/integration/assigned-catalogue-mqtt.test.ts index 5bd307d..762738c 100644 --- a/test/integration/assigned-catalogue-mqtt.test.ts +++ b/test/integration/assigned-catalogue-mqtt.test.ts @@ -22,7 +22,7 @@ * the seed), and mosquitto's provisioner — running in the assigned runtime — creates a scoped client * for a contribution the mesh delivered, which then authenticates with the password the mesh minted. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh mosquitto builds mesh-runtime-mosquitto:development (carrying * mosquitto_ctrl and the compiled bootstrap entrypoint) into the local daemon, which * scenarios/catalogue-mqtt.yml stocks. eclipse-mosquitto:2 must be in the local daemon to be @@ -36,7 +36,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -48,7 +48,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "catalogue-mqtt"; @@ -78,7 +78,7 @@ async function must(command: string, timeoutMs?: number): Promise { /** The control plane, a container on the node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The reference a manifest should carry, once this scenario has been raised. */ @@ -87,9 +87,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -102,7 +102,7 @@ async function settled(withinMs = 600_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -134,12 +134,12 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - // Raise the substrate — store, broker, control — from the bundle. - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + // Raise the foundation — store, broker, control — from the bundle. + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } // The node joins its own mesh and starts the host so it applies what it is pushed. @@ -187,7 +187,7 @@ test("the mesh assigns mosquitto: a run-once step seeds dynsec before the broker serves: { "mqtt-topic": {} }, emits: ["module.mosquitto.topic.provisioned", "module.mosquitto.topic.deprovisioned"], // The events entrypoint subscribes to its own lifecycle events (an audit log), so it consumes - // them too — declared, or the substrate never makes the queue the runtime binds (ADR 0046). + // them too — declared, or the foundation never makes the queue the runtime binds (ADR 0046). consumes: ["module.mosquitto.topic.provisioned", "module.mosquitto.topic.deprovisioned"], receives: { "mqtt-topic": "/var/lib/mosquitto-module/grants/mesh.json" }, grants: { "mqtt-topic": "/var/lib/mosquitto-module/grants" }, @@ -254,7 +254,7 @@ test("the mesh assigns mosquitto: a run-once step seeds dynsec before the broker ], }); - await must(`printf %s ${quote(manifest)} > /tmp/mosquitto.json && docker cp /tmp/mosquitto.json mesh-control:/mosquitto.json`); + await must(`printf %s ${quote(manifest)} > /tmp/mosquitto.json && docker cp /tmp/mosquitto.json mesh-controller:/mosquitto.json`); await mesh("module add /mosquitto.json"); const issued = await mesh(`module issue mosquitto --node ${MACHINE}`); assert.match(issued, /scoped to what it emits and consumes/, issued); @@ -324,7 +324,7 @@ test("the mesh assigns mosquitto: a run-once step seeds dynsec before the broker // each consumer it reads the login the mesh derived and the mesh-minted password the host unsealed, // and creates exactly that dynsec client, scoped to its own topic subtree (ADR 0048). A // hand-written contributions file + secret stand in for the control plane's write; their SHAPE is - // what mesh-control produces. The proof is authentication as the consumer with the mesh's password + // what mesh-controller produces. The proof is authentication as the consumer with the mesh's password // — a provisioner that invented its own would refuse the connection. const consumerPw = "mesh-minted-mqtt-7b2e1a"; await must(`printf %s ${quote(consumerPw)} > /var/lib/mosquitto-module/grants/app.secret`); diff --git a/test/integration/assigned-catalogue-small.test.ts b/test/integration/assigned-catalogue-small.test.ts index e32c950..819b711 100644 --- a/test/integration/assigned-catalogue-small.test.ts +++ b/test/integration/assigned-catalogue-small.test.ts @@ -18,7 +18,7 @@ * path is fulfilled by creating the consumer's login with the mesh-minted password — it seals nothing * and needs no seal key (novox/hq ADR 0048, issue 032-provider-runtime-has-no-seal-key). * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh {postgres,redis,minio,plex} build the four runtime images into the * local daemon; scenarios/catalogue-small.yml stocks them. postgres:17-alpine, redis:7-alpine and * minio/minio:latest is pulled from the internet by the node itself. @@ -31,7 +31,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -43,7 +43,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "catalogue-small"; @@ -74,7 +74,7 @@ async function must(command: string, timeoutMs?: number): Promise { /** The control plane, a container on the node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The reference a manifest should carry, once this scenario has been raised. */ @@ -83,9 +83,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -104,7 +104,7 @@ async function settled(withinMs = 600_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -136,12 +136,12 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - // Raise the substrate — store, broker, control — from the bundle. - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + // Raise the foundation — store, broker, control — from the bundle. + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } // The node joins its own mesh, so it is a node the mesh can assign to, and start the host so it @@ -340,7 +340,7 @@ test("the mesh assigns postgres, redis, minio and plex to one node in one push, // --- add, issue (the four that serve/emit), assign, then ONE push ------------------------------- async function add(name: string, manifest: string): Promise { - await must(`printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-control:/${name}.json`); + await must(`printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); } @@ -455,7 +455,7 @@ test("the mesh assigns postgres, redis, minio and plex to one node in one push, // issue 032): for each consumer it reads the login the mesh derived and the mesh-minted password the // host unsealed, and creates the ACL user under exactly that login and password — sealing nothing // and writing no credential file. A hand-written contributions file and secret stand in for the - // control plane's write; their SHAPE is what mesh-control produces. The proof is authentication as + // control plane's write; their SHAPE is what mesh-controller produces. The proof is authentication as // the consumer with the mesh's password (PONG) — a provisioner that invented its own would answer // WRONGPASS. const redisPassword = "mesh-minted-9f3c2a"; diff --git a/test/integration/assigned-grafana.test.ts b/test/integration/assigned-grafana.test.ts index bea54a3..1d92897 100644 --- a/test/integration/assigned-grafana.test.ts +++ b/test/integration/assigned-grafana.test.ts @@ -10,7 +10,7 @@ * Grafana — that the serve queue is bound is the proof the settings reached the runtime and its * tools loaded from them. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh grafana builds mesh-runtime-grafana:development into the local * daemon, which scenarios/grafana-node.yml stocks — so no MESH_LAB_RUNTIME here; the host pulls it. */ @@ -22,7 +22,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -34,7 +34,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "grafana-node"; @@ -63,7 +63,7 @@ async function must(command: string, timeoutMs?: number): Promise { } async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -72,7 +72,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -85,7 +85,7 @@ async function settled(withinMs = 480_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -117,11 +117,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } await mesh(`node add ${MACHINE}`); @@ -163,13 +163,13 @@ test("the mesh assigns grafana's runtime, configured by settings, and it serves }, ], }); - await must(`printf %s ${quote(manifest)} > /tmp/grafana.json && docker cp /tmp/grafana.json mesh-control:/grafana.json`); + await must(`printf %s ${quote(manifest)} > /tmp/grafana.json && docker cp /tmp/grafana.json mesh-controller:/grafana.json`); await mesh("module add /grafana.json"); // The operator states grafana's URL and API token as settings for this node — the config the // runtime will read. Nothing about them is in the manifest. const settings = JSON.stringify({ url: "http://127.0.0.1:3000", token: "lab-grafana-token" }); - await must(`printf %s ${quote(settings)} > /tmp/grafana-settings.json && docker cp /tmp/grafana-settings.json mesh-control:/grafana-settings.json`); + await must(`printf %s ${quote(settings)} > /tmp/grafana-settings.json && docker cp /tmp/grafana-settings.json mesh-controller:/grafana-settings.json`); await mesh(`settings set grafana /grafana-settings.json --node ${MACHINE}`); const issued = await mesh(`module issue grafana --node ${MACHINE}`); diff --git a/test/integration/assigned-model-usage.test.ts b/test/integration/assigned-model-usage.test.ts index abdd1cf..c809e63 100644 --- a/test/integration/assigned-model-usage.test.ts +++ b/test/integration/assigned-model-usage.test.ts @@ -1,7 +1,7 @@ /** * The usage context store, proved end to end (novox/hq ADR 0054). * - * model-usage is a MODULE, not a control-plane feature, because mesh-control is a CLI and cannot + * model-usage is a MODULE, not a control-plane feature, because mesh-controller is a CLI and cannot * consume events: the store that keeps the latest usage reading has to be something that subscribes * to `module.*.usage.*` and upserts. This bed raises a real mesh, provisions model-usage its own * postgres store, emits usage events into the mesh, and reads the store back to prove the three @@ -14,15 +14,15 @@ * 3. IN THE CLEAR — the value and its raw payload are ordinary columns an ordinary select returns; * nothing about usage is sealed. * - * The topology mirrors two-node-db: the app-postgres provider and the mesh's own substrate store both - * want host port 5432, so the substrate (store, broker, control) owns `anchor` and NOTHING else, and + * The topology mirrors two-node-db: the app-postgres provider and the mesh's own foundation store both + * want host port 5432, so the foundation (store, broker, control) owns `anchor` and NOTHING else, and * `laptop` runs the postgres PROVIDER and the model-usage CONSUMER co-located. Only enrolment crosses * to anchor, over the underlay both machines share. * - * It needs a host binary and the substrate bundle: + * It needs a host binary and the foundation bundle: * * MESH_LAB_HOST_BINARY=.../mesh-host - * MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * * HELPER — stock the two runtimes into the local daemon before the run (some may already be there): * scripts/build-module-runtime.sh postgres /tmp/postgres.tar @@ -38,7 +38,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -50,12 +50,12 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "model-usage-bed"; /** The node that carries the postgres provider and the model-usage consumer. anchor carries only the - * substrate. */ + * foundation. */ const NODE = "laptop"; let instanceId = ""; @@ -83,7 +83,7 @@ async function must(machine: string, command: string, timeoutMs?: number): Promi /** The control plane, a container on the first node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must("anchor", `docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must("anchor", `docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The reference a manifest should carry, once this scenario has been raised. */ @@ -92,9 +92,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -120,7 +120,7 @@ async function settled(node: string, withinMs = 1_200_000): Promise { } | undefined; let said = ""; try { - const asked = await on("anchor", `docker exec mesh-control /mesh-control status --json`); + const asked = await on("anchor", `docker exec mesh-controller /mesh-controller status --json`); said = asked.out; if (asked.ok) state = JSON.parse(said); } catch (err) { @@ -160,11 +160,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must("anchor", `cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must("anchor", `${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must("anchor", `cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must("anchor", `${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must("anchor", `docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } for (const [machine, node] of [["anchor", "anchor"], ["laptop", "laptop"]] as const) { @@ -269,7 +269,7 @@ test("usage events are upserted into model-usage's store — latest-per-key, bot }); async function addIssueAssign(name: string, manifest: string): Promise { - await must("anchor", `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-control:/${name}.json`); + await must("anchor", `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); await mesh(`module issue ${name} --node ${NODE}`); await mesh(`assign ${NODE} ${name}`); @@ -289,7 +289,7 @@ test("usage events are upserted into model-usage's store — latest-per-key, bot await mesh(`push ${NODE}`); await settled(NODE); - // The provider owns 5432 on laptop; the substrate store owns it on anchor. + // The provider owns 5432 on laptop; the foundation store owns it on anchor. const onLaptop = await must(NODE, `docker ps --format '{{.Names}}'`); for (const name of ["postgres", "mesh-postgres", "mesh-model-usage"]) { assert.match(onLaptop, new RegExp(`(^|\\n)${name}(\\n|$)`), @@ -331,7 +331,7 @@ test("usage events are upserted into model-usage's store — latest-per-key, bot assert.match(tableReady, /^t$/m, `the usage table was never created (the consumer did not migrate):\n${tableReady}`); // Inject a usage event into the mesh. model-usage is a PURE CONSUMER, so its own broker account has - // no publish right (mesh-control grants write to mesh.events only to a module that declares `emits`). + // no publish right (mesh-controller grants write to mesh.events only to a module that declares `emits`). // So publish from the postgres provider's container — a publisher already on the node — overriding // the source header to the real producer the key names, exactly as events.test.ts injects with // `-e MESH_MODULE=...`. Write permission is per-exchange, not per-key, so postgres may carry any diff --git a/test/integration/assigned-plex.test.ts b/test/integration/assigned-plex.test.ts index 33c4b5c..14e6877 100644 --- a/test/integration/assigned-plex.test.ts +++ b/test/integration/assigned-plex.test.ts @@ -10,10 +10,10 @@ * proof the invocation routed to the assigned runtime, ran plex's real code, and replied, all under * the scoped account and never the broker's own. * - * It needs the host binary, the substrate bundle, and the runtime image the scenario loads: + * It needs the host binary, the foundation bundle, and the runtime image the scenario loads: * * MESH_LAB_HOST_BINARY=.../mesh-host - * MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh plex builds mesh-runtime-plex:development into the local daemon, * which scenarios/plex-node.yml stocks — so no MESH_LAB_RUNTIME here; the host pulls it. */ @@ -25,7 +25,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -37,7 +37,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "plex-node"; @@ -68,7 +68,7 @@ async function must(command: string, timeoutMs?: number): Promise { /** The control plane, a container on the node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The reference a manifest should carry, once this scenario has been raised. */ @@ -77,9 +77,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -92,7 +92,7 @@ async function settled(withinMs = 480_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -124,12 +124,12 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - // Raise the substrate — store, broker, control — from the bundle. - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + // Raise the foundation — store, broker, control — from the bundle. + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } // The node joins its own mesh, so it is a node the mesh can assign to, and start the host so it @@ -176,7 +176,7 @@ test("the mesh assigns plex's runtime, and it serves plex's tools over the accou }, ], }); - await must(`printf %s ${quote(manifest)} > /tmp/plex.json && docker cp /tmp/plex.json mesh-control:/plex.json`); + await must(`printf %s ${quote(manifest)} > /tmp/plex.json && docker cp /tmp/plex.json mesh-controller:/plex.json`); await mesh("module add /plex.json"); // The mesh issues plex's scoped account and seals it to this machine, then assigns and pushes it. @@ -210,7 +210,7 @@ test("the mesh assigns plex's runtime, and it serves plex's tools over the accou `plex's runtime never bound its serve queue:\n${(await on(`docker logs mesh-plex 2>&1 | tail -20`)).out}\n---\n${served}`); // A caller invokes plex.plex_reachable over the mesh, from the bootstrap account (a caller, like - // mesh-control's command API — plex's own account serves, it does not call). The reply is the + // mesh-controller's command API — plex's own account serves, it does not call). The reply is the // tool's own answer: it ran in the assigned runtime and reported the Plex server is unreachable // (there is none in the lab). A reply at all — not a timeout — is the proof the invocation routed // to the assigned runtime and ran plex's real code under its scoped account. diff --git a/test/integration/assigned-redis.test.ts b/test/integration/assigned-redis.test.ts index 647d889..2e8cea0 100644 --- a/test/integration/assigned-redis.test.ts +++ b/test/integration/assigned-redis.test.ts @@ -12,9 +12,9 @@ * has no way yet to deliver one to a provider's runtime (04-ISSUES). The manifest here sets a * lab-local key so the mechanism can be proven; the delivery is a separate, open design question. * - * It needs the host binary, the substrate bundle, and the runtime image the scenario loads: + * It needs the host binary, the foundation bundle, and the runtime image the scenario loads: * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh redis builds mesh-runtime-redis:development into the local * daemon, which scenarios/redis-node.yml stocks — so no MESH_LAB_RUNTIME here; the host pulls it. */ @@ -26,7 +26,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -38,7 +38,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "redis-node"; @@ -67,7 +67,7 @@ async function must(command: string, timeoutMs?: number): Promise { } async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -76,7 +76,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -89,7 +89,7 @@ async function settled(withinMs = 480_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -121,11 +121,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } await mesh(`node add ${MACHINE}`); @@ -151,7 +151,7 @@ test("the mesh assigns redis, and its runtime serves tools and provisions grants version: "1", emits: ["module.redis.cache.provisioned", "module.redis.cache.deprovisioned"], // redis's events entrypoint subscribes to its own lifecycle events (an audit-trail log), so it - // consumes them too — declared, or the substrate never makes the queue the runtime binds and it + // consumes them too — declared, or the foundation never makes the queue the runtime binds and it // crashes on start with a 404 (novox/hq ADR 0046: a consume is declared). consumes: ["module.redis.cache.provisioned", "module.redis.cache.deprovisioned"], "own-secrets": { default: "/var/lib/redis-module/default.secret", broker: "/var/lib/mesh/redis/broker" }, @@ -190,7 +190,7 @@ test("the mesh assigns redis, and its runtime serves tools and provisions grants }, ], }); - await must(`printf %s ${quote(manifest)} > /tmp/redis.json && docker cp /tmp/redis.json mesh-control:/redis.json`); + await must(`printf %s ${quote(manifest)} > /tmp/redis.json && docker cp /tmp/redis.json mesh-controller:/redis.json`); await mesh("module add /redis.json"); const issued = await mesh(`module issue redis --node ${MACHINE}`); diff --git a/test/integration/assigned-schedule-tick.test.ts b/test/integration/assigned-schedule-tick.test.ts index 7cf53f8..e7487d1 100644 --- a/test/integration/assigned-schedule-tick.test.ts +++ b/test/integration/assigned-schedule-tick.test.ts @@ -25,8 +25,8 @@ * registry by digest; the host pulls and runs it on the cadence. * * MESH_LAB_HOST_BINARY=.../mesh-host (feat/apply-schedule — the scheduler that fires the step) - * MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock - * MESH_LAB_MODULES=.../mesh-control/examples/modules (feat/schedule-container — the parser that + * MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock + * MESH_LAB_MODULES=.../mesh-controller/examples/modules (feat/schedule-container — the parser that * carries `schedule` through). scenarios/schedule-tick.yml stocks alpine:latest (which must be in * the local daemon) and serves it by digest; there is no runtime image — schedtest is a bare tick. */ @@ -38,7 +38,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -50,7 +50,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "schedule-tick"; @@ -81,7 +81,7 @@ async function must(command: string, timeoutMs?: number): Promise { /** The control plane, a container on the node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The reference a manifest should carry, once this scenario has been raised. */ @@ -90,9 +90,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -119,7 +119,7 @@ async function settled(withinMs = 600_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -151,12 +151,12 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - // Raise the substrate — store, broker, control — from the bundle. - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + // Raise the foundation — store, broker, control — from the bundle. + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } // The node joins its own mesh, so it is a node the mesh can assign to, and start the host so it @@ -197,7 +197,7 @@ test("the mesh assigns schedtest: installing the schedule does not run it, and t ], }); - await must(`printf %s ${quote(manifest)} > /tmp/schedtest.json && docker cp /tmp/schedtest.json mesh-control:/schedtest.json`); + await must(`printf %s ${quote(manifest)} > /tmp/schedtest.json && docker cp /tmp/schedtest.json mesh-controller:/schedtest.json`); await mesh("module add /schedtest.json"); // No `module issue`: schedtest serves/consumes nothing and carries no runtime, so it needs no // broker account. `assign` resolves its plan (no own-secret to fill) and ONE push converges it. diff --git a/test/integration/assigned-sonarr.test.ts b/test/integration/assigned-sonarr.test.ts index 656f903..dc63f19 100644 --- a/test/integration/assigned-sonarr.test.ts +++ b/test/integration/assigned-sonarr.test.ts @@ -8,7 +8,7 @@ * Sonarr here), registers its tools, and serves them under a scoped account. There is no live Sonarr * to reach — that the serve queue is bound is the proof the key was detected and the tools loaded. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh sonarr builds mesh-runtime-sonarr:development into the local * daemon, which scenarios/sonarr-node.yml stocks — so no MESH_LAB_RUNTIME here; the host pulls it. */ @@ -20,7 +20,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -32,7 +32,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "sonarr-node"; @@ -61,7 +61,7 @@ async function must(command: string, timeoutMs?: number): Promise { } async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -70,7 +70,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -83,7 +83,7 @@ async function settled(withinMs = 480_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -115,11 +115,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } await mesh(`node add ${MACHINE}`); @@ -167,7 +167,7 @@ test("the mesh assigns sonarr's runtime, and it detects its key and serves its t }, ], }); - await must(`printf %s ${quote(manifest)} > /tmp/sonarr.json && docker cp /tmp/sonarr.json mesh-control:/sonarr.json`); + await must(`printf %s ${quote(manifest)} > /tmp/sonarr.json && docker cp /tmp/sonarr.json mesh-controller:/sonarr.json`); await mesh("module add /sonarr.json"); const issued = await mesh(`module issue sonarr --node ${MACHINE}`); diff --git a/test/integration/assigned-tools-confluence.test.ts b/test/integration/assigned-tools-confluence.test.ts index 681ebf3..00f3fed 100644 --- a/test/integration/assigned-tools-confluence.test.ts +++ b/test/integration/assigned-tools-confluence.test.ts @@ -17,7 +17,7 @@ * A tool would only fail if it were actually invoked without real creds — which this bed does not do, * because the point is exactly that serving does not require them. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh confluence builds mesh-runtime-confluence:development into the * local daemon; scenarios/tools-confluence.yml stocks it. There is no service image. */ @@ -29,7 +29,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -41,7 +41,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "tools-confluence"; @@ -72,7 +72,7 @@ async function must(command: string, timeoutMs?: number): Promise { /** The control plane, a container on the node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The reference a manifest should carry, once this scenario has been raised. */ @@ -81,9 +81,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -96,7 +96,7 @@ async function settled(withinMs = 600_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -128,12 +128,12 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - // Raise the substrate — store, broker, control — from the bundle. - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + // Raise the foundation — store, broker, control — from the bundle. + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } // The node joins its own mesh, so it is a node the mesh can assign to, and start the host so it @@ -185,7 +185,7 @@ test("the mesh assigns confluence: its tools-only runtime comes up and serves th ], }); - await must(`printf %s ${quote(manifest)} > /tmp/confluence.json && docker cp /tmp/confluence.json mesh-control:/confluence.json`); + await must(`printf %s ${quote(manifest)} > /tmp/confluence.json && docker cp /tmp/confluence.json mesh-controller:/confluence.json`); await mesh("module add /confluence.json"); // confluence serves tools, so it is issued a scoped broker account (it emits/consumes nothing else). const issued = await mesh(`module issue confluence --node ${MACHINE}`); diff --git a/test/integration/assigned-tools-gitlab.test.ts b/test/integration/assigned-tools-gitlab.test.ts index 70fcc19..622882d 100644 --- a/test/integration/assigned-tools-gitlab.test.ts +++ b/test/integration/assigned-tools-gitlab.test.ts @@ -16,7 +16,7 @@ * A tool would only fail if it were actually invoked without real creds — which this bed does not do, * because the point is exactly that serving does not require them. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh gitlab builds mesh-runtime-gitlab:development into the local * daemon; scenarios/tools-gitlab.yml stocks it. There is no service image — gitlab is tools-only. */ @@ -28,7 +28,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -40,7 +40,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "tools-gitlab"; @@ -71,7 +71,7 @@ async function must(command: string, timeoutMs?: number): Promise { /** The control plane, a container on the node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The reference a manifest should carry, once this scenario has been raised. */ @@ -80,9 +80,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -95,7 +95,7 @@ async function settled(withinMs = 600_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -127,12 +127,12 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - // Raise the substrate — store, broker, control — from the bundle. - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + // Raise the foundation — store, broker, control — from the bundle. + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } // The node joins its own mesh, so it is a node the mesh can assign to, and start the host so it @@ -184,7 +184,7 @@ test("the mesh assigns gitlab: its tools-only runtime comes up and serves the fu ], }); - await must(`printf %s ${quote(manifest)} > /tmp/gitlab.json && docker cp /tmp/gitlab.json mesh-control:/gitlab.json`); + await must(`printf %s ${quote(manifest)} > /tmp/gitlab.json && docker cp /tmp/gitlab.json mesh-controller:/gitlab.json`); await mesh("module add /gitlab.json"); // gitlab serves tools, so it is issued a scoped broker account (it emits/consumes nothing else). const issued = await mesh(`module issue gitlab --node ${MACHINE}`); diff --git a/test/integration/assigned-two-node-db.test.ts b/test/integration/assigned-two-node-db.test.ts index 33ea3d0..d8602f8 100644 --- a/test/integration/assigned-two-node-db.test.ts +++ b/test/integration/assigned-two-node-db.test.ts @@ -1,18 +1,18 @@ /** * The DB-consumer chain a single node cannot host, proved across two machines. * - * app-postgres and the mesh's own substrate store both want host port 5432, so they cannot share a - * machine. Every earlier catalogue bed put the provider on the same node as the substrate and got + * app-postgres and the mesh's own foundation store both want host port 5432, so they cannot share a + * machine. Every earlier catalogue bed put the provider on the same node as the foundation and got * away with it only because the provider published no 5432 a consumer ever reached, or because the - * substrate's store and the module's postgres were the same container. The moment a real + * foundation's store and the module's postgres were the same container. The moment a real * postgres PROVIDER must publish 5432 for real consumers to connect, it collides with the store the - * substrate already has there — and the chain is blocked single-node. + * foundation already has there — and the chain is blocked single-node. * - * This is the split that unblocks it. `anchor` runs the substrate (store, broker, control) and + * This is the split that unblocks it. `anchor` runs the foundation (store, broker, control) and * NOTHING else. `laptop` runs the whole chain: the postgres and redis PROVIDERS, and the baserow * and letta CONSUMERS that require them. Provider and consumers are co-located on laptop, so the * grant never crosses a node boundary and no overlay is needed — only enrolment crosses to anchor, - * over the underlay both machines share. And because the substrate store is on the OTHER node, the + * over the underlay both machines share. And because the foundation store is on the OTHER node, the * provider owns laptop's 5432 uncontested. * * The four manifests are the committed catalogue shapes (novox/hq ADR 0039/0047/0048), verbatim @@ -22,10 +22,10 @@ * ONCE; laptop converges once with every one up, and the two consumers are provisioned against the * database the provider on their own node gave them. * - * It needs a host binary and the substrate bundle: + * It needs a host binary and the foundation bundle: * * MESH_LAB_HOST_BINARY=.../mesh-host - * MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * * HELPER — stock the four runtimes into the local daemon before the run (some may already be there): * scripts/build-module-runtime.sh postgres /tmp/postgres.tar @@ -44,7 +44,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -56,11 +56,11 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "two-node-db"; -/** The node that carries the whole DB-consumer chain. anchor carries only the substrate. */ +/** The node that carries the whole DB-consumer chain. anchor carries only the foundation. */ const NODE = "laptop"; let instanceId = ""; @@ -88,7 +88,7 @@ async function must(machine: string, command: string, timeoutMs?: number): Promi /** The control plane, a container on the first node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must("anchor", `docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must("anchor", `docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The reference a manifest should carry, once this scenario has been raised. */ @@ -97,9 +97,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -128,7 +128,7 @@ async function settled(node: string, withinMs = 1_200_000): Promise { } | undefined; let said = ""; try { - const asked = await on("anchor", `docker exec mesh-control /mesh-control status --json`); + const asked = await on("anchor", `docker exec mesh-controller /mesh-controller status --json`); said = asked.out; if (asked.ok) state = JSON.parse(said); } catch (err) { @@ -169,13 +169,13 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - // The first node raises the substrate — store, broker, control — from the bundle its host carries, + // The first node raises the foundation — store, broker, control — from the bundle its host carries, // its digests rewritten to the ones this scenario's own registry serves. - await must("anchor", `cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must("anchor", `${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must("anchor", `cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must("anchor", `${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must("anchor", `docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } // Both machines join the one mesh, each with a token that says what the mesh calls it, and each @@ -196,7 +196,7 @@ after(async () => { await destroyAll(`${SCENARIO}-`); }, { timeout: 600_000 }); -test("the provider and its consumers ride the second node while the substrate owns 5432 on the first", { +test("the provider and its consumers ride the second node while the foundation owns 5432 on the first", { skip, timeout: 1_500_000, }, async () => { // ================================================================================================ @@ -392,7 +392,7 @@ test("the provider and its consumers ride the second node while the substrate ow // --- add, issue a scoped broker account, assign to laptop, then ONE push ------------------------- async function addIssueAssign(name: string, manifest: string): Promise { - await must("anchor", `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-control:/${name}.json`); + await must("anchor", `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); const issued = await mesh(`module issue ${name} --node ${NODE}`); assert.match(issued, /scoped to what it emits and consumes/, issued); @@ -400,7 +400,7 @@ test("the provider and its consumers ride the second node while the substrate ow } // The consumer connects to its provider by the provider's PRIVATE-NETWORK address — the binding's - // `at`, which mesh-control fills as "where the consuming machine is on the private network, empty + // `at`, which mesh-controller fills as "where the consuming machine is on the private network, empty // if it is not on one" (declaration.go). So even though provider and consumer are co-located on // laptop, the address baserow is handed is the mesh OVERLAY address, and it is empty unless the // machine is on the overlay. The overlay networking is therefore assigned first, to both nodes. @@ -411,7 +411,7 @@ test("the provider and its consumers ride the second node while the substrate ow // Providers first, then the consumers that require them. The mesh resolves the whole set at push // time regardless of order; this order simply reads like the dependency graph. Provider AND - // consumers all go to laptop; the 5432 conflict is gone because the substrate store is on anchor. + // consumers all go to laptop; the 5432 conflict is gone because the foundation store is on anchor. await addIssueAssign("postgres", postgresManifest); await addIssueAssign("redis", redisManifest); await addIssueAssign("baserow", baserowManifest); @@ -422,14 +422,14 @@ test("the provider and its consumers ride the second node while the substrate ow await settled(NODE); // ================================================================================================ - // THE two-node split — the substrate owns 5432 on anchor, the provider owns it on laptop. + // THE two-node split — the foundation owns 5432 on anchor, the provider owns it on laptop. // ================================================================================================ const onAnchor = await must("anchor", `docker ps --format '{{.Names}}'`); const onLaptop = await must(NODE, `docker ps --format '{{.Names}}'`); - assert.match(onAnchor, /(^|\n)mesh-store(\n|$)/, "the substrate store is not on the first node"); + assert.match(onAnchor, /(^|\n)mesh-store(\n|$)/, "the foundation store is not on the first node"); assert.doesNotMatch(onAnchor, /(^|\n)postgres(\n|$)/, - "the postgres provider landed on the substrate node — the 5432 collision this bed exists to avoid"); - assert.doesNotMatch(onLaptop, /(^|\n)mesh-store(\n|$)/, "the substrate store leaked onto the second node"); + "the postgres provider landed on the foundation node — the 5432 collision this bed exists to avoid"); + assert.doesNotMatch(onLaptop, /(^|\n)mesh-store(\n|$)/, "the foundation store leaked onto the second node"); assert.match(onLaptop, /(^|\n)postgres(\n|$)/, "the postgres provider is not on the second node"); // ================================================================================================ @@ -453,7 +453,7 @@ test("the provider and its consumers ride the second node while the substrate ow // REGRESSION (provider-seal-key): baserow's server.env DATABASE_PASSWORD is filled from the // ${secret:postgres-database} placeholder; its database.secret file carries the same credential via // the secrets: map. Both are baserow's one postgres password and MUST be equal. Before the - // mesh-control fix (secrets_into_files.go matched a need by provision name alone, not by consuming + // mesh-controller fix (secrets_into_files.go matched a need by provision name alone, not by consuming // module) the placeholder path took whichever co-located consumer came last — letta's — so the two // diverged and baserow authenticated with the wrong password. novox/hq 04-ISSUES/022. { @@ -496,7 +496,7 @@ test("the provider and its consumers ride the second node while the substrate ow } // ================================================================================================ - // Each module got its own scoped broker account on the substrate's broker (which is on anchor, + // Each module got its own scoped broker account on the foundation's broker (which is on anchor, // reached from laptop over the shared segment) — named for the node that runs it and the module. // ================================================================================================ const users = await must("anchor", `docker exec mesh-broker lavinmqctl list_users 2>&1`); diff --git a/test/integration/builds.test.ts b/test/integration/builds.test.ts index 539068b..5118a44 100644 --- a/test/integration/builds.test.ts +++ b/test/integration/builds.test.ts @@ -9,7 +9,7 @@ * agree over a wire. Everything either side of the wire is already asserted in its own suite. * * MESH_LAB_HOST_BINARY a built mesh-host - * MESH_LAB_BUNDLE the substrate bundle + * MESH_LAB_BUNDLE the foundation bundle * MESH_LAB_BUILDER a built mesh-builder */ @@ -20,7 +20,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; import { incus } from "../../src/incus/client.ts"; import { machineName } from "../../src/lifecycle/names.ts"; @@ -35,7 +35,7 @@ const skip = !capability.usable : !host || !existsSync(host) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle" : !builder || !existsSync(builder) ? "MESH_LAB_BUILDER is not set to a built mesh-builder" : false; @@ -79,12 +79,12 @@ async function must(command: string): Promise { } async function mesh(command: string): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`); + return must(`docker exec mesh-controller /mesh-controller ${command}`); } /** The bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } before(async () => { @@ -92,8 +92,8 @@ before(async () => { const raised = await raise(loadScenario(`scenarios/${SCENARIO}.yml`), {}); instanceId = raised.instanceId; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`); // The mesh's artifact store, standing where the `registry` module would. Read back rather than // assumed: a builder publishing into a registry that never came up fails several minutes later, @@ -166,7 +166,7 @@ test("a build that cannot succeed says why, and records nothing", { skip, timeou // A failure is a result. A build that fails silently is indistinguishable from a builder that // is not running, and those want completely different responses. const { out, ok } = await on( - `docker exec mesh-control /mesh-control build /root/does-not-exist --wait 120s`, + `docker exec mesh-controller /mesh-controller build /root/does-not-exist --wait 120s`, ); assert.equal(ok, false, "a build of nothing reported success"); assert.match(out, /could not build/, out); diff --git a/test/integration/canary.test.ts b/test/integration/canary.test.ts index 7584f22..e94b27e 100644 --- a/test/integration/canary.test.ts +++ b/test/integration/canary.test.ts @@ -35,7 +35,7 @@ const skip = !capability.usable : !host || !existsSync(host) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle" : false; const SCENARIO = "first-node"; @@ -62,7 +62,7 @@ async function must(command: string, timeoutMs?: number): Promise { } const mesh = (command: string, timeoutMs?: number) => - must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); before(async () => { if (skip) return; @@ -70,9 +70,9 @@ before(async () => { onProgress: (m) => console.log(`raise: ${m}`), }); instanceId = raised.instanceId; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${ + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${ pinnedInto(readFileSync(bundle, "utf8"), raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 300_000); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 300_000); // **And the machine joins.** Applying the bundle raises a control plane; it does not tell that // control plane a machine exists. Leaving this out is what the first run of this canary found, @@ -111,7 +111,7 @@ test("a module reaches the machine", { skip, timeout: 600_000 }, async () => { { id: "note", type: "file", path: "/var/lib/canary/it-arrived", content: "yes\n", mode: "0644" }, ], }))} > /tmp/canary.json`); - await must(`docker cp /tmp/canary.json mesh-control:/canary.json`); + await must(`docker cp /tmp/canary.json mesh-controller:/canary.json`); await mesh("module add /canary.json"); await mesh(`assign ${MACHINE} canary`); await mesh(`push ${MACHINE}`, 300_000); @@ -151,7 +151,7 @@ test("a consumer gets a credential it can use", { skip, timeout: 600_000 }, asyn ], }))} > /tmp/canary-app.json`); for (const name of ["canary-store", "canary-app"]) { - await must(`docker cp /tmp/${name}.json mesh-control:/${name}.json`); + await must(`docker cp /tmp/${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); await mesh(`assign ${MACHINE} ${name}`); } diff --git a/test/integration/certificates.test.ts b/test/integration/certificates.test.ts index 37aa663..1fe8e64 100644 --- a/test/integration/certificates.test.ts +++ b/test/integration/certificates.test.ts @@ -25,7 +25,7 @@ const proxy = process.env["MESH_LAB_ROUTE_PROXY"] ?? ""; const skip = !capability.usable ? `lab not usable: ${capability.why}` : !proxy - ? "set MESH_LAB_ROUTE_PROXY to a built proxy (mesh-control: go build ./examples/route-proxy)" + ? "set MESH_LAB_ROUTE_PROXY to a built proxy (mesh-controller: go build ./examples/route-proxy)" : false; const SCENARIO = "a-public-name"; diff --git a/test/integration/events.test.ts b/test/integration/events.test.ts index 3ed2758..c986311 100644 --- a/test/integration/events.test.ts +++ b/test/integration/events.test.ts @@ -5,8 +5,8 @@ * credential is delivered over it. This proves the other half of the bus (novox/hq ADR 0046): the * events exchange, where a module emits and any number listen, and the audit logger consumes `#` * and writes down what happened. It runs against the `mesh-broker` this scenario's own host raised - * from the substrate bundle — not a broker a test stood up — because "the mesh hosts the broker" - * (tier-1 substrate) is the thing being relied on. + * from the foundation bundle — not a broker a test stood up — because "the mesh hosts the broker" + * (tier-1 foundation) is the thing being relied on. * * The wire shape it asserts is ADR 0047: metadata rides as headers so the body is only the * payload, a consumer gets a durable per-consumer queue `..events`, and a @@ -14,10 +14,10 @@ * on the raised broker is the check that the runtime provisioned the contract, not just that a * message happened to arrive. * - * It needs the host binary and the substrate bundle, like the mesh walk, plus a runtime image: + * It needs the host binary and the foundation bundle, like the mesh walk, plus a runtime image: * * MESH_LAB_HOST_BINARY=.../mesh-host - * MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * MESH_LAB_RUNTIME=.../mesh-runtime-audit.tar (docker save of the runtime+audit-logger image; * built by scripts/build-runtime-image.sh) * @@ -33,7 +33,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; import { incus } from "../../src/incus/client.ts"; import { machineName } from "../../src/lifecycle/names.ts"; @@ -48,7 +48,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : !runtime || !existsSync(runtime) ? "MESH_LAB_RUNTIME is not set to a runtime image tar (scripts/build-runtime-image.sh)" : false; @@ -58,7 +58,7 @@ const MACHINE = "anchor"; /** Where the audit-logger container writes its trail, on the machine — mounted from a host dir. */ const TRAIL_DIR = "/var/lib/mesh-audit"; const TRAIL = `${TRAIL_DIR}/audit.log`; -/** The broker the substrate raised, reachable on the node's loopback (novox/hq ADR 0001). */ +/** The broker the foundation raised, reachable on the node's loopback (novox/hq ADR 0001). */ const BROKER = "amqp://guest:guest@127.0.0.1:5672/"; let instanceId = ""; @@ -73,7 +73,7 @@ function quote(s: string): string { * A command on the machine, its exit read from a marker on its own line. * * `exec 2>&1` on its own first line and the marker on its own last line, so a command that carries - * a heredoc — the substrate bundle is written with one — terminates where it says it does rather + * a heredoc — the foundation bundle is written with one — terminates where it says it does rather * than swallowing the marker (the fault mesh.test.ts documents). */ async function on(command: string, timeoutMs?: number): Promise<{ out: string; ok: boolean }> { @@ -92,14 +92,14 @@ async function must(command: string, timeoutMs?: number): Promise { } /** - * The substrate bundle, its image references pointed at this scenario's own registry. + * The foundation bundle, its image references pointed at this scenario's own registry. * * A digest belongs to whatever registry serves it, so the committed bundle names a registry that * is not this one; matching by repository and rewriting to the digest this registry assigned is * what makes it applicable (the same rewrite mesh.test.ts does). */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } /** Read the trail back as parsed JSON lines. */ @@ -120,14 +120,14 @@ before(async () => { }); instanceId = raised.instanceId; - // The node raises its substrate — store, broker and the rest — from the bundle, applied from a + // The node raises its foundation — store, broker and the rest — from the bundle, applied from a // file because the control plane's image is named by the ID this machine holds it under, // which is not knowable until it has been handed over. - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const running = await must(`docker ps --format '{{.Names}}'`); - assert.match(running, /mesh-broker/, `the substrate did not raise a broker:\n${running}`); + assert.match(running, /mesh-broker/, `the foundation did not raise a broker:\n${running}`); // Bring the runtime+audit-logger image onto the machine. Loaded, not pulled: the machine has no // route out (novox/hq the lab is a closed address space), so the image arrives as a tar the way diff --git a/test/integration/fresh-mesh.test.ts b/test/integration/fresh-mesh.test.ts index 6e940f9..a14a221 100644 --- a/test/integration/fresh-mesh.test.ts +++ b/test/integration/fresh-mesh.test.ts @@ -31,9 +31,9 @@ * MESH_LAB_INCUS='sudo -n incus' * MESH_LAB_HOST_BINARY=.../mesh-host/mesh-host * MESH_LAB_BOOTSTRAP_BINARY=.../mesh-host/mesh-bootstrap - * MESH_LAB_BUNDLE=.../mesh-host/examples/substrate-first-node.lock + * MESH_LAB_BUNDLE=.../mesh-host/examples/foundation-first-node.lock * MESH_LAB_CATALOG=.../mesh-catalog/modules - * MESH_LAB_SOURCE=/mesh-control.git MESH_LAB_SOURCE_REF= + * MESH_LAB_SOURCE=/mesh-controller.git MESH_LAB_SOURCE_REF= * MESH_LAB_KEEP=1 to leave it standing afterwards */ import { test, before, after } from "node:test"; @@ -44,7 +44,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec, push } from "../../src/lifecycle/operate.ts"; import { bootstrapBinaryPath, hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle } from "./harness.ts"; import { genesis, type GenesisResult } from "./genesis.ts"; const SCENARIO = "fresh-mesh"; @@ -68,7 +68,7 @@ const REGISTRY = `${ANCHOR}:5000`; */ const BASE = { module: "mesh-tools", repo: "mesh-tools", path: "" }; /** - * What `amqp-ping` requires, and what the substrate does not supply. + * What `amqp-ping` requires, and what the foundation does not supply. * * The installer raises a broker, but as a bundle resource — plumbing, not a module the mesh has a * record of, so it provides nothing to anything. A module asking for `amqp` is asking for a @@ -86,7 +86,7 @@ const PROVIDER = { module: "lavinmq", repo: "mesh-catalog", path: "modules/lavin const STORE = { module: "postgres", repo: "mesh-catalog", path: "modules/postgres", container: "mesh-postgres" }; const CATALOGUE = { module: "mesh-catalog", repo: "mesh-catalog", path: "modules/mesh-catalog", container: "mesh-catalog" }; /** The control plane, rebuilt from its own repository — the step that ends the installer's tenure. */ -const CONTROL_PLANE = { module: "mesh-control", repo: "mesh-control", path: "", container: "mesh-control" }; +const CONTROL_PLANE = { module: "mesh-controller", repo: "mesh-controller", path: "", container: "mesh-controller" }; /** Named once, because the step title is also how later steps say what they waited on. */ const NEEDS = "the mesh runs a broker for that module to talk to"; const GENESIS = "a bare machine becomes a mesh of one, raised by the installer"; @@ -154,7 +154,7 @@ const skip = !installer ? "MESH_LAB_BOOTSTRAP_BINARY is not set to a built mesh-bootstrap" : !source ? "MESH_LAB_SOURCE is not set to the repository the control plane is built from" : !sourceRef ? "MESH_LAB_SOURCE_REF is not set to the commit to build" : - !bundle || !existsSync(bundle) ? "MESH_LAB_BUNDLE is not set to a substrate template" : + !bundle || !existsSync(bundle) ? "MESH_LAB_BUNDLE is not set to a foundation template" : !catalogDir || !existsSync(catalogDir) ? "MESH_LAB_CATALOG is not set to mesh-catalog/modules" : false; @@ -180,7 +180,7 @@ async function must(machine: string, command: string, timeoutMs?: number): Promi return out; } async function mesh(command: string, timeoutMs?: number): Promise { - return must(CONTROL, `docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(CONTROL, `docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** A module the mesh has to make for itself: where its source is, and what it runs when it works. */ @@ -239,7 +239,7 @@ async function waitForContainer(node: string, container: string, seconds = 200): * * **The control plane runs in a container, so a file on the machine is not a file it can open.** * Pushing the manifest to the machine and naming that path got `no such file or directory` from - * inside mesh-control, which is correct and was briefly mistaken for a missing manifest. It is + * inside mesh-controller, which is correct and was briefly mistaken for a missing manifest. It is * copied the last step of the way with `docker cp`. * * **Into the root, not into /tmp.** The control plane's image is a minimal one and has no `/tmp` @@ -250,7 +250,7 @@ async function registerModule(module: string, manifest: string): Promise const onMachine = `/tmp/${module}.json`; const inContainer = `/${module}.json`; await push(instanceId, CONTROL, manifest, onMachine); - await must(CONTROL, `docker cp ${onMachine} mesh-control:${inContainer}`); + await must(CONTROL, `docker cp ${onMachine} mesh-controller:${inContainer}`); return mesh(`module add ${inContainer}`); } function tokenFrom(said: string): string { @@ -325,7 +325,7 @@ before(async () => { // whose anchor is somewhere else every node, including this one, would enrol against an // address nothing answers on. The installer refuses to guess it and says so, which is // right: it does not know what this machine is called from outside. - bundleTemplate: substrateBundle(bundle, []).replaceAll("192.0.2.10:5671", `${ANCHOR}:5671`), + bundleTemplate: foundationBundle(bundle, []).replaceAll("192.0.2.10:5671", `${ANCHOR}:5671`), registry: REGISTRY, source, sourceRef, @@ -371,7 +371,7 @@ before(async () => { return built; }); - // A store of its own. **Not the substrate's.** The installer raises a store for the control + // A store of its own. **Not the foundation's.** The installer raises a store for the control // plane to keep its own records in, the way it raises a broker — plumbing, not a module the mesh // has any record of, so it provides nothing to anything. A module that wants a database wants a // provider in the graph, and the catalogue below is the first thing to want one. diff --git a/test/integration/genesis-single.test.ts b/test/integration/genesis-single.test.ts index 42dc73a..d27ca2f 100644 --- a/test/integration/genesis-single.test.ts +++ b/test/integration/genesis-single.test.ts @@ -12,7 +12,7 @@ * MESH_LAB_INCUS='sudo -n incus' * MESH_LAB_HOST_BINARY=.../mesh-host/mesh-host * MESH_LAB_BOOTSTRAP_BINARY=.../mesh-host/mesh-bootstrap - * MESH_LAB_BUNDLE=.../mesh-host/examples/substrate-first-node.lock + * MESH_LAB_BUNDLE=.../mesh-host/examples/foundation-first-node.lock * MESH_LAB_CATALOG=.../mesh-catalog/modules * MESH_LAB_KEEP=1 to leave it standing afterwards */ @@ -23,7 +23,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy } from "../../src/lifecycle/operate.ts"; import { bootstrapBinaryPath, hostBinaryPath } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle } from "./harness.ts"; import { genesis, type GenesisResult } from "./genesis.ts"; const SCENARIO = "genesis-single"; @@ -49,7 +49,7 @@ const skip = "bootstrap IMAGE=mesh-builder:development`)" : !source ? "MESH_LAB_SOURCE is not set to the repository the control plane is built from" : !sourceRef ? "MESH_LAB_SOURCE_REF is not set to the commit to build" : - !bundle || !existsSync(bundle) ? "MESH_LAB_BUNDLE is not set to a substrate template" : + !bundle || !existsSync(bundle) ? "MESH_LAB_BUNDLE is not set to a foundation template" : !catalogDir || !existsSync(catalogDir) ? "MESH_LAB_CATALOG is not set to mesh-catalog/modules" : false; @@ -75,10 +75,10 @@ before(async () => { installer: installer as string, catalogDir, // The template, not a bundle. The store and broker become the references mesh-catalog pins, - // and the machine pulls them over its uplink like any first node. mesh-control is left + // and the machine pulls them over its uplink like any first node. mesh-controller is left // naming a registry that does not exist — the installer overwrites it, and leaving it proves // that it does. - bundleTemplate: substrateBundle(bundle, []), + bundleTemplate: foundationBundle(bundle, []), source, sourceRef, // Phase two builds from the same forge; the repositories sit beside the control plane's. diff --git a/test/integration/genesis.ts b/test/integration/genesis.ts index 64222cb..2793b52 100644 --- a/test/integration/genesis.ts +++ b/test/integration/genesis.ts @@ -37,7 +37,7 @@ export interface GenesisOptions { /** A checkout of mesh-catalog's `modules/` on this workstation. */ catalogDir: string; /** - * The substrate TEMPLATE's content — not a bundle. The installer produces the bundle from it, + * The foundation TEMPLATE's content — not a bundle. The installer produces the bundle from it, * replacing the control plane's image with the id of the image it carries. */ bundleTemplate: string; @@ -91,7 +91,7 @@ export function stepIn(said: string): string { export async function genesis(o: GenesisOptions): Promise { const node = o.node; const registry = o.registry ?? "127.0.0.1:5000"; - const modules = o.catalogueModules ?? ["distribution", "mesh-control", "builder"]; + const modules = o.catalogueModules ?? ["distribution", "mesh-controller", "builder"]; const catalogueOnMachine = o.catalogueOnMachine ?? "/opt/mesh-catalog"; const log = o.log ?? (() => {}); @@ -147,9 +147,9 @@ export async function genesis(o: GenesisOptions): Promise { } report.push(` catalogue full checkout at ${catalogueOnMachine} (${modules.join(", ")} + phase two)`); - const local = join(tmpdir(), `mesh-lab-substrate-${process.pid}-${node}.lock`); + const local = join(tmpdir(), `mesh-lab-foundation-${process.pid}-${node}.lock`); writeFileSync(local, o.bundleTemplate); - await push(o.instanceId, node, local, "/tmp/substrate-template.lock"); + await push(o.instanceId, node, local, "/tmp/foundation-template.lock"); // Supervise the host as a service (the real install path) when asked — the only way it survives a // reboot. Installs the shipped packaging in the machine, then lets --host-service start+enable it. @@ -165,7 +165,7 @@ export async function genesis(o: GenesisOptions): Promise { BOOTSTRAP_PATH, `--source ${o.source}`, `--source-ref ${o.sourceRef}`, - `--bundle /tmp/substrate-template.lock`, + `--bundle /tmp/foundation-template.lock`, `--catalog ${catalogueOnMachine}`, `--node ${node}`, `--registry ${registry}`, @@ -212,9 +212,9 @@ export async function genesis(o: GenesisOptions): Promise { // ------------------------------------------------------------------------------------------ // 1. The control plane answers, asked of the PERMANENT container by name. - const answered = await on(`docker exec mesh-control /mesh-control status`, 60_000); + const answered = await on(`docker exec mesh-controller /mesh-controller status`, 60_000); report.push(` control plane ${answered.ok ? answered.out.split("\n")[0] : "NO ANSWER"}`); - if (!answered.ok) return stop("after the last step", `mesh-control does not answer:\n${answered.out}`); + if (!answered.ok) return stop("after the last step", `mesh-controller does not answer:\n${answered.out}`); // 2. The registry replies on /v2/. A container that is up is not a registry that serves. const v2 = await on(`curl -s -o /dev/null -w '%{http_code}' --max-time 10 http://${registry}/v2/`); @@ -224,17 +224,17 @@ export async function genesis(o: GenesisOptions): Promise { // 3. THE PIVOT COMPLETED — the running control plane is pinned by a digest THIS MESH'S REGISTRY // assigned, not by an image id (ADR 0067 states this check in as many words). - const pinnedTo = (await on(`docker inspect --format '{{.Config.Image}}' mesh-control`)).out.trim(); + const pinnedTo = (await on(`docker inspect --format '{{.Config.Image}}' mesh-controller`)).out.trim(); report.push(` pinned to ${pinnedTo || "(nothing)"}`); if (/^sha256:[0-9a-f]{64}$/.test(pinnedTo)) { return stop("after the last step", - `mesh-control is running from ${pinnedTo}, which is an IMAGE ID — the digest of the image's ` + + `mesh-controller is running from ${pinnedTo}, which is an IMAGE ID — the digest of the image's ` + `own configuration, which no registry ever served. The pivot did not happen, so this mesh ` + `cannot upgrade itself (novox/hq ADR 0067, "the pivot completed").`); } - if (!new RegExp(`^${registry.replaceAll(".", "\\.")}/mesh-control@sha256:[0-9a-f]{64}$`).test(pinnedTo)) { + if (!new RegExp(`^${registry.replaceAll(".", "\\.")}/mesh-controller@sha256:[0-9a-f]{64}$`).test(pinnedTo)) { return stop("after the last step", - `mesh-control is running from ${pinnedTo || "nothing this bed could read"}, which is not a ` + + `mesh-controller is running from ${pinnedTo || "nothing this bed could read"}, which is not a ` + `digest assigned by ${registry}.`); } @@ -249,34 +249,34 @@ export async function genesis(o: GenesisOptions): Promise { // installer that quietly built something else would satisfy a weaker check and raise a mesh // nobody asked for. const wanted = o.sourceRef.slice(0, 8); - if (!new RegExp(`built mesh-control from ${wanted}`).test(said)) { + if (!new RegExp(`built mesh-controller from ${wanted}`).test(said)) { return stop("after the last step", - `the installer never said it built mesh-control from ${wanted}. What runs may have been ` + + `the installer never said it built mesh-controller from ${wanted}. What runs may have been ` + `carried rather than made here, which is a mesh that cannot rebuild its own control plane. ` + `The installer said:\n${said.split("\n").filter((l) => /built|build/.test(l)).join("\n") || "(nothing about building)"}`); } - report.push(` built here mesh-control from ${wanted}, by the carried builder`); + report.push(` built here mesh-controller from ${wanted}, by the carried builder`); // 3b. And the registry really serves it. A reference is a claim; a tag list is the registry agreeing. - const tags = await on(`curl -s --max-time 10 http://${registry}/v2/mesh-control/tags/list`); + const tags = await on(`curl -s --max-time 10 http://${registry}/v2/mesh-controller/tags/list`); report.push(` registry holds ${tags.out.trim() || "nothing"}`); if (!tags.out.includes("genesis")) { return stop("after the last step", - `${registry} does not serve mesh-control, so the digest the container is pinned to names an ` + + `${registry} does not serve mesh-controller, so the digest the container is pinned to names an ` + `image nothing can pull: ${tags.out.trim()}`); } // 4. The temporary control plane is GONE. The name is the audit. - const temp = await on(`docker inspect --format '{{.State.Status}}' temp-mesh-control`); - report.push(` temp-mesh-control ${temp.ok ? `STILL HERE (${temp.out.trim()})` : "gone"}`); + const temp = await on(`docker inspect --format '{{.State.Status}}' temp-mesh-controller`); + report.push(` temp-mesh-controller ${temp.ok ? `STILL HERE (${temp.out.trim()})` : "gone"}`); if (temp.ok) { return stop("after the last step", - `temp-mesh-control is still ${temp.out.trim()}. Two control planes are consuming this mesh's ` + + `temp-mesh-controller is still ${temp.out.trim()}. Two control planes are consuming this mesh's ` + `broker queues; neither is wrong and the pivot is not finished.`); } // 5. And the mesh has heard from its one node. - const nodes = await on(`docker exec mesh-control /mesh-control node list`); + const nodes = await on(`docker exec mesh-controller /mesh-controller node list`); report.push(` node list ${nodes.out.trim().split("\n").join(" | ")}`); const line = nodes.out.split("\n").map((l) => l.trim()).find((l) => l.startsWith(`${node} `)); if (!line || !/^\S+\s+here\b/.test(line)) { diff --git a/test/integration/harness.ts b/test/integration/harness.ts index 8d82bc0..4cc9f99 100644 --- a/test/integration/harness.ts +++ b/test/integration/harness.ts @@ -17,19 +17,19 @@ import { duplicateAddresses, describeConflicts, type Held } from "../../src/life import type { Scenario } from "../../src/declaration/types.ts"; import { mustBeHandedOver, pinnedInto, referenceFor, repositoryOf, type HeldImage } from "../../src/pinning.ts"; -// --- the substrate bundle, and what its three images are on a real machine --------------------- +// --- the foundation bundle, and what its three images are on a real machine --------------------- /** * The example bundle in mesh-host names a registry that no longer exists. * - * `examples/substrate-first-node.lock` was written **for a target**, and the target was the lab: it + * `examples/foundation-first-node.lock` was written **for a target**, and the target was the lab: it * pins `192.0.2.250:5000/…` because that is where the registry the lab used to raise served from. * That registry is gone, so those three references name nothing. * * Two of them are ordinary third-party images and belong to the internet. Rather than invent * digests here, they are the ones the mesh's own modules already pin — mesh-catalog's `postgres` - * and `lavinmq` — so the substrate's store and broker are literally the images the mesh runs. The - * third, mesh-control, exists in no registry at all and becomes the ID the machine holds it under. + * and `lavinmq` — so the foundation's store and broker are literally the images the mesh runs. The + * third, mesh-controller, exists in no registry at all and becomes the ID the machine holds it under. * * **The bundle itself should be fixed in mesh-host**, and this substitution deleted with it. It is * here because the file lives in another repository and because a fixture that lies about where an @@ -41,13 +41,13 @@ const UPSTREAM_BROKER = "cloudamqp/lavinmq@sha256:3eb54c12916d700a978c2ea86e6362cd4974b0e3189508718006d4e6d341246b"; /** - * The substrate bundle as a machine should receive it. + * The foundation bundle as a machine should receive it. * * Third-party references become upstream ones, which the machine pulls over its uplink; ours * become the ID the machine was handed. Nothing points inside the scenario any more, which is the * whole of this change: what the bed proves about a bootstrap is now what would happen anywhere. */ -export function substrateBundle(path: string, held: HeldImage[]): string { +export function foundationBundle(path: string, held: HeldImage[]): string { let text = readFileSync(path, "utf8"); text = text.replaceAll(/[A-Za-z0-9_.:-]+\/postgres@sha256:[0-9a-f]{64}/g, UPSTREAM_STORE); text = text.replaceAll( diff --git a/test/integration/lavinmq-bed.test.ts b/test/integration/lavinmq-bed.test.ts index e2326d5..97a68a7 100644 --- a/test/integration/lavinmq-bed.test.ts +++ b/test/integration/lavinmq-bed.test.ts @@ -1,12 +1,12 @@ /** - * A lavinmq PROVIDER and a consumer of it ride one node while the substrate's own broker owns 5672 on + * A lavinmq PROVIDER and a consumer of it ride one node while the foundation's own broker owns 5672 on * another — the end-to-end proof that a module which needs a message queue gets its OWN broker. * * lavinmq is the mesh's control-plane broker AND a user-facing capability: a consumer that requires * `amqp` is given a scoped vhost + user on a lavinmq PROVIDER, not an account on the control broker - * (novox/hq ADR 0048). That makes it the two-node case, the twin of two-node-db: the substrate's + * (novox/hq ADR 0048). That makes it the two-node case, the twin of two-node-db: the foundation's * broker publishes 5672 on anchor, and a lavinmq provider must publish 5672 for its consumers to - * reach it — so the two cannot share a machine. `anchor` runs the substrate and nothing else; `laptop` + * reach it — so the two cannot share a machine. `anchor` runs the foundation and nothing else; `laptop` * runs the provider AND the amqp-ping consumer, co-located, and owns laptop's 5672 uncontested. * * The proof is layered: @@ -14,7 +14,7 @@ * config BEFORE the broker started (ADR 0052) — proven because the broker came up at all and is * gone from `docker ps -a` (a step, not a service); * - the lavinmq service and BOTH runtimes (bootstrap done, provisioner running) are up and stable; - * - each module got its own scoped broker account on the substrate broker (anchor), named for the + * - each module got its own scoped broker account on the foundation broker (anchor), named for the * node that runs it; * - the provisioner (in mesh-lavinmq on laptop) created the consumer's vhost AND user, both named * for the login the mesh derived — checked with `lavinmqctl` inside the broker; @@ -25,12 +25,12 @@ * its vhost — the provider named the vhost after the login — and nothing is hardcoded; the provider's * `serves` carries the port so the consumer references `${bound:amqp:port}`. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * * HELPER — stock the two runtimes into the local daemon before the run (some may already be there): * scripts/build-module-runtime.sh lavinmq /tmp/lavinmq.tar * scripts/build-module-runtime.sh amqp-ping /tmp/amqp-ping.tar - * cloudamqp/lavinmq:latest must be in the local daemon too (it is the substrate's own broker image); + * cloudamqp/lavinmq:latest must be in the local daemon too (it is the foundation's own broker image); * scenarios/lavinmq-bed.yml stocks all of them, and each node pulls what it runs by digest. */ @@ -41,7 +41,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -53,11 +53,11 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "lavinmq-bed"; -/** The node that carries the provider and its consumer. anchor carries only the substrate. */ +/** The node that carries the provider and its consumer. anchor carries only the foundation. */ const NODE = "laptop"; /** The login the mesh derives for the consumer: mesh__ (slug "ping"; ADR 0049). */ const CONSUMER_LOGIN = "mesh_laptop_ping"; @@ -86,7 +86,7 @@ async function must(machine: string, command: string, timeoutMs?: number): Promi /** The control plane, a container on the first node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must("anchor", `docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must("anchor", `docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The reference a manifest should carry, once this scenario has been raised. */ @@ -95,9 +95,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -118,7 +118,7 @@ async function settled(node: string, withinMs = 1_200_000): Promise { } | undefined; let said = ""; try { - const asked = await on("anchor", `docker exec mesh-control /mesh-control status --json`); + const asked = await on("anchor", `docker exec mesh-controller /mesh-controller status --json`); said = asked.out; if (asked.ok) state = JSON.parse(said); } catch (err) { @@ -157,11 +157,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must("anchor", `cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must("anchor", `${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must("anchor", `cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must("anchor", `${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must("anchor", `docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } for (const [machine, node] of [["anchor", "anchor"], ["laptop", "laptop"]] as const) { @@ -178,7 +178,7 @@ after(async () => { await destroyAll(`${SCENARIO}-`); }, { timeout: 600_000 }); -test("the lavinmq provider and its consumer ride laptop while the substrate broker owns 5672 on anchor", { +test("the lavinmq provider and its consumer ride laptop while the foundation broker owns 5672 on anchor", { skip, timeout: 1_500_000, }, async () => { // ================================================================================================ @@ -287,7 +287,7 @@ test("the lavinmq provider and its consumer ride laptop while the substrate brok // --- add, issue a scoped broker account, assign to laptop -------------------------------------- async function addIssueAssign(name: string, manifest: string): Promise { - await must("anchor", `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-control:/${name}.json`); + await must("anchor", `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); const issued = await mesh(`module issue ${name} --node ${NODE}`); assert.match(issued, /broker account/, issued); @@ -311,14 +311,14 @@ test("the lavinmq provider and its consumer ride laptop while the substrate brok await settled(NODE); // ================================================================================================ - // THE two-node split — the substrate broker owns 5672 on anchor, the provider owns it on laptop. + // THE two-node split — the foundation broker owns 5672 on anchor, the provider owns it on laptop. // ================================================================================================ const onAnchor = await must("anchor", `docker ps --format '{{.Names}}'`); const onLaptop = await must(NODE, `docker ps --format '{{.Names}}'`); - assert.match(onAnchor, /(^|\n)mesh-broker(\n|$)/, "the substrate broker is not on the first node"); + assert.match(onAnchor, /(^|\n)mesh-broker(\n|$)/, "the foundation broker is not on the first node"); assert.doesNotMatch(onAnchor, /(^|\n)lavinmq(\n|$)/, - "the lavinmq provider landed on the substrate node — the 5672 collision this bed exists to avoid"); - assert.doesNotMatch(onLaptop, /(^|\n)mesh-store(\n|$)/, "the substrate store leaked onto the second node"); + "the lavinmq provider landed on the foundation node — the 5672 collision this bed exists to avoid"); + assert.doesNotMatch(onLaptop, /(^|\n)mesh-store(\n|$)/, "the foundation store leaked onto the second node"); assert.match(onLaptop, /(^|\n)lavinmq(\n|$)/, "the lavinmq provider is not on the second node"); // ================================================================================================ @@ -359,7 +359,7 @@ test("the lavinmq provider and its consumer ride laptop while the substrate brok } // ================================================================================================ - // Each module got its own scoped broker account on the substrate broker (anchor), named for the + // Each module got its own scoped broker account on the foundation broker (anchor), named for the // node that runs it and the module. // ================================================================================================ const users = await must("anchor", `docker exec mesh-broker lavinmqctl list_users 2>&1`); diff --git a/test/integration/local-model-bed.test.ts b/test/integration/local-model-bed.test.ts index 981734c..7a01fe0 100644 --- a/test/integration/local-model-bed.test.ts +++ b/test/integration/local-model-bed.test.ts @@ -15,7 +15,7 @@ * asserts the templated URL and that a request to it reaches the running server (ollama answers * /v1/models even with no model pulled — the wiring is what is proven, not a model's output). * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * No module runtime image is built — both modules are pure declaration. */ @@ -26,7 +26,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -38,7 +38,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "local-model-bed"; @@ -67,11 +67,11 @@ async function must(command: string, timeoutMs?: number): Promise { } async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } async function meshTry(command: string): Promise<{ out: string; ok: boolean }> { - return on(`docker exec mesh-control /mesh-control ${command}`); + return on(`docker exec mesh-controller /mesh-controller ${command}`); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -80,7 +80,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -117,7 +117,7 @@ async function settled(withinMs = 600_000): Promise { } async function addAssign(name: string, manifest: string): Promise { - await must(`printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-control:/${name}.json`); + await must(`printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); await mesh(`module issue ${name} --node ${MACHINE}`); await mesh(`assign ${MACHINE} ${name}`); @@ -132,11 +132,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } await mesh(`node add ${MACHINE}`); diff --git a/test/integration/mesh-grant-end-to-end.test.ts b/test/integration/mesh-grant-end-to-end.test.ts index 0d96850..167aff9 100644 --- a/test/integration/mesh-grant-end-to-end.test.ts +++ b/test/integration/mesh-grant-end-to-end.test.ts @@ -11,7 +11,7 @@ * Mint on one side and create on the other agreeing, with no shared key and nothing placed by the * test, is the entire provider/consumer contract working as one thing. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh redis builds mesh-runtime-redis:development, which * scenarios/redis-node.yml stocks. */ @@ -23,7 +23,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -35,7 +35,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "redis-node"; @@ -64,7 +64,7 @@ async function must(command: string, timeoutMs?: number): Promise { } async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -73,7 +73,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -86,7 +86,7 @@ async function settled(withinMs = 480_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -118,11 +118,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } await mesh(`node add ${MACHINE}`); @@ -201,12 +201,12 @@ test("the mesh grants a consumer redis's cache, and the credential it delivers a resources: [{ id: "state", type: "directory", path: "/var/lib/cacheuser", mode: "0700" }], }); - await must(`printf %s ${quote(redisManifest)} > /tmp/redis.json && docker cp /tmp/redis.json mesh-control:/redis.json`); + await must(`printf %s ${quote(redisManifest)} > /tmp/redis.json && docker cp /tmp/redis.json mesh-controller:/redis.json`); await mesh("module add /redis.json"); await mesh(`module issue redis --node ${MACHINE}`); await mesh(`assign ${MACHINE} redis`); - await must(`printf %s ${quote(consumerManifest)} > /tmp/cacheuser.json && docker cp /tmp/cacheuser.json mesh-control:/cacheuser.json`); + await must(`printf %s ${quote(consumerManifest)} > /tmp/cacheuser.json && docker cp /tmp/cacheuser.json mesh-controller:/cacheuser.json`); await mesh("module add /cacheuser.json"); await mesh(`assign ${MACHINE} cacheuser`); diff --git a/test/integration/mesh.test.ts b/test/integration/mesh.test.ts index 5c249db..46487cd 100644 --- a/test/integration/mesh.test.ts +++ b/test/integration/mesh.test.ts @@ -6,10 +6,10 @@ * project keeps saying cannot be checked any other way (novox/hq ADR 0001: every fault of * 2026-08-22 was found in production because nothing could be stood up locally). * - * It needs a host binary and the substrate bundle: + * It needs a host binary and the foundation bundle: * * MESH_LAB_HOST_BINARY=.../mesh-host - * MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * * The bundle's image references are rewritten to the ones this scenario's own registry serves. * A digest belongs to whatever registry serves it, so a committed bundle names a registry that is @@ -24,7 +24,7 @@ import { raise } from "../../src/lifecycle/raise.ts"; import { pinnedInto, stillUnpinned, type HeldImage } from "../../src/pinning.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import { incus } from "../../src/incus/client.ts"; import { machineName } from "../../src/lifecycle/names.ts"; import { ready, returnTo, keep, rememberStock, warmStock } from "../../src/warm.ts"; @@ -36,7 +36,7 @@ const capability = await labIsUsable(); const binary = hostBinaryPath(); const bundle = process.env["MESH_LAB_BUNDLE"] ?? ""; const builder = process.env["MESH_LAB_BUILDER"] ?? ""; -/** mesh-control's `examples/modules`, so the manifests proven here are the ones that ship. */ +/** mesh-controller's `examples/modules`, so the manifests proven here are the ones that ship. */ const moduleExamples = process.env["MESH_LAB_MODULES"] ?? ""; const skip = !capability.usable @@ -44,7 +44,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "two-nodes"; @@ -105,7 +105,7 @@ async function must(machine: string, command: string, timeoutMs?: number): Promi /** The control plane, which runs in a container on the first node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must("anchor", `docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must("anchor", `docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** @@ -144,7 +144,7 @@ async function settled(node: string, withinMs = 480_000): Promise { let said = ""; try { const asked = await on("anchor", - `docker exec mesh-control /mesh-control status --json`); + `docker exec mesh-controller /mesh-controller status --json`); said = asked.out; // Parsed inside the try on purpose: a truncated answer from a struggling machine is the // same fact as no answer, and the likeliest moment for one is exactly the machine this @@ -186,11 +186,11 @@ async function settled(node: string, withinMs = 480_000): Promise { * The bundle, as a machine should receive it. * * The committed example was written for a target that had a registry the lab raised. Its two - * third-party images become upstream references the machine pulls itself; mesh-control, which + * third-party images become upstream references the machine pulls itself; mesh-controller, which * exists in no registry, becomes the ID this machine was handed. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } /** Take a token out of what `token issue` printed. It is the one base64url blob on its own line. */ @@ -253,8 +253,8 @@ before(async () => { // because the control plane's image is named by the ID this machine holds it under, which is not // knowable until it has been handed over. held = raised.images; - await must("anchor", `cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must("anchor", `${HOST_PATH} apply /tmp/substrate.lock`); + await must("anchor", `cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must("anchor", `${HOST_PATH} apply /tmp/foundation.lock`); // A build machine, so anything here can ask the mesh to build something. Placed rather than // assumed: nothing else in this scenario would start one. @@ -289,7 +289,7 @@ after(async () => { test("a bare machine becomes a mesh", { skip, timeout: 600_000 }, async () => { const running = await must("anchor", `docker ps --format '{{.Names}}'`); - for (const container of ["mesh-store", "mesh-broker", "mesh-control"]) { + for (const container of ["mesh-store", "mesh-broker", "mesh-controller"]) { assert.match(running, new RegExp(container), `${container} is not running`); } // Answering, not merely up. A container that is running is not a control plane that replies — @@ -334,8 +334,8 @@ test("a credential reaches both ends and the mesh holds neither", { skip, timeou `"content":"PGHOST=$\{bound:postgres-database:at\}\\nPGPORT=$\{bound:postgres-database:port\}\\n` + `PGUSER=$\{bound:postgres-database:as\}\\nPGPASSWORD=$\{secret:postgres-database\}\\n"}]}' ` + `> /tmp/app.json`); - await must("anchor", `docker cp /tmp/pg.json mesh-control:/pg.json`); - await must("anchor", `docker cp /tmp/app.json mesh-control:/app.json`); + await must("anchor", `docker cp /tmp/pg.json mesh-controller:/pg.json`); + await must("anchor", `docker cp /tmp/app.json mesh-controller:/app.json`); await mesh("module add /pg.json"); await mesh("module add /app.json"); @@ -423,7 +423,7 @@ test("when a machine cannot do what it was told, the mesh says which and why", { // is the situation `status` exists to distinguish from a machine that refused everything. await must("anchor", `printf %s '{"module":"impossible","version":"1","resources":[` + `{"id":"nothing","type":"package","package":"a-package-that-does-not-exist"}]}' > /tmp/imp.json`); - await must("anchor", `docker cp /tmp/imp.json mesh-control:/imp.json`); + await must("anchor", `docker cp /tmp/imp.json mesh-controller:/imp.json`); await mesh("module add /imp.json"); await mesh("assign laptop impossible"); await mesh("push laptop"); @@ -471,7 +471,7 @@ test("a declaration waits for a machine that is switched off", { skip, timeout: await must("anchor", `printf %s '{"module":"while-away","version":"1","resources":[` + `{"id":"note","type":"file","path":"/etc/mesh-while-away","content":"waited"}]}' > /tmp/away.json`); - await must("anchor", `docker cp /tmp/away.json mesh-control:/away.json`); + await must("anchor", `docker cp /tmp/away.json mesh-controller:/away.json`); await mesh("module add /away.json"); await mesh("assign laptop while-away"); await mesh("push laptop"); @@ -512,13 +512,13 @@ test("a declaration waits for a machine that is switched off", { skip, timeout: test("unassigning takes away exactly what it should", { skip, timeout: 900_000 }, async () => { // Removal is the half nobody tests. The mesh takes away what IT declared and no longer declares, // and never what the machine raised for itself from its bundle — which is the fault that - // destroyed a substrate once (novox/hq 04-ISSUES/010). + // destroyed a foundation once (novox/hq 04-ISSUES/010). // // Two modules, so the test can tell "removed the right one" from "removed everything". for (const [name, path] of [["kept", "/etc/mesh-kept"], ["going", "/etc/mesh-going"]] as const) { await must("anchor", `printf %s '{"module":"${name}","version":"1","resources":[` + `{"id":"note","type":"file","path":"${path}","content":"${name}"}]}' > /tmp/${name}.json`); - await must("anchor", `docker cp /tmp/${name}.json mesh-control:/${name}.json`); + await must("anchor", `docker cp /tmp/${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); await mesh(`assign anchor ${name}`); } @@ -536,11 +536,11 @@ test("unassigning takes away exactly what it should", { skip, timeout: 900_000 } assert.ok((await on("anchor", `test -f /etc/mesh-kept`)).ok, "unassigning one module took another one's file with it"); - // And the substrate this machine raised from its own bundle is untouched. It was not declared by + // And the foundation this machine raised from its own bundle is untouched. It was not declared by // the mesh, so the mesh must never remove it — the machine would take its own control plane // away, which is exactly what happened before origins existed. const running = await must("anchor", `docker ps --format '{{.Names}}'`); - for (const container of ["mesh-store", "mesh-broker", "mesh-control"]) { + for (const container of ["mesh-store", "mesh-broker", "mesh-controller"]) { assert.match(running, new RegExp(container), `${container} was removed by a declaration that never declared it`); } @@ -565,7 +565,7 @@ test("a machine that fell behind catches up without being named", { skip, timeou await must("anchor", `printf %s '{"module":"fixable","version":"1","resources":[` + `{"id":"pkg","type":"package","package":"a-package-that-does-not-exist-yet"},` + `{"id":"note","type":"file","path":"/etc/mesh-fixable","content":"here"}]}' > /tmp/fix.json`); - await must("anchor", `docker cp /tmp/fix.json mesh-control:/fix.json`); + await must("anchor", `docker cp /tmp/fix.json mesh-controller:/fix.json`); await mesh("module add /fix.json"); await mesh("assign laptop fixable"); await mesh("push laptop"); @@ -585,7 +585,7 @@ test("a machine that fell behind catches up without being named", { skip, timeou // Fix the cause, the way somebody would: the module stops asking for the impossible thing. await must("anchor", `printf %s '{"module":"fixable","version":"1","resources":[` + `{"id":"note","type":"file","path":"/etc/mesh-fixable","content":"here"}]}' > /tmp/fix.json`); - await must("anchor", `docker cp /tmp/fix.json mesh-control:/fix.json`); + await must("anchor", `docker cp /tmp/fix.json mesh-controller:/fix.json`); await mesh("module add /fix.json"); // And nobody names the machine. @@ -626,7 +626,7 @@ test("the mesh runs its own artifact store", { skip, timeout: 900_000 }, async ( // and a builder will not start without an artifact store to publish to, so a mesh that has just // bootstrapped cannot build the module that gives it one. Adding the manifest directly is the // path a real first mesh has to take, so it is the path this walks. - await must("anchor", `docker cp /root/registry/module.json mesh-control:/registry.json`); + await must("anchor", `docker cp /root/registry/module.json mesh-controller:/registry.json`); await mesh("module add /registry.json"); await mesh("assign anchor registry"); await mesh("push anchor"); @@ -658,7 +658,7 @@ test("a machine serves its internal name with a certificate the mesh issued", { `"certificate":{"into":"/etc/mesh/serving.crt","authority":"/etc/mesh/authority.crt"},` + `"resources":[{"id":"dir","type":"directory","path":"/etc/mesh","mode":"0755"}]}' ` + `> /tmp/served.json`); - await must("anchor", `docker cp /tmp/served.json mesh-control:/served.json`); + await must("anchor", `docker cp /tmp/served.json mesh-controller:/served.json`); await mesh("module add /served.json"); await mesh("assign anchor served"); await mesh("push anchor"); @@ -768,7 +768,7 @@ test("a machine filters exactly what its modules declared, and nothing else", { `{"id":"filter","type":"service","unit":"mesh-filter.service","state":"running",` + `"boot":"enabled","restart-on":["filtering"]}]}' > /tmp/firewall.json`); for (const f of ["talker", "firewall"]) { - await must("anchor", `docker cp /tmp/${f}.json mesh-control:/${f}.json`); + await must("anchor", `docker cp /tmp/${f}.json mesh-controller:/${f}.json`); await mesh(`module add /${f}.json`); } await mesh("assign laptop talker"); @@ -972,7 +972,7 @@ test("rotating a credential moves both ends, and the old one stops working", { `"resources":[{"id":"dir","type":"directory","path":"/etc/realapp","mode":"0755"}]}' ` + `> /tmp/realapp.json`); for (const f of ["realstore", "realapp"]) { - await must("anchor", `docker cp /tmp/${f}.json mesh-control:/${f}.json`); + await must("anchor", `docker cp /tmp/${f}.json mesh-controller:/${f}.json`); await mesh(`module add /${f}.json`); } await mesh("assign anchor realstore"); @@ -1081,7 +1081,7 @@ test("a route is a grant: a workload is reached by the name it asked for", { `{"id":"app","type":"container","name":"storefront",` + `"image":"${ARTIFACT_STORE}","ports":["8088:5000"]}]}' > /tmp/storefront.json`); for (const f of ["frontdoor", "storefront"]) { - await must("anchor", `docker cp /tmp/${f}.json mesh-control:/${f}.json`); + await must("anchor", `docker cp /tmp/${f}.json mesh-controller:/${f}.json`); await mesh(`module add /${f}.json`); } await mesh("assign anchor frontdoor"); @@ -1135,7 +1135,7 @@ test("a route is a grant: a workload is reached by the name it asked for", { assert.ok(withdrawn, `the route outlived the module that asked for it:\n${after}\n\n` + `the machine did apply — this is what the mesh would send now:\n` + - `${(await on("anchor", `docker exec mesh-control /mesh-control plan anchor --files`)).out}`); + `${(await on("anchor", `docker exec mesh-controller /mesh-controller plan anchor --files`)).out}`); let gone = false; for (let i = 0; i < 15 && !gone; i++) { @@ -1159,7 +1159,7 @@ test("model access is answered by a record, and the key the mesh took is one it `"secrets":{"model-access":"/etc/assistant/key"},` + `"resources":[{"id":"dir","type":"directory","path":"/etc/assistant","mode":"0755"}]}' ` + `> /tmp/assistant.json`); - await must("anchor", `docker cp /tmp/assistant.json mesh-control:/assistant.json`); + await must("anchor", `docker cp /tmp/assistant.json mesh-controller:/assistant.json`); await mesh("module add /assistant.json"); // The licences first. With none recorded at all the honest answer is that nothing provides @@ -1171,7 +1171,7 @@ test("model access is answered by a record, and the key the mesh took is one it // then says the machine's set cannot be applied. The refusal names both candidates and the // command. ADR 0024 warns this will be felt — which is correct, and correct is not the same as // usable. - const refused = await on("anchor", `docker exec mesh-control /mesh-control assign laptop assistant`); + const refused = await on("anchor", `docker exec mesh-controller /mesh-controller assign laptop assistant`); assert.ok(!refused.ok, `a consumer was given model access without anybody saying which:\n${refused.out}`); for (const want of ["personal", "the-organisation", "licence use"]) { @@ -1182,7 +1182,7 @@ test("model access is answered by a record, and the key the mesh took is one it await mesh("licence use personal laptop assistant"); // Chosen, and still no key: the mesh has one thing to deliver and has not been given it. - const noKey = await on("anchor", `docker exec mesh-control /mesh-control plan laptop`); + const noKey = await on("anchor", `docker exec mesh-controller /mesh-controller plan laptop`); assert.ok(!noKey.ok, `a module was planned with a licence that has no key:\n${noKey.out}`); assert.match(noKey.out, /licence key personal/, noKey.out); @@ -1190,7 +1190,7 @@ test("model access is answered by a record, and the key the mesh took is one it // command line is a key in shell history and in every process listing taken while it ran. const secret = "sk-test-" + "0123456789abcdef".repeat(2); const accepted = await must("anchor", - `printf %s ${quote(secret)} | docker exec -i mesh-control /mesh-control licence key personal`); + `printf %s ${quote(secret)} | docker exec -i mesh-controller /mesh-controller licence key personal`); assert.match(accepted, /sealed to 1 holder/, accepted); assert.doesNotMatch(accepted, new RegExp(secret), "the key was echoed back, so the one copy that matters is on a terminal"); @@ -1298,11 +1298,11 @@ test("the board names the machine that is not doing what it was told", { await must("anchor", `printf %s '{"module":"board","version":"1",` + `"listens":[{"port":8090,"from":"mesh","why":"the board"}],` + `"resources":[]}' > /tmp/board.json`); - await must("anchor", `docker cp /tmp/board.json mesh-control:/board.json`); + await must("anchor", `docker cp /tmp/board.json mesh-controller:/board.json`); await mesh("module add /board.json"); // Served from the control plane's own container, reading the mesh on every request. - await must("anchor", `docker exec -d mesh-control /mesh-control board --listen 0.0.0.0:8090`); + await must("anchor", `docker exec -d mesh-controller /mesh-controller board --listen 0.0.0.0:8090`); await new Promise((r) => setTimeout(r, 3000)); const read = async (path: string) => @@ -1325,7 +1325,7 @@ test("the board names the machine that is not doing what it was told", { await must("anchor", `printf %s '{"module":"impossible","version":"1",` + `"resources":[{"id":"nowhere","type":"service","unit":"nothing-like-this.service",` + `"state":"running"}]}' > /tmp/impossible.json`); - await must("anchor", `docker cp /tmp/impossible.json mesh-control:/impossible.json`); + await must("anchor", `docker cp /tmp/impossible.json mesh-controller:/impossible.json`); await mesh("module add /impossible.json"); await mesh("assign laptop impossible"); await mesh("push laptop"); @@ -1389,7 +1389,7 @@ test("the hub can be filtered without severing the mesh", { `ExecStart=/usr/bin/nft -f ${rules}\\n[Install]\\nWantedBy=multi-user.target\\n"},` + `{"id":"filter","type":"service","unit":"hub-filter.service","state":"running",` + `"boot":"enabled","restart-on":["filtering"]}]}' > /tmp/hubfilter.json`); - await must("anchor", `docker cp /tmp/hubfilter.json mesh-control:/hubfilter.json`); + await must("anchor", `docker cp /tmp/hubfilter.json mesh-controller:/hubfilter.json`); await mesh("module add /hubfilter.json"); await mesh("assign anchor hubfilter"); await mesh("push anchor"); @@ -1414,7 +1414,7 @@ test("the hub can be filtered without severing the mesh", { await must("anchor", `printf %s '{"module":"stillworks","version":"1",` + `"resources":[{"id":"marker","type":"file","path":"/etc/mesh-still-works",` + `"content":"yes","mode":"0644"}]}' > /tmp/stillworks.json`); - await must("anchor", `docker cp /tmp/stillworks.json mesh-control:/stillworks.json`); + await must("anchor", `docker cp /tmp/stillworks.json mesh-controller:/stillworks.json`); await mesh("module add /stillworks.json"); await mesh("assign laptop stillworks"); await mesh("push laptop"); @@ -1450,7 +1450,7 @@ test("a container reaches another machine by the name the mesh gave it", { `"capabilities":["container-runtime"],` + `"resources":[{"id":"idle","type":"container","name":"resolves",` + `"image":"${ARTIFACT_STORE}"}]}' > /tmp/resolves.json`); - await must("anchor", `docker cp /tmp/resolves.json mesh-control:/resolves.json`); + await must("anchor", `docker cp /tmp/resolves.json mesh-controller:/resolves.json`); await mesh("module add /resolves.json"); await mesh("assign laptop resolves"); await mesh("push laptop"); @@ -1539,7 +1539,7 @@ test("every name under a machine resolves to that machine", { }); test("a service is reached by a name under the machine it runs on", { - skip: skip || (!moduleExamples ? "set MESH_LAB_MODULES to mesh-control's examples/modules" : false), + skip: skip || (!moduleExamples ? "set MESH_LAB_MODULES to mesh-controller's examples/modules" : false), timeout: 900_000, }, async () => { // postgres.novox.internal, plex.ace.internal — the first label is the service and the rest is @@ -1555,7 +1555,7 @@ test("a service is reached by a name under the machine it runs on", { for (const name of ["dnsmasq", "resolved-split-dns"]) { const manifest = readFileSync(`${moduleExamples}/${name}.json`, "utf8"); await must("anchor", `cat > /tmp/${name}.json <<'MANIFEST'\n${manifest}\nMANIFEST`); - await must("anchor", `docker cp /tmp/${name}.json mesh-control:/${name}.json`); + await must("anchor", `docker cp /tmp/${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); } @@ -1700,18 +1700,18 @@ test("a third-party workload is adopted, with the credential it already had", { }, ], }))} > /umami.json`); - await must("anchor", `docker cp /umami.json mesh-control:/umami.json`); + await must("anchor", `docker cp /umami.json mesh-controller:/umami.json`); await mesh("module add /umami.json"); // **Accepted, not generated.** The value is what the database already answers to; the mesh // seals it and cannot read it again. Given whole, as the environment lines the containers read. await must("anchor", `printf %s ${quote(`POSTGRES_PASSWORD=${password}`)} | ` + - `docker exec -i mesh-control /mesh-control secret accept anchor umami database --from -`); + `docker exec -i mesh-controller /mesh-controller secret accept anchor umami database --from -`); await must("anchor", `printf %s ${quote( `DATABASE_URL=postgresql://umami:${password}@umami-db:5432/umami`)} | ` + - `docker exec -i mesh-control /mesh-control secret accept anchor umami app --from -`); + `docker exec -i mesh-controller /mesh-controller secret accept anchor umami app --from -`); await mesh("assign anchor umami"); await mesh("push anchor", 300_000); @@ -1815,7 +1815,7 @@ test("the real modules resolve together, and compose a declaration a host accept } planned.push(name); await must("anchor", `printf %s ${quote(pinned)} > /${name}.json`); - await must("anchor", `docker cp /${name}.json mesh-control:/${name}.json`); + await must("anchor", `docker cp /${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); } @@ -1932,7 +1932,7 @@ test("the forge runs, on a database the mesh gave it", { skip, timeout: 900_000 assert.deepEqual(stillUnpinned(pinned), [], `${name} still names an image nothing serves, so it could not start`); await must("anchor", `printf %s ${quote(pinned)} > /run-${name}.json`); - await must("anchor", `docker cp /run-${name}.json mesh-control:/run-${name}.json`); + await must("anchor", `docker cp /run-${name}.json mesh-controller:/run-${name}.json`); await mesh(`module add /run-${name}.json`); await mesh(`assign anchor ${name}`); } @@ -2018,7 +2018,7 @@ test("a consumer's cache grant means exactly its own keys", { skip, timeout: 600 assert.deepEqual(stillUnpinned(pinned), [], "redis still names an image nothing serves, so it could not start"); await must("anchor", `printf %s ${quote(pinned)} > /run-redis.json`); - await must("anchor", `docker cp /run-redis.json mesh-control:/run-redis.json`); + await must("anchor", `docker cp /run-redis.json mesh-controller:/run-redis.json`); await mesh("module add /run-redis.json"); // A consumer with no container: what is under test is the credential's reach, and files on the @@ -2030,7 +2030,7 @@ test("a consumer's cache grant means exactly its own keys", { skip, timeout: 600 `"secrets":{"redis-cache":"/var/lib/cachetest/cache.secret"},` + `"resources":[{"id":"state","type":"directory","path":"/var/lib/cachetest","mode":"0700"}]}' ` + `> /cachetest.json`); - await must("anchor", `docker cp /cachetest.json mesh-control:/cachetest.json`); + await must("anchor", `docker cp /cachetest.json mesh-controller:/cachetest.json`); await mesh("module add /cachetest.json"); await mesh("assign anchor redis"); diff --git a/test/integration/minio-grant-end-to-end.test.ts b/test/integration/minio-grant-end-to-end.test.ts index 1fae1de..34fd53e 100644 --- a/test/integration/minio-grant-end-to-end.test.ts +++ b/test/integration/minio-grant-end-to-end.test.ts @@ -9,7 +9,7 @@ * the consumer reaching its bucket with the access key and secret the mesh delivered it. Nothing is * placed by the test. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh minio builds mesh-runtime-minio:development (with mc), which * scenarios/minio-node.yml stocks. minio/minio:latest must be in the local daemon. */ @@ -21,7 +21,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -36,7 +36,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "minio-node"; @@ -65,7 +65,7 @@ async function must(command: string, timeoutMs?: number): Promise { } async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -74,7 +74,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -93,7 +93,7 @@ async function settled(withinMs = 480_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -125,11 +125,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } await mesh(`node add ${MACHINE}`); @@ -203,12 +203,12 @@ test("the mesh grants a consumer an S3 bucket, and the credential it delivers re resources: [{ id: "state", type: "directory", path: "/var/lib/bucketuser", mode: "0700" }], }); - await must(`printf %s ${quote(minioManifest)} > /tmp/minio.json && docker cp /tmp/minio.json mesh-control:/minio.json`); + await must(`printf %s ${quote(minioManifest)} > /tmp/minio.json && docker cp /tmp/minio.json mesh-controller:/minio.json`); await mesh("module add /minio.json"); await mesh(`module issue minio --node ${MACHINE}`); await mesh(`assign ${MACHINE} minio`); - await must(`printf %s ${quote(consumerManifest)} > /tmp/bucketuser.json && docker cp /tmp/bucketuser.json mesh-control:/bucketuser.json`); + await must(`printf %s ${quote(consumerManifest)} > /tmp/bucketuser.json && docker cp /tmp/bucketuser.json mesh-controller:/bucketuser.json`); await mesh("module add /bucketuser.json"); await mesh(`assign ${MACHINE} bucketuser`); diff --git a/test/integration/objectstore.test.ts b/test/integration/objectstore.test.ts index 337025a..1b08379 100644 --- a/test/integration/objectstore.test.ts +++ b/test/integration/objectstore.test.ts @@ -33,7 +33,7 @@ const skip = !capability.usable ? `lab not usable: ${capability.why}` : !provisioner ? "set MESH_LAB_OBJECTSTORE_PROVISIONER to a built provisioner " + - "(mesh-control: go build ./examples/objectstore-provisioner)" + "(mesh-controller: go build ./examples/objectstore-provisioner)" : false; const SCENARIO = "an-object-store"; diff --git a/test/integration/one-node-mesh.test.ts b/test/integration/one-node-mesh.test.ts index 4f17c66..f381028 100644 --- a/test/integration/one-node-mesh.test.ts +++ b/test/integration/one-node-mesh.test.ts @@ -31,9 +31,9 @@ * MESH_LAB_INCUS='sudo -n incus' * MESH_LAB_HOST_BINARY=.../mesh-host/mesh-host * MESH_LAB_BOOTSTRAP_BINARY=.../mesh-host/mesh-bootstrap - * MESH_LAB_BUNDLE=.../mesh-host/examples/substrate-first-node.lock + * MESH_LAB_BUNDLE=.../mesh-host/examples/foundation-first-node.lock * MESH_LAB_CATALOG=.../mesh-catalog/modules - * MESH_LAB_SOURCE=/mesh-control.git MESH_LAB_SOURCE_REF= + * MESH_LAB_SOURCE=/mesh-controller.git MESH_LAB_SOURCE_REF= * MESH_LAB_KEEP=1 to leave it standing afterwards */ import { test, before, after } from "node:test"; @@ -45,7 +45,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec, push } from "../../src/lifecycle/operate.ts"; import { bootstrapBinaryPath, hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle } from "./harness.ts"; import { genesis, type GenesisResult } from "./genesis.ts"; import { incus } from "../../src/incus/client.ts"; import { instanceNameOf } from "../../src/lifecycle/operate.ts"; @@ -69,7 +69,7 @@ const REGISTRY = `${ANCHOR}:5000`; */ const BASE = { module: "mesh-tools", repo: "mesh-tools", path: "" }; /** - * What `amqp-ping` requires, and what the substrate does not supply. + * What `amqp-ping` requires, and what the foundation does not supply. * * The installer raises a broker, but as a bundle resource — plumbing, not a module the mesh has a * record of, so it provides nothing to anything. A module asking for `amqp` is asking for a @@ -98,7 +98,7 @@ const FILTER_MODULE = "nftables"; const STORE = { module: "postgres", repo: "mesh-catalog", path: "modules/postgres", container: "mesh-postgres" }; const CATALOGUE = { module: "mesh-catalog", repo: "mesh-catalog", path: "modules/mesh-catalog", container: "mesh-catalog" }; /** The control plane, rebuilt from its own repository — the step that ends the installer's tenure. */ -const CONTROL_PLANE = { module: "mesh-control", repo: "mesh-control", path: "", container: "mesh-control" }; +const CONTROL_PLANE = { module: "mesh-controller", repo: "mesh-controller", path: "", container: "mesh-controller" }; /** * What this mesh must hold when it is finished, and what must be RUNNING on the machine. @@ -108,14 +108,14 @@ const CONTROL_PLANE = { module: "mesh-control", repo: "mesh-control", path: "", * the registry and the builder — are here too: they are carried in, and a mesh missing any of them * is not one. */ -const MUST_HOLD = ["mesh-control", "distribution", "builder", "mesh-tools", "postgres", +const MUST_HOLD = ["mesh-controller", "distribution", "builder", "mesh-tools", "postgres", "mesh-catalog", "lavinmq", "amqp-ping"]; -const MUST_RUN = ["mesh-control", "mesh-registry", "mesh-broker", "mesh-store", +const MUST_RUN = ["mesh-controller", "mesh-registry", "mesh-broker", "mesh-store", "mesh-postgres", "mesh-catalog", "mesh-lavinmq", "amqp-ping"]; /** Named once, because the step title is also how later steps say what they waited on. */ const NEEDS = "the mesh runs a broker for that module to talk to"; const GENESIS = "a bare machine becomes a mesh of one, raised by the installer"; -const SUBSTRATE = "the substrate is up — a store and a broker of the mesh's own"; +const FOUNDATION = "the foundation is up — a store and a broker of the mesh's own"; const BUILT_CP = "the control plane is one this mesh built, not one it was handed"; const PIVOTED = "the pivot finished — what raised the mesh is gone"; const HAS_REGISTRY = "the registry serves this mesh its own images"; @@ -191,7 +191,7 @@ const skip = !installer ? "MESH_LAB_BOOTSTRAP_BINARY is not set to a built mesh-bootstrap" : !source ? "MESH_LAB_SOURCE is not set to the repository the control plane is built from" : !sourceRef ? "MESH_LAB_SOURCE_REF is not set to the commit to build" : - !bundle || !existsSync(bundle) ? "MESH_LAB_BUNDLE is not set to a substrate template" : + !bundle || !existsSync(bundle) ? "MESH_LAB_BUNDLE is not set to a foundation template" : !catalogDir || !existsSync(catalogDir) ? "MESH_LAB_CATALOG is not set to mesh-catalog/modules" : false; @@ -220,19 +220,19 @@ async function mesh(command: string, timeoutMs?: number): Promise { // The control plane is a live, mesh-MANAGED container: the mesh recreates it whenever its // declaration changes — most visibly when the machine first gets its `.internal` name on the // private network, which becomes the container's `--add-host` (containers are immutable, so a new - // spec is a new container). A `docker exec mesh-control` that lands in that brief recreate window + // spec is a new container). A `docker exec mesh-controller` that lands in that brief recreate window // fails with "container ... is not running". That is not the mesh being wrong — it is a command // racing a legitimate restart — so it is retried until the control plane answers again. A real // command failure (anything else) still throws at once. const deadline = Date.now() + (timeoutMs ?? 120_000); for (;;) { - const { out, ok } = await on(CONTROL, `docker exec mesh-control /mesh-control ${command}`, timeoutMs); + const { out, ok } = await on(CONTROL, `docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); if (ok) return out; if (/is not running|No such container/i.test(out) && Date.now() < deadline) { await new Promise((r) => setTimeout(r, 2_000)); continue; } - throw new Error(`${CONTROL}: docker exec mesh-control /mesh-control ${command}\n${out}`); + throw new Error(`${CONTROL}: docker exec mesh-controller /mesh-controller ${command}\n${out}`); } } @@ -304,7 +304,7 @@ async function waitForContainer(node: string, container: string, seconds = 200): * * **The control plane runs in a container, so a file on the machine is not a file it can open.** * Pushing the manifest to the machine and naming that path got `no such file or directory` from - * inside mesh-control, which is correct and was briefly mistaken for a missing manifest. It is + * inside mesh-controller, which is correct and was briefly mistaken for a missing manifest. It is * copied the last step of the way with `docker cp`. * * **Into the root, not into /tmp.** The control plane's image is a minimal one and has no `/tmp` @@ -315,7 +315,7 @@ async function registerModule(module: string, manifest: string): Promise const onMachine = `/tmp/${module}.json`; const inContainer = `/${module}.json`; await push(instanceId, CONTROL, manifest, onMachine); - await must(CONTROL, `docker cp ${onMachine} mesh-control:${inContainer}`); + await must(CONTROL, `docker cp ${onMachine} mesh-controller:${inContainer}`); return mesh(`module add ${inContainer}`); } function tokenFrom(said: string): string { @@ -393,7 +393,7 @@ function report(name: string): string { */ const PLAN: { code: string; title: string }[] = [ { code: "R1", title: GENESIS }, - { code: "R2", title: SUBSTRATE }, + { code: "R2", title: FOUNDATION }, { code: "R3", title: BUILT_CP }, { code: "R4", title: PIVOTED }, { code: "R5", title: HAS_REGISTRY }, @@ -479,7 +479,7 @@ before(async () => { // whose anchor is somewhere else every node, including this one, would enrol against an // address nothing answers on. The installer refuses to guess it and says so, which is // right: it does not know what this machine is called from outside. - bundleTemplate: substrateBundle(bundle, []).replaceAll("192.0.2.10:5671", `${ANCHOR}:5671`), + bundleTemplate: foundationBundle(bundle, []).replaceAll("192.0.2.10:5671", `${ANCHOR}:5671`), registry: REGISTRY, source, sourceRef, @@ -514,7 +514,7 @@ before(async () => { // saying it published an image and the registry serving one are different facts, and it is the // second that matters. - await step("R2", SUBSTRATE, GENESIS, async () => { + await step("R2", FOUNDATION, GENESIS, async () => { await waitForContainer(CONTROL, "mesh-store", 120); await waitForContainer(CONTROL, "mesh-broker", 120); return (await on(CONTROL, `docker ps --format '{{.Names}}\t{{.Status}}'`)).out; @@ -525,7 +525,7 @@ before(async () => { // the image has to be one this mesh's own registry serves. await step("R3", BUILT_CP, GENESIS, async () => { const image = (await on(CONTROL, - `docker inspect -f '{{.Config.Image}}' mesh-control 2>&1`)).out.trim(); + `docker inspect -f '{{.Config.Image}}' mesh-controller 2>&1`)).out.trim(); assert.match(image, /@sha256:[0-9a-f]{64}/, `the control plane names its image by tag, not by digest: ${image}`); assert.ok(image.includes(":5000/"), @@ -535,17 +535,17 @@ before(async () => { await step("R4", PIVOTED, BUILT_CP, async () => { const ps = (await on(CONTROL, `docker ps -a --format '{{.Names}}'`)).out; - assert.doesNotMatch(ps, /^temp-mesh-control$/m, + assert.doesNotMatch(ps, /^temp-mesh-controller$/m, `the temporary control plane is still here, so the pivot did not finish:\n${ps}`); return ps; }); - await step("R5", HAS_REGISTRY, SUBSTRATE, async () => { + await step("R5", HAS_REGISTRY, FOUNDATION, async () => { await waitForContainer(CONTROL, "mesh-registry", 120); const held = (await on(CONTROL, `curl -sS --max-time 15 http://127.0.0.1:5000/v2/_catalog`)).out; - assert.match(held, /mesh-control/, - `the registry serves no mesh-control, so nothing was published into it:\n${held}`); + assert.match(held, /mesh-controller/, + `the registry serves no mesh-controller, so nothing was published into it:\n${held}`); return held.trim(); }); @@ -597,7 +597,7 @@ before(async () => { return built; }); - // A store of its own. **Not the substrate's.** The installer raises a store for the control + // A store of its own. **Not the foundation's.** The installer raises a store for the control // plane to keep its own records in, the way it raises a broker — plumbing, not a module the mesh // has any record of, so it provides nothing to anything. A module that wants a database wants a // provider in the graph, and the catalogue below is the first thing to want one. @@ -790,7 +790,7 @@ before(async () => { // broker: a module's account is scoped to what it declares it emits and consumes, and calling // a tool needs a temporary reply queue that scope does not cover. So a module can SERVE tools // and cannot CALL them, and nothing issues an account to anyone who wants to ask (novox/hq - // issue 049). Until that is decided the caller is the substrate's bootstrap admin over the + // issue 049). Until that is decided the caller is the foundation's bootstrap admin over the // broker's loopback, reached by joining its network namespace. const image = (await on(CONTROL, `docker inspect -f '{{.Config.Image}}' mesh-catalog`)).out.trim(); @@ -1061,7 +1061,7 @@ after(async () => { for (const name of [ GENESIS, - SUBSTRATE, + FOUNDATION, BUILT_CP, PIVOTED, HAS_REGISTRY, diff --git a/test/integration/openai-bed.test.ts b/test/integration/openai-bed.test.ts index f15d57d..f80f327 100644 --- a/test/integration/openai-bed.test.ts +++ b/test/integration/openai-bed.test.ts @@ -12,7 +12,7 @@ * ordinary sealed-delivery path — with NO manager, NO refresh, NO access/refresh split, NO usage. The * whole of OpenAI's integration in the control plane is one registry line (vendor -> static-key). * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * Build the consumer runtime image into the local daemon first: * scripts/build-module-runtime.sh openai-consumer /tmp/openai-consumer.tar */ @@ -24,7 +24,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -36,7 +36,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "openai-bed"; @@ -69,11 +69,11 @@ async function must(command: string, timeoutMs?: number): Promise { } async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } async function meshTry(command: string): Promise<{ out: string; ok: boolean }> { - return on(`docker exec mesh-control /mesh-control ${command}`); + return on(`docker exec mesh-controller /mesh-controller ${command}`); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -82,7 +82,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -135,11 +135,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } await mesh(`node add ${MACHINE}`); @@ -159,7 +159,7 @@ test("a static-key model-access licence delivers the operator's API key to the c const consumerImage = pinned("mesh-runtime-openai-consumer"); // --- the licence, a record with vendor openai (static-key) ------------------------------------- - // No manager: a static-key licence has none (mesh-control refuses `licence manager` on it). The + // No manager: a static-key licence has none (mesh-controller refuses `licence manager` on it). The // consumer is put on the licence BEFORE the key is set — the static-key adapter's Accept seals to the // CURRENT holders, so a holder must exist first, or the key would seal to nobody. await mesh(`licence add openai personal --serves '{"model":"gpt-model"}'`); @@ -167,8 +167,8 @@ test("a static-key model-access licence delivers the operator's API key to the c // The operator sets the static key. `licence key` reads it from a file (never a CLI arg) and seals it // to the holder; the plaintext is discarded by the control plane. Staged 0644 so distroless - // mesh-control can read the file (docker cp preserves the mode). - await must(`printf %s ${quote(API_KEY)} > /tmp/openai-key && chmod 0644 /tmp/openai-key && docker cp /tmp/openai-key mesh-control:/openai-key`); + // mesh-controller can read the file (docker cp preserves the mode). + await must(`printf %s ${quote(API_KEY)} > /tmp/openai-key && chmod 0644 /tmp/openai-key && docker cp /tmp/openai-key mesh-controller:/openai-key`); await mesh(`licence key personal --file /openai-key`); // --- deploy the consumer ----------------------------------------------------------------------- @@ -199,7 +199,7 @@ test("a static-key model-access licence delivers the operator's API key to the c }, ], }); - await must(`printf %s ${quote(consumerManifest)} > /tmp/openai-consumer.json && docker cp /tmp/openai-consumer.json mesh-control:/openai-consumer.json`); + await must(`printf %s ${quote(consumerManifest)} > /tmp/openai-consumer.json && docker cp /tmp/openai-consumer.json mesh-controller:/openai-consumer.json`); await mesh(`module add /openai-consumer.json`); await mesh(`module issue openai-consumer --node ${MACHINE}`); await mesh(`assign ${MACHINE} openai-consumer`); diff --git a/test/integration/postgres-grant-end-to-end.test.ts b/test/integration/postgres-grant-end-to-end.test.ts index 3ee0521..1859f98 100644 --- a/test/integration/postgres-grant-end-to-end.test.ts +++ b/test/integration/postgres-grant-end-to-end.test.ts @@ -9,7 +9,7 @@ * the mesh minted. The proof is the consumer connecting to its database with the credential the mesh * delivered it. Nothing is placed by the test. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh postgres builds mesh-runtime-postgres:development (with psql), * which scenarios/postgres-node.yml stocks. */ @@ -21,7 +21,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -33,7 +33,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "postgres-node"; @@ -62,7 +62,7 @@ async function must(command: string, timeoutMs?: number): Promise { } async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -71,7 +71,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -84,7 +84,7 @@ async function settled(withinMs = 480_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -116,11 +116,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } await mesh(`node add ${MACHINE}`); @@ -157,7 +157,7 @@ test("the mesh grants a consumer a postgres database, and the credential it deli { id: "data", type: "directory", path: "/services/postgres/db-data", mode: "0700" }, { id: "net", type: "network", name: "postgres" }, { - // No published port here: the substrate's own store already holds host :5432 on this + // No published port here: the foundation's own store already holds host :5432 on this // single-node bed, and the consumer reaches postgres over the private network by name. The // committed manifest publishes it for cross-node consumers, which is a different node. id: "server", type: "container", name: "postgres", image: pinned("postgres"), network: "postgres", @@ -194,12 +194,12 @@ test("the mesh grants a consumer a postgres database, and the credential it deli resources: [{ id: "state", type: "directory", path: "/var/lib/dbuser", mode: "0700" }], }); - await must(`printf %s ${quote(postgresManifest)} > /tmp/postgres.json && docker cp /tmp/postgres.json mesh-control:/postgres.json`); + await must(`printf %s ${quote(postgresManifest)} > /tmp/postgres.json && docker cp /tmp/postgres.json mesh-controller:/postgres.json`); await mesh("module add /postgres.json"); await mesh(`module issue postgres --node ${MACHINE}`); await mesh(`assign ${MACHINE} postgres`); - await must(`printf %s ${quote(consumerManifest)} > /tmp/dbuser.json && docker cp /tmp/dbuser.json mesh-control:/dbuser.json`); + await must(`printf %s ${quote(consumerManifest)} > /tmp/dbuser.json && docker cp /tmp/dbuser.json mesh-controller:/dbuser.json`); await mesh("module add /dbuser.json"); await mesh(`assign ${MACHINE} dbuser`); diff --git a/test/integration/provider-on-backend-network.test.ts b/test/integration/provider-on-backend-network.test.ts index 4b3f1b8..18b1894 100644 --- a/test/integration/provider-on-backend-network.test.ts +++ b/test/integration/provider-on-backend-network.test.ts @@ -20,7 +20,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -32,7 +32,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "redis-node"; @@ -61,7 +61,7 @@ async function must(command: string, timeoutMs?: number): Promise { } async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -70,7 +70,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -83,7 +83,7 @@ async function settled(withinMs = 480_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -115,11 +115,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } await mesh(`node add ${MACHINE}`); @@ -184,7 +184,7 @@ test("redis's runtime, on the backend's private network, binds the broker and pr }, ], }); - await must(`printf %s ${quote(manifest)} > /tmp/redis.json && docker cp /tmp/redis.json mesh-control:/redis.json`); + await must(`printf %s ${quote(manifest)} > /tmp/redis.json && docker cp /tmp/redis.json mesh-controller:/redis.json`); await mesh("module add /redis.json"); await mesh(`module issue redis --node ${MACHINE}`); await mesh(`assign ${MACHINE} redis`); diff --git a/test/integration/provider-uses-mesh-credential.test.ts b/test/integration/provider-uses-mesh-credential.test.ts index 1fca4ba..1e72897 100644 --- a/test/integration/provider-uses-mesh-credential.test.ts +++ b/test/integration/provider-uses-mesh-credential.test.ts @@ -9,10 +9,10 @@ * password gets PONG — where a provisioner that invented its own password would answer WRONGPASS. * * A hand-written contributions file and secret stand in for the control plane here (a full grant - * from a second module is a heavier bed); their SHAPE is exactly what mesh-control writes — a + * from a second module is a heavier bed); their SHAPE is exactly what mesh-controller writes — a * `receives` doc with `as`/`secret`, and the secret file the host leaves after unsealing. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh redis builds mesh-runtime-redis:development, which * scenarios/redis-node.yml stocks. */ @@ -24,7 +24,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -36,7 +36,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "redis-node"; @@ -65,7 +65,7 @@ async function must(command: string, timeoutMs?: number): Promise { } async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -74,7 +74,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -87,7 +87,7 @@ async function settled(withinMs = 480_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -119,11 +119,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } await mesh(`node add ${MACHINE}`); @@ -184,7 +184,7 @@ test("redis creates a consumer's login with the password the mesh minted, sealin }, ], }); - await must(`printf %s ${quote(manifest)} > /tmp/redis.json && docker cp /tmp/redis.json mesh-control:/redis.json`); + await must(`printf %s ${quote(manifest)} > /tmp/redis.json && docker cp /tmp/redis.json mesh-controller:/redis.json`); await mesh("module add /redis.json"); await mesh(`module issue redis --node ${MACHINE}`); await mesh(`assign ${MACHINE} redis`); diff --git a/test/integration/provisioner.test.ts b/test/integration/provisioner.test.ts index 853d14b..f4a43ef 100644 --- a/test/integration/provisioner.test.ts +++ b/test/integration/provisioner.test.ts @@ -25,7 +25,7 @@ const provisioner = process.env["MESH_LAB_PROVISIONER"] ?? ""; const skip = !capability.usable ? `lab not usable: ${capability.why}` : !provisioner - ? "set MESH_LAB_PROVISIONER to a built provisioner (mesh-control: go build ./examples/postgres-provisioner)" + ? "set MESH_LAB_PROVISIONER to a built provisioner (mesh-controller: go build ./examples/postgres-provisioner)" : false; const SCENARIO = "a-provider"; diff --git a/test/integration/route-forwarding.test.ts b/test/integration/route-forwarding.test.ts index bb8fe23..9ded2b1 100644 --- a/test/integration/route-forwarding.test.ts +++ b/test/integration/route-forwarding.test.ts @@ -25,7 +25,7 @@ * publicly-trusted certificate and answering an HTTP-01 challenge at the name — is proven separately * by certificates.test.ts, which drives the same proxy binary against a real ACME server (Pebble). * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-route-proxy-image.sh builds mesh-route-proxy:development into the local daemon; * scenarios/route-forwarding.yml stocks it and alpine:latest, and serves both by digest. */ @@ -37,7 +37,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -49,7 +49,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "route-forwarding"; @@ -81,7 +81,7 @@ async function must(command: string, timeoutMs?: number): Promise { /** The control plane, a container on the node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The reference a manifest should carry, once this scenario has been raised. */ @@ -90,9 +90,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -105,7 +105,7 @@ async function settled(withinMs = 600_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -143,12 +143,12 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - // Raise the substrate — store, broker, control — from the bundle. - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + // Raise the foundation — store, broker, control — from the bundle. + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } // The node joins its own mesh, so it is a node the mesh can assign to, and the host runs so it @@ -216,13 +216,13 @@ test("the mesh routes a public name through the proxy to the consumer, and withd ], }); - await must(`printf %s ${quote(proxyManifest)} > /tmp/route-proxy.json && docker cp /tmp/route-proxy.json mesh-control:/route-proxy.json`); + await must(`printf %s ${quote(proxyManifest)} > /tmp/route-proxy.json && docker cp /tmp/route-proxy.json mesh-controller:/route-proxy.json`); await mesh("module add /route-proxy.json"); // No `module issue`: route-proxy has no broker account and no own-secret to mint. `assign` resolves // its plan and the provider is matchable by a consumer's route from that alone. await mesh(`assign ${MACHINE} route-proxy`); - await must(`printf %s ${quote(webManifest)} > /tmp/hello-web.json && docker cp /tmp/hello-web.json mesh-control:/hello-web.json`); + await must(`printf %s ${quote(webManifest)} > /tmp/hello-web.json && docker cp /tmp/hello-web.json mesh-controller:/hello-web.json`); await mesh("module add /hello-web.json"); await mesh(`assign ${MACHINE} hello-web`); diff --git a/test/integration/runtime-restart-on-config.test.ts b/test/integration/runtime-restart-on-config.test.ts index 6eaf500..5dc1bcb 100644 --- a/test/integration/runtime-restart-on-config.test.ts +++ b/test/integration/runtime-restart-on-config.test.ts @@ -12,7 +12,7 @@ * the container was replaced (a new container id) and the config on disk carries the new value. * It builds the host from source (no --no-build), because the behaviour under test is the host's. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh grafana builds mesh-runtime-grafana:development, which * scenarios/grafana-node.yml stocks. */ @@ -24,7 +24,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -36,7 +36,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "grafana-node"; @@ -65,7 +65,7 @@ async function must(command: string, timeoutMs?: number): Promise { } async function mesh(command: string, timeoutMs?: number): Promise { - return must(`docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(`docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -74,7 +74,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function tokenFrom(said: string): string { @@ -87,7 +87,7 @@ async function settled(withinMs = 480_000): Promise { const until = Date.now() + withinMs; let last = ""; while (Date.now() < until) { - const asked = await on(`docker exec mesh-control /mesh-control status --json`); + const asked = await on(`docker exec mesh-controller /mesh-controller status --json`); if (asked.ok) { try { const state = JSON.parse(asked.out) as { @@ -112,7 +112,7 @@ async function settled(withinMs = 480_000): Promise { async function setToken(token: string): Promise { const settings = JSON.stringify({ url: "http://127.0.0.1:3000", token }); - await must(`printf %s ${quote(settings)} > /tmp/s.json && docker cp /tmp/s.json mesh-control:/s.json`); + await must(`printf %s ${quote(settings)} > /tmp/s.json && docker cp /tmp/s.json mesh-controller:/s.json`); await mesh(`settings set grafana /s.json --node ${MACHINE}`); } @@ -129,11 +129,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must(`cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must(`${HOST_PATH} apply /tmp/substrate.lock`, 600_000); + await must(`cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must(`${HOST_PATH} apply /tmp/foundation.lock`, 600_000); const up = await must(`docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } await mesh(`node add ${MACHINE}`); @@ -170,7 +170,7 @@ test("a running runtime is recreated when its settings change, and reads the new }, ], }); - await must(`printf %s ${quote(manifest)} > /tmp/grafana.json && docker cp /tmp/grafana.json mesh-control:/grafana.json`); + await must(`printf %s ${quote(manifest)} > /tmp/grafana.json && docker cp /tmp/grafana.json mesh-controller:/grafana.json`); await mesh("module add /grafana.json"); await setToken("token-alpha"); diff --git a/test/integration/whole-mesh-ace.test.ts b/test/integration/whole-mesh-ace.test.ts index 39606aa..44cca2d 100644 --- a/test/integration/whole-mesh-ace.test.ts +++ b/test/integration/whole-mesh-ace.test.ts @@ -1,9 +1,9 @@ /** * The whole `ace` server's converted service set, installed together on ONE node behind the - * substrate — the media / home-automation half of the whole-mesh rehearsal (novox/hq). Sibling of + * foundation — the media / home-automation half of the whole-mesh rehearsal (novox/hq). Sibling of * whole-mesh-novox.test.ts; same harness and topology, a larger, media-heavy module set. * - * Substrate (store, broker, control) rides `anchor` and nothing else; ALL of ace's services ride the + * Foundation (store, broker, control) rides `anchor` and nothing else; ALL of ace's services ride the * `ace` node. An overlay is placed so the two DB consumers (baserow, letta) reach the postgres/redis * providers co-located with them. The media stack shares the operator-owned library directories * under /services/media (ADR 0051 `accesses`): the mesh writes an `access` resource that CONFIRMS @@ -20,7 +20,7 @@ * references of OURS are rewritten to the IDs the machine holds, and the co-located * host-port collisions are remapped at load time (see REMAP). * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock */ import { test, before, after } from "node:test"; @@ -31,7 +31,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -44,7 +44,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "whole-mesh-ace"; @@ -171,7 +171,7 @@ async function must(machine: string, command: string, timeoutMs?: number): Promi } async function mesh(command: string, timeoutMs?: number): Promise { - return must("anchor", `docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must("anchor", `docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -180,7 +180,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function loadManifest(name: string): { manifest: string; broker: boolean } { @@ -214,7 +214,7 @@ interface NodeState { } async function nodeState(node: string): Promise { - const asked = await on("anchor", `docker exec mesh-control /mesh-control status --json`); + const asked = await on("anchor", `docker exec mesh-controller /mesh-controller status --json`); if (!asked.ok) return { reached: false, applied: false, current: false, waiting: false, raw: asked.out }; let state: { wrong: { node: string; outcome: string; refused?: string; failed?: { id: string; error: string }[] }[]; @@ -248,11 +248,11 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - await must("anchor", `cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must("anchor", `${HOST_PATH} apply /tmp/substrate.lock`, 900_000); + await must("anchor", `cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must("anchor", `${HOST_PATH} apply /tmp/foundation.lock`, 900_000); const up = await must("anchor", `docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } for (const machine of ["anchor", NODE]) { @@ -294,7 +294,7 @@ test("the whole ace service set resolves, installs and converges on one node in if (DROPPED.some((d) => d.name === name)) continue; try { const { manifest, broker } = loadManifest(name); - await must("anchor", `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-control:/${name}.json`); + await must("anchor", `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); if (broker) { await mesh(`module issue ${name} --node ${NODE}`); diff --git a/test/integration/whole-mesh-full.test.ts b/test/integration/whole-mesh-full.test.ts index 7198827..82f4009 100644 --- a/test/integration/whole-mesh-full.test.ts +++ b/test/integration/whole-mesh-full.test.ts @@ -5,12 +5,12 @@ * * hosting (public) home (private, behind a NAT access point) * novox 192.0.2.20 — the ANCHOR: ace 10.99.1.10 the home server, media/IoT set - * substrate (store/broker/ shanks 10.99.1.20 workstation (light: portainer only) + * foundation (store/broker/ shanks 10.99.1.20 workstation (light: portainer only) * control) + the whole novox g14 10.99.1.30 workstation (light: portainer only) * set + overlay hub + ingress * - * There is NO separate anchor: novox IS the anchor. The substrate runs on novox, and novox also - * enrols as a node and receives its own service set — the substrate host and a service node at once. + * There is NO separate anchor: novox IS the anchor. The foundation runs on novox, and novox also + * enrols as a node and receives its own service set — the foundation host and a service node at once. * * TWO ACTS, AND THE BED NOW DISTINGUISHES THEM (novox/hq ADR 0067). * @@ -22,9 +22,9 @@ * is a WORKING MESH OF ONE, and this bed asserts exactly that before going any further. * * JOINING — ace, shanks and g14 then join a mesh that already exists: host binary, token, - * `enrol`, run the agent. No bootstrap, no substrate, no registry. novox is NOT enrolled again. + * `enrol`, run the agent. No bootstrap, no foundation, no registry. novox is NOT enrolled again. * - * The bed used to do neither. It applied the substrate bundle itself and looped enrolment over all + * The bed used to do neither. It applied the foundation bundle itself and looped enrolment over all * four machines as one continuous operation — which got the order right by accident and modelled * the wrong shape, and is why ADR 0067's own acceptance check ("the bed bootstraps through the * installer rather than around it") went unmet. Genesis GATES joining: if it stops, the bed says @@ -39,10 +39,10 @@ * BEFORE any heavy module lands, so the cross-segment-overlay verdict survives whatever the module * convergence then does. Phase B converges the full node sets and reports per node. * - * SUBSTRATE-ON-NOVOX PORT COLLISIONS (a real consequence of collapsing the anchor onto novox that the - * separate-anchor beds never hit): the substrate store binds 127.0.0.1:5432 and novox's postgres - * provider publishes 5432; the substrate broker binds 5671 + 127.0.0.1:5672 and novox's lavinmq - * provider publishes 5672. The two provider host publishes are REMAPPED off the substrate's ports + * FOUNDATION-ON-NOVOX PORT COLLISIONS (a real consequence of collapsing the anchor onto novox that the + * separate-anchor beds never hit): the foundation store binds 127.0.0.1:5432 and novox's postgres + * provider publishes 5432; the foundation broker binds 5671 + 127.0.0.1:5672 and novox's lavinmq + * provider publishes 5672. The two provider host publishes are REMAPPED off the foundation's ports * (REMAP below); consumers reach the providers over the mesh network on the container port, so the * host side is free to move. Reported as a topology finding. * @@ -50,7 +50,7 @@ * (whole-mesh-full-live) and NOT torn down — it is left standing and browsable. Without it the bed * behaves like every other: raise in before(), destroy in after(). * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * MESH_LAB_BOOTSTRAP_BINARY=.../mesh-bootstrap MESH_LAB_CATALOG=.../mesh-catalog/modules */ @@ -65,7 +65,7 @@ import { destroy, exec, instanceNameOf, push } from "../../src/lifecycle/operate import { bootstrapBinaryPath, hostBinaryPath, placeBootstrap, BOOTSTRAP_PATH, HOST_PATH, } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import { referenceFor, type HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -84,7 +84,7 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : !installer || !existsSync(installer) ? "MESH_LAB_BOOTSTRAP_BINARY is not set to a built mesh-bootstrap (mesh-host `make " + "bootstrap IMAGE=mesh-builder:development`). The anchor is raised BY the installer now, " + @@ -96,7 +96,7 @@ const skip = !capability.usable : false; const SCENARIO = "whole-mesh-full"; -/** novox hosts the substrate and the control plane; it is where `mesh` commands run. */ +/** novox hosts the foundation and the control plane; it is where `mesh` commands run. */ const CONTROL = "novox"; /** Every node in the mesh. novox is on hosting; the rest are behind the home gateway. */ const NODES = ["novox", "ace", "shanks", "g14"]; @@ -118,7 +118,7 @@ const CATALOGUE_ON_MACHINE = "/opt/mesh-catalog"; * `internal/bootstrap` RegistryModule and ControlPlaneModule. If it ever opens a third, this list * is where the bed finds out, by the installer saying which manifest it could not read. */ -const CATALOGUE_MODULES = ["registry", "mesh-control", "builder"]; +const CATALOGUE_MODULES = ["registry", "mesh-controller", "builder"]; /** * Where this mesh keeps its own images, as the anchor reaches it. @@ -221,7 +221,7 @@ const CORE_NOVOX = new Set([ const GAPS_NOVOX = new Set([ "umami", "mailu", "firewall", "fail2ban", "only-office", "de-spiegel", "amqp-email-forwarder", // step-ca is reported, not gated: the internal-CA ISSUANCE path is still being fixed in - // mesh-control, and this bed is not the place to discover that a fix has not landed yet. What is + // mesh-controller, and this bed is not the place to discover that a fix has not landed yet. What is // gated is the half that is decided and cheap — see the ADR 0066 section at the end. "step-ca", ]); @@ -274,13 +274,13 @@ const PLAN: { node: string; mods: Mod[]; core: Set; gaps: Set }[ /** * Host-port remaps (per module; host ports are per-VM so novox's and ace's never clash across nodes). - * The two SUBSTRATE collisions are the new ones: postgres 5432 and lavinmq 5672 are moved off the - * substrate store/broker's host ports, which only exist on novox because that is where the substrate + * The two FOUNDATION collisions are the new ones: postgres 5432 and lavinmq 5672 are moved off the + * foundation store/broker's host ports, which only exist on novox because that is where the foundation * runs. The rest break the novox web/app host-port collisions (route-proxy fronts 80/443). */ const REMAP: Record> = { // Moved, NOT bound to loopback. These two carried `127.0.0.1:` and it broke a consumer on a node - // with no substrate at all: a module is told to reach its provider at `.internal`, that + // with no foundation at all: a module is told to reach its provider at `.internal`, that // name resolves to the node's overlay address, and a provider listening only on loopback refuses // it. letta on ace died of exactly this — "is the server running on that host and accepting // TCP/IP connections?" — while postgres sat healthy beside it. @@ -344,7 +344,7 @@ async function must(machine: string, command: string, timeoutMs?: number): Promi /** The control plane, a container on novox (the anchor). */ async function mesh(command: string, timeoutMs?: number): Promise { - return must(CONTROL, `docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must(CONTROL, `docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** What a manifest's image reference becomes on the machine — ours by ID, everything else as written. */ @@ -353,7 +353,7 @@ function pinned(reference: string): string { } function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } function loadManifest(name: string): { manifest: string; broker: boolean } { @@ -405,7 +405,7 @@ interface NodeState { } async function nodeState(node: string): Promise { - const asked = await on(CONTROL, `docker exec mesh-control /mesh-control status --json`); + const asked = await on(CONTROL, `docker exec mesh-controller /mesh-controller status --json`); if (!asked.ok) return { reached: false, applied: false, current: false, waiting: false, raw: asked.out }; let state: { wrong: { node: string; outcome: string; refused?: string; failed?: { id: string; error: string }[] }[]; @@ -467,9 +467,9 @@ async function deliverCaRoot(): Promise { // Safe here and nowhere else: these three exist for the seconds between being written and // being sealed to the machine, on a lab node, for a CA thrown away with the scenario. "chmod 0644 /tmp/ca/root.crt /tmp/ca/root.key /tmp/ca/key-password", - "docker cp /tmp/ca/root.crt mesh-control:/ca-root-cert", - "docker cp /tmp/ca/root.key mesh-control:/ca-root-key", - "docker cp /tmp/ca/key-password mesh-control:/ca-root-key-password", + "docker cp /tmp/ca/root.crt mesh-controller:/ca-root-cert", + "docker cp /tmp/ca/root.key mesh-controller:/ca-root-key", + "docker cp /tmp/ca/key-password mesh-controller:/ca-root-key-password", ].join("\n"), 180_000); if (!made.ok) { console.log(`CA ROOT NOT MADE on ${CONTROL}:\n${made.out.split("\n").slice(-8).join("\n")}`); @@ -527,7 +527,7 @@ function stepIn(said: string): string { * Put on the anchor what the installer needs to read, and run it. * * **This is the whole of what changed, and it is not a refactor.** The bed used to apply the - * substrate bundle itself, by hand, and then enrol four machines in one loop. It got the order + * foundation bundle itself, by hand, and then enrol four machines in one loop. It got the order * right by accident and it modelled the wrong shape: an install procedure that exists only as a * test fixture is exercised by whoever writes tests and never by whoever installs, which is why * every bootstrap fault this year was found late (novox/hq ADR 0067). The anchor is now raised by @@ -566,31 +566,31 @@ async function genesis(images: HeldImage[]): Promise { } report.push(` catalogue ${CATALOGUE_MODULES.join(", ")} at ${CATALOGUE_ON_MACHINE}`); - // The substrate TEMPLATE — not the bundle. The installer produces the bundle from it: it replaces + // The foundation TEMPLATE — not the bundle. The installer produces the bundle from it: it replaces // the control plane's image with the id of the image it carries, renames that container - // `temp-mesh-control`, and writes the result where a person can read it. + // `temp-mesh-controller`, and writes the result where a person can read it. // // Two substitutions still happen here, and both belong to the bed rather than to the installer. // The example names three images at a registry the lab no longer raises: the store and the broker // become the upstream references mesh-catalog pins (harness), and the machine pulls them over its - // uplink like any first node. The third, mesh-control, is deliberately LEFT naming that dead + // uplink like any first node. The third, mesh-controller, is deliberately LEFT naming that dead // registry — the installer overwrites it, and leaving it proves that it does. // // And the broker's advertised address. The template hardcodes 192.0.2.10:5671, the old // separate-anchor address; a token carries MESH_BROKER_ADDRESS verbatim as the endpoint an - // enrolling node dials, so with the substrate on novox it must be novox's own public address or + // enrolling node dials, so with the foundation on novox it must be novox's own public address or // every node would enrol against a dead one. The installer refuses to guess this and says so // loudly, which is right — it does not know what this machine is called from outside. const template = bundleFor(images).replaceAll("192.0.2.10:5671", "192.0.2.20:5671"); - const local = join(tmpdir(), `mesh-lab-substrate-${process.pid}.lock`); + const local = join(tmpdir(), `mesh-lab-foundation-${process.pid}.lock`); writeFileSync(local, template); - await push(instanceId, CONTROL, local, "/tmp/substrate-template.lock"); + await push(instanceId, CONTROL, local, "/tmp/foundation-template.lock"); const command = [ BOOTSTRAP_PATH, `--source ${source}`, `--source-ref ${sourceRef}`, - `--bundle /tmp/substrate-template.lock`, + `--bundle /tmp/foundation-template.lock`, `--catalog ${CATALOGUE_ON_MACHINE}`, `--node ${CONTROL}`, `--registry ${MESH_REGISTRY}`, @@ -628,10 +628,10 @@ async function genesis(images: HeldImage[]): Promise { // ------------------------------------------------------------------------------------------ // 1. The control plane answers. Asked of the PERMANENT container by name — `status` opens all - // three stores, so a reply proves the connections it was given are the substrate's own. - const answered = await on(CONTROL, `docker exec mesh-control /mesh-control status`, 60_000); + // three stores, so a reply proves the connections it was given are the foundation's own. + const answered = await on(CONTROL, `docker exec mesh-controller /mesh-controller status`, 60_000); report.push(` control plane ${answered.ok ? answered.out.split("\n")[0] : "NO ANSWER"}`); - if (!answered.ok) return stop("after step 10", `mesh-control does not answer:\n${answered.out}`); + if (!answered.ok) return stop("after step 10", `mesh-controller does not answer:\n${answered.out}`); // 2. The registry replies on /v2/ — the registry API's own "yes, I am one and I am ready". A // container that is up is not a registry that serves. @@ -643,22 +643,22 @@ async function genesis(images: HeldImage[]): Promise { // 3. THE PIVOT COMPLETED. ADR 0067 states this check in as many words: after installing, the // running control plane's image is pinned by a digest THE MESH'S OWN REGISTRY ASSIGNED — not - // by an image id. If it is still an image id, the substrate's container is what is running, + // by an image id. If it is still an image id, the foundation's container is what is running, // nothing was published, and this mesh can never roll out its own upgrades. - const pinnedTo = (await on(CONTROL, `docker inspect --format '{{.Config.Image}}' mesh-control`)) + const pinnedTo = (await on(CONTROL, `docker inspect --format '{{.Config.Image}}' mesh-controller`)) .out.trim(); report.push(` pinned to ${pinnedTo || "(nothing)"}`); if (/^sha256:[0-9a-f]{64}$/.test(pinnedTo)) { return stop("after step 10", - `mesh-control is running from ${pinnedTo}, which is an IMAGE ID — the digest of the image's ` + + `mesh-controller is running from ${pinnedTo}, which is an IMAGE ID — the digest of the image's ` + `own configuration, which no registry ever served. The pivot did not happen: what is ` + `running is the image the installer carried, not one this mesh published, so this mesh ` + `cannot upgrade itself (novox/hq ADR 0067, "the pivot completed").`); } - if (!new RegExp(`^${MESH_REGISTRY.replaceAll(".", "\\.")}/mesh-control@sha256:[0-9a-f]{64}$`) + if (!new RegExp(`^${MESH_REGISTRY.replaceAll(".", "\\.")}/mesh-controller@sha256:[0-9a-f]{64}$`) .test(pinnedTo)) { return stop("after step 10", - `mesh-control is running from ${pinnedTo || "nothing this bed could read"}, which is not a ` + + `mesh-controller is running from ${pinnedTo || "nothing this bed could read"}, which is not a ` + `digest assigned by ${MESH_REGISTRY}.`); } @@ -669,36 +669,36 @@ async function genesis(images: HeldImage[]): Promise { // outside to one that can — same container, same digest, same registry. The difference is // whether a build happened, and the only place that is visible is the installer saying so. const wanted = sourceRef.slice(0, 8); - if (!new RegExp(`built mesh-control from ${wanted}`).test(said)) { + if (!new RegExp(`built mesh-controller from ${wanted}`).test(said)) { return stop("after the last step", - `the installer never said it built mesh-control from ${wanted}. What runs may have been ` + + `the installer never said it built mesh-controller from ${wanted}. What runs may have been ` + `carried rather than made here, which is a mesh that cannot rebuild its own control plane.`); } - report.push(` built here mesh-control from ${wanted}, by the carried builder`); + report.push(` built here mesh-controller from ${wanted}, by the carried builder`); // 3b. And the registry really serves it, asked of the registry rather than of the container. A // reference is a claim; a tag list is the registry agreeing. const tags = await on(CONTROL, - `curl -s --max-time 10 http://${MESH_REGISTRY}/v2/mesh-control/tags/list`); + `curl -s --max-time 10 http://${MESH_REGISTRY}/v2/mesh-controller/tags/list`); report.push(` registry holds ${tags.out.trim() || "nothing"}`); if (!tags.out.includes("genesis")) { return stop("after step 10", - `${MESH_REGISTRY} does not serve mesh-control, so the digest the container is pinned to ` + + `${MESH_REGISTRY} does not serve mesh-controller, so the digest the container is pinned to ` + `names an image nothing can pull: ${tags.out.trim()}`); } // 4. The temporary control plane is GONE. Two control planes is the half-finished state, and the - // name is the audit: a machine running mesh-control and not temp-mesh-control has pivoted. - const temp = await on(CONTROL, `docker inspect --format '{{.State.Status}}' temp-mesh-control`); - report.push(` temp-mesh-control ${temp.ok ? `STILL HERE (${temp.out.trim()})` : "gone"}`); + // name is the audit: a machine running mesh-controller and not temp-mesh-controller has pivoted. + const temp = await on(CONTROL, `docker inspect --format '{{.State.Status}}' temp-mesh-controller`); + report.push(` temp-mesh-controller ${temp.ok ? `STILL HERE (${temp.out.trim()})` : "gone"}`); if (temp.ok) { return stop("after step 10", - `temp-mesh-control is still ${temp.out.trim()}. Two control planes are consuming this ` + + `temp-mesh-controller is still ${temp.out.trim()}. Two control planes are consuming this ` + `mesh's broker queues; neither is wrong and the pivot is not finished.`); } // 5. And the mesh has heard from its one node. Everything the join phase does next depends on it. - const nodes = await on(CONTROL, `docker exec mesh-control /mesh-control node list`); + const nodes = await on(CONTROL, `docker exec mesh-controller /mesh-controller node list`); report.push(` node list ${nodes.out.trim().split("\n").join(" | ")}`); const line = nodes.out.split("\n").map((l) => l.trim()).find((l) => l.startsWith(`${CONTROL} `)); if (!line || !/^\S+\s+here\b/.test(line)) { @@ -715,7 +715,7 @@ async function genesis(images: HeldImage[]): Promise { // ================================================================================================== /** - * ace, shanks and g14 join. Host binary plus a token — no bootstrap, no substrate, no registry. + * ace, shanks and g14 join. Host binary plus a token — no bootstrap, no foundation, no registry. * * **novox is not in this loop.** It was enrolled by the installer, as part of becoming a mesh, and * enrolling it again would present the mesh with a second identity for a node it already knows — @@ -866,7 +866,7 @@ test("the full mesh forms across the access point and both server sets converge" const known = added.get(name); if (known !== undefined) return known; const { manifest, broker } = loadManifest(name); - await must(CONTROL, `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-control:/${name}.json`); + await must(CONTROL, `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); added.set(name, broker); return broker; @@ -892,7 +892,7 @@ test("the full mesh forms across the access point and both server sets converge" // Operator-provided app credentials (own-secrets), delivered as fake values through `secret accept`. const credentialDelivered = new Map(); for (const name of new Set(CREDENTIALS.map((c) => c.name))) { - await must(CONTROL, `printf %s ${quote(`fake-${name}-value`)} > /tmp/fake-${name} && docker cp /tmp/fake-${name} mesh-control:/fake-${name}`); + await must(CONTROL, `printf %s ${quote(`fake-${name}-value`)} > /tmp/fake-${name} && docker cp /tmp/fake-${name} mesh-controller:/fake-${name}`); } for (const c of CREDENTIALS) { if (!assigned[c.node]!.has(c.module)) { @@ -912,7 +912,7 @@ test("the full mesh forms across the access point and both server sets converge" if (!assigned[s.node]!.has(s.module)) continue; try { const inControl = `/secret-${s.module}-${s.name}`; - await must(CONTROL, `printf %s ${quote(s.value)} > /tmp${inControl} && docker cp /tmp${inControl} mesh-control:${inControl}`); + await must(CONTROL, `printf %s ${quote(s.value)} > /tmp${inControl} && docker cp /tmp${inControl} mesh-controller:${inControl}`); await mesh(`secret accept ${s.node} ${s.module} ${s.name} --from ${inControl}`); } catch (err) { console.log(`OPERATOR SECRET FAILED ${s.node}/${s.module}/${s.name}: ${(err as Error).message.split("\n").slice(0, 2).join(" | ")}`); @@ -1021,7 +1021,7 @@ test("the full mesh forms across the access point and both server sets converge" // and before this bed set a public domain it composed to nothing on every node, silently. // // What is NOT checked here is issuance: whether route-proxy actually obtains a certificate from - // step-ca over ACME. That path is being fixed in mesh-control as this is written, and a bed that + // step-ca over ACME. That path is being fixed in mesh-controller as this is written, and a bed that // gated on it would be reporting somebody else's in-flight work as this bed's failure. // ================================================================================================ const adr: string[] = ["================ ADR 0066: LABELLED ROUTES ================"]; diff --git a/test/integration/whole-mesh-novox.test.ts b/test/integration/whole-mesh-novox.test.ts index edbf11c..85d1dd4 100644 --- a/test/integration/whole-mesh-novox.test.ts +++ b/test/integration/whole-mesh-novox.test.ts @@ -1,11 +1,11 @@ /** * The whole `novox` server's converted service set, installed together on ONE node behind the - * substrate — the whole-catalogue install this rebuild has never actually run. First stage of a + * foundation — the whole-catalogue install this rebuild has never actually run. First stage of a * whole-mesh rehearsal (novox/hq). * - * Topology (proven by assigned-two-node-db.test.ts): the substrate (store, broker, control) rides + * Topology (proven by assigned-two-node-db.test.ts): the foundation (store, broker, control) rides * `anchor` and nothing else; ALL of novox's services ride the `novox` node. novox's own postgres - * provider owns 5432 there, so it cannot co-locate with the substrate store. An overlay is placed so + * provider owns 5432 there, so it cannot co-locate with the foundation store. An overlay is placed so * each consumer's binding `at` resolves to novox's private address and reaches the providers * co-located with it. * @@ -28,7 +28,7 @@ * host ports here (container ports unchanged); the provider ports the consumers actually connect to * (postgres 5432, minio 9000, mongodb 27017, mssql 1433) are left as-is. See REMAP below. * - * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/substrate-first-node.lock + * MESH_LAB_HOST_BINARY=.../mesh-host MESH_LAB_BUNDLE=.../examples/foundation-first-node.lock * scripts/build-module-runtime.sh builds one runtime per module that has code; the route-proxy image * is built by scripts/build-route-proxy-image.sh; scenarios/whole-mesh-novox.yml stocks them all * alongside every server image. @@ -42,7 +42,7 @@ import { loadScenario } from "../../src/declaration/parse.ts"; import { raise } from "../../src/lifecycle/raise.ts"; import { destroy, exec } from "../../src/lifecycle/operate.ts"; import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts"; -import { labIsUsable, destroyAll, substrateBundle, onTheMachine } from "./harness.ts"; +import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts"; import type { HeldImage } from "../../src/pinning.ts"; const capability = await labIsUsable(); @@ -55,13 +55,13 @@ const skip = !capability.usable : !binary || !existsSync(binary) ? "MESH_LAB_HOST_BINARY is not set to a built mesh-host" : !bundle || !existsSync(bundle) - ? "MESH_LAB_BUNDLE is not set to a substrate bundle (mesh-host examples/)" + ? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)" : false; const SCENARIO = "whole-mesh-novox"; const NODE = "novox"; -/** Where the committed module.json files live: the mesh-catalog beside mesh-control. */ +/** Where the committed module.json files live: the mesh-catalog beside mesh-controller. */ const catalogDir = process.env["MESH_LAB_CATALOG"] ?? (modulesEnv ? resolve(dirname(dirname(dirname(modulesEnv))), "mesh-catalog", "modules") : "") ?? resolve(process.cwd(), "..", "mesh-catalog", "modules"); @@ -103,7 +103,7 @@ const MODULES: { name: string; containers: string[]; node?: boolean }[] = [ * fail2ban declares `capabilities: ["intrusion-prevention"]`, but mesh-host advertises no such * capability: profile/detectors.go defines container-runtime, package-manager, service-manager, * firewall, overlay, seat, privileged and graphical-session — nothing for intrusion-prevention. So - * NO node can ever host fail2ban. Worse, `mesh-control assign` records the assignment even while + * NO node can ever host fail2ban. Worse, `mesh-controller assign` records the assignment even while * reporting it "cannot be applied", and the whole-node `push` then refuses to resolve the ENTIRE node * ("nothing was sent") over that one un-hostable assignment — one bad module blocks every other. It * is therefore left unassigned here so the rest of the set can be proven. (novox/hq — escalated.) @@ -118,8 +118,8 @@ const DROPPED: { name: string; why: string }[] = [ /** * The provable CORE: modules that converge WHOLE on this node (every container up and stable) once - * the substrate resolves and applies the set. This bed gates green on the CORE — a regression in any - * of these turns it red. It is the substrate + all five providers + the four consumers that reach + * the foundation resolves and applies the set. This bed gates green on the CORE — a regression in any + * of these turns it red. It is the foundation + all five providers + the four consumers that reach * their providers and stay up + the four standalone apps. */ const CORE = new Set([ @@ -132,7 +132,7 @@ const CORE = new Set([ * KNOWN GAPS: modules that RESOLVE and are placed, but whose containers do not stay up because the * committed catalog manifest is incomplete (or, for firewall, a node-service expectation is unmet). * They are reported every run with the exact failure, and escalated (novox/hq) — but they do not gate - * green, because the gap is in the catalog/host, not in this bed or the mesh substrate. + * green, because the gap is in the catalog/host, not in this bed or the mesh foundation. * * umami — the mesh-umami provisioner needs the umami server URL and admin password in its * provisioner.env; the manifest wires neither, so it dies "UMAMI url or admin password @@ -185,7 +185,7 @@ async function must(machine: string, command: string, timeoutMs?: number): Promi /** The control plane, a container on the first node. */ async function mesh(command: string, timeoutMs?: number): Promise { - return must("anchor", `docker exec mesh-control /mesh-control ${command}`, timeoutMs); + return must("anchor", `docker exec mesh-controller /mesh-controller ${command}`, timeoutMs); } /** The pinned reference this scenario's registry serves for a repository. */ @@ -194,9 +194,9 @@ function pinned(reference: string): string { return onTheMachine(reference, held); } -/** The substrate bundle: ours by the ID the machine holds, everything else upstream. */ +/** The foundation bundle: ours by the ID the machine holds, everything else upstream. */ function bundleFor(images: HeldImage[]): string { - return substrateBundle(bundle, images); + return foundationBundle(bundle, images); } /** @@ -236,7 +236,7 @@ interface NodeState { /** Ask the mesh, in its own terms, what a node has done with what it was sent. Never throws. */ async function nodeState(node: string): Promise { - const asked = await on("anchor", `docker exec mesh-control /mesh-control status --json`); + const asked = await on("anchor", `docker exec mesh-controller /mesh-controller status --json`); if (!asked.ok) return { reached: false, applied: false, current: false, waiting: false, raw: asked.out }; let state: { wrong: { node: string; outcome: string; refused?: string; failed?: { id: string; error: string }[] }[]; @@ -270,12 +270,12 @@ before(async () => { instanceId = raised.instanceId; held = raised.images; - // anchor raises the substrate from its bundle, digests rewritten to the scenario registry's. - await must("anchor", `cat > /tmp/substrate.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); - await must("anchor", `${HOST_PATH} apply /tmp/substrate.lock`, 900_000); + // anchor raises the foundation from its bundle, digests rewritten to the scenario registry's. + await must("anchor", `cat > /tmp/foundation.lock <<'MESHBUNDLE'\n${bundleFor(raised.images)}\nMESHBUNDLE`); + await must("anchor", `${HOST_PATH} apply /tmp/foundation.lock`, 900_000); const up = await must("anchor", `docker ps --format '{{.Names}}'`); - for (const c of ["mesh-store", "mesh-broker", "mesh-control"]) { - assert.match(up, new RegExp(c), `the substrate did not raise ${c}:\n${up}`); + for (const c of ["mesh-store", "mesh-broker", "mesh-controller"]) { + assert.match(up, new RegExp(c), `the foundation did not raise ${c}:\n${up}`); } // Both machines join the one mesh and run a host so they apply what they are pushed. @@ -316,7 +316,7 @@ test("the whole novox service set resolves, installs and converges on one node i for (const { name } of MODULES) { try { const { manifest, broker } = loadManifest(name); - await must("anchor", `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-control:/${name}.json`); + await must("anchor", `printf %s ${quote(manifest)} > /tmp/${name}.json && docker cp /tmp/${name}.json mesh-controller:/${name}.json`); await mesh(`module add /${name}.json`); if (broker) { await mesh(`module issue ${name} --node ${NODE}`); diff --git a/test/lastrun.test.ts b/test/lastrun.test.ts index 3e6d0b9..2b19ae4 100644 --- a/test/lastrun.test.ts +++ b/test/lastrun.test.ts @@ -23,13 +23,13 @@ test("a machine that has never run the suite is told so", () => { // The one that matters: it passed, and against code nobody runs any more. test("a run against code that has since changed is not current", () => { const said = judge( - passing("2026-08-31T11:00:00Z", { "mesh-lab": "aaa", "mesh-control": "bbb" }), + passing("2026-08-31T11:00:00Z", { "mesh-lab": "aaa", "mesh-controller": "bbb" }), now, - { "mesh-lab": "aaa", "mesh-control": "ccc" }, + { "mesh-lab": "aaa", "mesh-controller": "ccc" }, ); assert.equal(said.current, false, "a run against changed code was reported as current"); const text = said.lines.join("\n"); - assert.match(text, /mesh-control\s+at bbb, now at ccc/, text); + assert.match(text, /mesh-controller\s+at bbb, now at ccc/, text); assert.match(text, /code that has since changed/, text); }); diff --git a/test/pinning.test.ts b/test/pinning.test.ts index 65a943a..e03769a 100644 --- a/test/pinning.test.ts +++ b/test/pinning.test.ts @@ -16,8 +16,8 @@ import { */ const HELD: HeldImage[] = [ { - requested: "mesh-control:development", - repository: "mesh-control", + requested: "mesh-controller:development", + repository: "mesh-controller", reference: "sha256:" + "a".repeat(64), }, { @@ -51,7 +51,7 @@ test("the repository is the reference without its tag", () => { */ test("only what is built here and published nowhere counts as ours", () => { for (const ours of [ - "mesh-control:development", "mesh-runtime-plex:development", "mesh-route-proxy:development", + "mesh-controller:development", "mesh-runtime-plex:development", "mesh-route-proxy:development", "mesh-provision-postgres@sha256:" + "0".repeat(64), ]) { assert.ok(isMeshBuilt(ours), `${ours} is one of ours and was not recognised`); @@ -61,7 +61,7 @@ test("only what is built here and published nowhere counts as ours", () => { "registry.example:5000/novox/www:latest", // A registry host in front of one of our names does NOT make it ours: it says somebody // published it, so the machine can fetch it from there like anything else. - "registry.example:5000/mesh-control:development", + "registry.example:5000/mesh-controller:development", ]) { assert.ok(!isMeshBuilt(theirs), `${theirs} is not ours and was claimed`); } @@ -96,16 +96,16 @@ test("a third-party image is left exactly as the manifest wrote it", () => { }); test("a reference of ours that already carries a registry is still redirected", () => { - // What the committed substrate bundle looks like: written for a target that had a registry. - const before = `"image": "192.0.2.250:5000/mesh-control@sha256:${"e".repeat(64)}"`; + // What the committed foundation bundle looks like: written for a target that had a registry. + const before = `"image": "192.0.2.250:5000/mesh-controller@sha256:${"e".repeat(64)}"`; assert.equal(pinnedInto(before, HELD), `"image": "sha256:${"a".repeat(64)}"`); }); // A longer repository ending in a shorter one must not be half-replaced. test("a repository that ends in another one is not partly rewritten", () => { - const before = `"image": "our-mesh-control@sha256:${"7".repeat(64)}"`; + const before = `"image": "our-mesh-controller@sha256:${"7".repeat(64)}"`; assert.equal(pinnedInto(before, HELD), before, - "'our-mesh-control' was rewritten because it ends in 'mesh-control'"); + "'our-mesh-controller' was rewritten because it ends in 'mesh-controller'"); }); test("every image in a whole manifest is settled at once", () => { @@ -127,7 +127,7 @@ test("every image in a whole manifest is settled at once", () => { }); test("what a repository is held under can be asked for, and absence is not an empty string", () => { - assert.equal(referenceFor(HELD, "mesh-control"), `sha256:${"a".repeat(64)}`); + assert.equal(referenceFor(HELD, "mesh-controller"), `sha256:${"a".repeat(64)}`); assert.equal(referenceFor(HELD, "mesh-runtime-plex"), undefined); }); @@ -144,7 +144,7 @@ test("what is still a placeholder can be named", () => { */ test("an image the machine cannot fetch by itself is handed over", () => { for (const handed of [ - "mesh-control:development", + "mesh-controller:development", "mesh-runtime-plex:development", `registry.example/novox/www@sha256:${"a".repeat(64)}`, "registry.example:5000/novox/photos-server:latest", diff --git a/test/place.test.ts b/test/place.test.ts index ebb7da1..e8dddbb 100644 --- a/test/place.test.ts +++ b/test/place.test.ts @@ -38,10 +38,10 @@ test("a per-machine entry OVERRIDES `all:`, it does not add to it", () => { // Worth being exact about: a scenario naming one artifact for one machine gets that // artifact, not that artifact plus everything in `all:`. The opposite reading would place // things nobody asked for, which is the shape of fault this lab exists to catch. - const placements = planPlacements(scenario("place:\n all: [host]\n anchor: [substrate]")); + const placements = planPlacements(scenario("place:\n all: [host]\n anchor: [foundation]")); const byMachine = new Map(placements.map((p) => [p.machine, p.artifacts])); - assert.deepEqual(byMachine.get("anchor"), ["substrate"], "anchor should have ONLY substrate"); + assert.deepEqual(byMachine.get("anchor"), ["foundation"], "anchor should have ONLY foundation"); assert.deepEqual(byMachine.get("peer"), ["host"]); }); @@ -74,11 +74,11 @@ scenario: s segments: { hosting: { kind: public, cidr: [192.0.2.0/24] } } machines: anchor: { at: { segment: hosting, address: [192.0.2.10] }, egress: true } -images: [mesh-control:development, mesh-runtime-redis:development] +images: [mesh-controller:development, mesh-runtime-redis:development] place: { all: [host, runtime] } `); assert.deepEqual(planHeldImages(s), [ - { machine: "anchor", images: ["mesh-control:development", "mesh-runtime-redis:development"] }, + { machine: "anchor", images: ["mesh-controller:development", "mesh-runtime-redis:development"] }, ]); }); @@ -90,16 +90,16 @@ machines: anchor: at: { segment: hosting, address: [192.0.2.10] } egress: true - images: [mesh-control:development] + images: [mesh-controller:development] laptop: at: { segment: hosting, address: [192.0.2.20] } egress: true images: [mesh-runtime-redis:development] -images: [mesh-control:development, mesh-runtime-redis:development] +images: [mesh-controller:development, mesh-runtime-redis:development] place: { all: [host, runtime] } `); assert.deepEqual(planHeldImages(s), [ - { machine: "anchor", images: ["mesh-control:development"] }, + { machine: "anchor", images: ["mesh-controller:development"] }, { machine: "laptop", images: ["mesh-runtime-redis:development"] }, ]); }); @@ -113,7 +113,7 @@ segments: { hosting: { kind: public, cidr: [192.0.2.0/24] } } machines: anchor: { at: { segment: hosting, address: [192.0.2.10] }, egress: true } bare: { at: { segment: hosting, address: [192.0.2.20] }, egress: true, images: [] } -images: [mesh-control:development] +images: [mesh-controller:development] place: { all: [host, runtime] } `); assert.deepEqual(planHeldImages(s).map((p) => p.machine), ["anchor"]); @@ -132,15 +132,15 @@ place: { all: [host] } test("a tier that does not exist is refused BY NAME", () => { // Named individually rather than refused as a whole, so a scenario placing a host and a - // substrate is told exactly which half the lab cannot do — rather than being told `place:` + // foundation is told exactly which half the lab cannot do — rather than being told `place:` // is unsupported when half of it now works. try { - assertSupported(scenario("place:\n all: [host, substrate]\n peer: [control]")); + assertSupported(scenario("place:\n all: [host, foundation]\n peer: [control]")); assert.fail("expected a refusal"); } catch (err) { assert.ok(err instanceof UnsupportedError); const missing = err.missing.join("\n"); - assert.match(missing, /substrate/, "the substrate was not named"); + assert.match(missing, /foundation/, "the foundation was not named"); assert.match(missing, /control/, "the control plane was not named"); assert.doesNotMatch(missing, /place: host/, "the host is placeable and was refused anyway"); } @@ -172,18 +172,18 @@ test("an image reference is placeable, and a bare 'image:' is not", () => { test("the placeables are host, runtime and an image", () => { assert.ok(isPlaceable("host")); assert.ok(isPlaceable("runtime")); - assert.ok(!isPlaceable("substrate"), "the tiers above tier 0 do not exist yet"); + assert.ok(!isPlaceable("foundation"), "the tiers above tier 0 do not exist yet"); assert.ok(!isPlaceable("control-plane")); }); test("an unplaceable artifact is named, not refused as a whole", () => { - // A scenario placing a host and a substrate is told which half the lab cannot do — refusing + // A scenario placing a host and a foundation is told which half the lab cannot do — refusing // wholesale would send somebody looking for the wrong problem. const scenario = { name: "s", segments: {}, machines: { a: {} as never }, - place: { a: ["host", "runtime", "image:alpine@sha256:x", "substrate"] }, + place: { a: ["host", "runtime", "image:alpine@sha256:x", "foundation"] }, } as unknown as Parameters[0]; assert.throws( @@ -192,7 +192,7 @@ test("an unplaceable artifact is named, not refused as a whole", () => { // Checked as `place: —`, which is how an artifact is REPORTED as // unplaceable. Searching for the bare word matched the message's own list of what CAN // be placed, which mentions runtime — so the test failed on a correct message. - assert.ok(err.message.includes("place: substrate —"), "the unplaceable one is named"); + assert.ok(err.message.includes("place: foundation —"), "the unplaceable one is named"); assert.ok(!err.message.includes("place: image:alpine"), "a placeable one is not"); assert.ok(!err.message.includes("place: runtime —"), "nor is runtime"); assert.ok(!err.message.includes("place: host —"), "nor is host"); diff --git a/test/rebuild.test.ts b/test/rebuild.test.ts index f452700..32af5f5 100644 --- a/test/rebuild.test.ts +++ b/test/rebuild.test.ts @@ -79,9 +79,9 @@ test("the whole-mesh bed hands its anchor no control-plane image", () => { ...Object.values(scenario.machines).flatMap((m) => m.images ?? []), ]; assert.deepEqual( - named.filter((i) => i.startsWith("mesh-control")), + named.filter((i) => i.startsWith("mesh-controller")), [], - "the anchor is handed mesh-control, so genesis would never find out whether the installer " + + "the anchor is handed mesh-controller, so genesis would never find out whether the installer " + "really carries it", ); }); diff --git a/test/supported.test.ts b/test/supported.test.ts index 6a57d20..5154ec3 100644 --- a/test/supported.test.ts +++ b/test/supported.test.ts @@ -60,20 +60,20 @@ place: { all: [host] }`); }); test("a tier above 0 is still refused, and named", () => { - // The refusal narrowed rather than disappearing. A scenario placing a host AND a substrate + // The refusal narrowed rather than disappearing. A scenario placing a host AND a foundation // must be told which half is missing — not that `place:` is unsupported, when half of it // now works. const scenario = parseScenario(`scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: { a: { at: { segment: net, address: [192.0.2.1] } } } -place: { all: [host, substrate] }`); +place: { all: [host, foundation] }`); try { assertSupported(scenario); assert.fail("should have refused"); } catch (err) { assert.ok(err instanceof UnsupportedError); - assert.equal(err.missing.length, 1, `expected only the substrate: ${err.missing.join(", ")}`); - assert.match(err.missing[0] ?? "", /substrate/); + assert.equal(err.missing.length, 1, `expected only the foundation: ${err.missing.join(", ")}`); + assert.match(err.missing[0] ?? "", /foundation/); assert.match(err instanceof Error ? err.message : "", /silently lacks them/); } }); diff --git a/test/validate.test.ts b/test/validate.test.ts index 065b70e..41d2716 100644 --- a/test/validate.test.ts +++ b/test/validate.test.ts @@ -280,7 +280,7 @@ test("one of ours in images: is accepted", () => { assert.doesNotThrow(() => parseScenario(`scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: { a: { at: { segment: net, address: [192.0.2.1] }, egress: true } } -images: [mesh-control:development, mesh-route-proxy:development] +images: [mesh-controller:development, mesh-route-proxy:development] place: { all: [runtime] }`)); }); @@ -288,7 +288,7 @@ test("images: is named by tag — an image ID is not knowable until the image is refuses(`scenario: x segments: { net: { kind: public, cidr: [192.0.2.0/24] } } machines: { a: { at: { segment: net, address: [192.0.2.1] }, egress: true } } -images: ["mesh-control@sha256:${"0".repeat(64)}"] +images: ["mesh-controller@sha256:${"0".repeat(64)}"] place: { all: [runtime] }`, /is pinned by digest/); }); @@ -303,7 +303,7 @@ machines: at: { segment: net, address: [192.0.2.1] } egress: true images: [mesh-runtime-redis:development] -images: [mesh-control:development] +images: [mesh-controller:development] place: { all: [runtime] }`, /is not in this scenario's images/); }); diff --git a/test/warm.test.ts b/test/warm.test.ts index adf2207..3f9be20 100644 --- a/test/warm.test.ts +++ b/test/warm.test.ts @@ -3,7 +3,7 @@ import assert from "node:assert/strict"; import { judge, type Warm } from "../src/warm.ts"; const at = "2026-08-31T20:00:00Z"; -const built = { "mesh-lab": "aaa", "mesh-host": "bbb", "mesh-control": "ccc" }; +const built = { "mesh-lab": "aaa", "mesh-host": "bbb", "mesh-controller": "ccc" }; const warm = (over: Partial = {}): Warm => ({ scenario: "two-nodes", instanceId: "mlab-two-nodes-1", images: [], against: built, at, ...over });