Wave 1: thirteen modules' code moves into bundles the node's runtime serves (hq ADR 0198, to-be 38 WP4c) #245

Merged
mesh-admin merged 13 commits from feat/0198-wave-1-module-code-moves into main 2026-10-03 22:52:09 +00:00
Contributor

One commit per module. Each runtime container's imported entrypoints (MESH_TOOL_MODULES) become loads of one TypeScript bundle named code. Its env becomes the bundle's env, with mount targets folded back to host paths and container-network names replaced by 127.0.0.1:${port:N}. The container, build.on, the Dockerfile and the broker own-secret are removed. mesh-state is removed only where nothing else names it.

Per module

Module Runtime loads Step / package Removed beyond the common set
postgres handlers, tools, provisioner package postgresql-libs (psql) mesh-state; the ${seat:mesh-store:5432} word, which is optional in the client
redis handlers, tools, provisioner mesh-state
mosquitto handlers, tools, provisioner run-once process bootstrap; package mosquitto (mosquitto_ctrl)
influxdb tools, provisioner
keycloak handlers, tools, provisioner
umami tools, provisioner mesh-state; the provisioner-env file, now one word
cloudflare-dns tools, provisioner mesh-state; MESH_RECEIVES now names the real grants path
grafana handlers, tools
icecast handlers, tools
home-assistant handlers, tools run-once process provisions, which keeps its restart-on
nodered tools run-once process mqtt, which keeps its restart-on
nextcloud handlers, tools uses the host's docker CLI and socket for occ
minio tools, provisioner package minio-client, with MESH_MINIO_MC_BIN=mcli mesh-state; mc's config moves from /tmp to the state directory

Run-once steps use an env-file. The controller's portInto fills ${port:} only in file content and container env, not in a process's env. Each step's environment is therefore a mode 0600 file resource, <step>-env, whose content the mesh fills. The process reads it through env-file, as showcase does, and lists that file under restart-on. The longer-term fix is to make portInto cover process env in mesh-controller.

No code changed. Every container-only default is overridden by an env value.

Validation

  • Builder-style compile. tsc was run on each bundle's entrypoints against @novox/mesh-sdk@0.1.6 from the registry, with --rootDir .. All 13 compile, and every declared entrypoint is emitted.
  • Module tests pass. postgres 4, mosquitto 5, influxdb 11, keycloak 10, home-assistant 13, nodered 6. The other seven modules have no tests.
  • Controller catalogue tests pass. go test ./internal/catalogue/... ./cmd/mesh-controller/ ran with this branch as the sibling catalogue.
  • Composition checked. A throwaway test composed each module's declaration with the runtime. Every word and step env-file resolves, and no ${…} is left except ${secret:}, which the host fills. No container runs an artifact. Grant-dependent service files were left out of that harness, and main fails the same way without them.

Needs live proof

  • Event handlers and provisioners. node-tools must run them through mesh/subscribe and mesh/publish, and grants must flow end to end. Postgres, redis, mosquitto, keycloak, influxdb, umami, minio and cloudflare-dns are the ones to check first.
  • Run-once steps. Each step must run on first apply and run again when a binding or its env-file changes.
  • Files given to the operator account. The state, grants, mesh-state and named secret files are chowned to the operator account. Service containers reach them through bind mounts, so this should not matter, but it is unproven.
  • The mosquitto package. It ships mosquitto.service, which is not enabled. It must stay disabled, or it collides with the container on 1883.
  • The minio and postgres CLIs. minio needs mcli reachable under the runtime's PATH, and postgres needs psql from postgresql-libs.
  • nextcloud's docker access. The runtime account must be in the docker group on whichever machine runs nextcloud.

Not merged; nothing live was touched.

One commit per module. Each runtime container's imported entrypoints (`MESH_TOOL_MODULES`) become `loads` of one TypeScript bundle named `code`. Its env becomes the bundle's `env`, with mount targets folded back to host paths and container-network names replaced by `127.0.0.1:${port:N}`. The container, `build.on`, the Dockerfile and the `broker` own-secret are removed. `mesh-state` is removed only where nothing else names it. ## Per module | Module | Runtime loads | Step / package | Removed beyond the common set | |---|---|---|---| | postgres | handlers, tools, provisioner | package `postgresql-libs` (psql) | `mesh-state`; the `${seat:mesh-store:5432}` word, which is optional in the client | | redis | handlers, tools, provisioner | | `mesh-state` | | mosquitto | handlers, tools, provisioner | run-once process `bootstrap`; package `mosquitto` (mosquitto_ctrl) | | | influxdb | tools, provisioner | | | | keycloak | handlers, tools, provisioner | | | | umami | tools, provisioner | | `mesh-state`; the `provisioner-env` file, now one word | | cloudflare-dns | tools, provisioner | | `mesh-state`; `MESH_RECEIVES` now names the real grants path | | grafana | handlers, tools | | | | icecast | handlers, tools | | | | home-assistant | handlers, tools | run-once process `provisions`, which keeps its restart-on | | | nodered | tools | run-once process `mqtt`, which keeps its restart-on | | | nextcloud | handlers, tools | uses the host's docker CLI and socket for occ | | | minio | tools, provisioner | package `minio-client`, with `MESH_MINIO_MC_BIN=mcli` | `mesh-state`; mc's config moves from /tmp to the state directory | **Run-once steps use an env-file.** The controller's `portInto` fills `${port:}` only in file content and container env, not in a process's `env`. Each step's environment is therefore a mode 0600 file resource, `<step>-env`, whose content the mesh fills. The process reads it through `env-file`, as showcase does, and lists that file under `restart-on`. The longer-term fix is to make `portInto` cover process env in mesh-controller. **No code changed.** Every container-only default is overridden by an env value. ## Validation - **Builder-style compile.** tsc was run on each bundle's entrypoints against `@novox/mesh-sdk@0.1.6` from the registry, with `--rootDir .`. All 13 compile, and every declared entrypoint is emitted. - **Module tests pass.** postgres 4, mosquitto 5, influxdb 11, keycloak 10, home-assistant 13, nodered 6. The other seven modules have no tests. - **Controller catalogue tests pass.** `go test ./internal/catalogue/... ./cmd/mesh-controller/` ran with this branch as the sibling catalogue. - **Composition checked.** A throwaway test composed each module's declaration with the runtime. Every word and step env-file resolves, and no `${…}` is left except `${secret:}`, which the host fills. No container runs an artifact. Grant-dependent service files were left out of that harness, and main fails the same way without them. ## Needs live proof - **Event handlers and provisioners.** node-tools must run them through `mesh/subscribe` and `mesh/publish`, and grants must flow end to end. Postgres, redis, mosquitto, keycloak, influxdb, umami, minio and cloudflare-dns are the ones to check first. - **Run-once steps.** Each step must run on first apply and run again when a binding or its env-file changes. - **Files given to the operator account.** The `state`, `grants`, `mesh-state` and named secret files are chowned to the operator account. Service containers reach them through bind mounts, so this should not matter, but it is unproven. - **The mosquitto package.** It ships `mosquitto.service`, which is not enabled. It must stay disabled, or it collides with the container on 1883. - **The minio and postgres CLIs.** minio needs `mcli` reachable under the runtime's PATH, and postgres needs psql from postgresql-libs. - **nextcloud's docker access.** The runtime account must be in the docker group on whichever machine runs nextcloud. Not merged; nothing live was touched.
mesh-admin added 13 commits 2026-10-03 21:34:17 +00:00
The mesh-postgres container goes with its Dockerfile, build bases and bus credential: its three entrypoints are loads of one bundle, given their words as host paths, and psql comes from postgresql-libs instead of the image's apt layer. The seat word is dropped, since a bundle's words cannot carry one and the client treats it as optional.
The mesh-redis container goes with its Dockerfile, build bases, bus credential and the state directory only that credential lived in. The bundle reaches redis on the port this machine published rather than the container network's name.
The mesh-mosquitto container goes with its Dockerfile, build bases and bus credential. mosquitto_ctrl comes from the mosquitto package, and the bootstrap step runs node on the bundle, reading the broker's published port from an env-file the mesh fills, because a process's env is not given ${port:…}.
The mesh-influxdb container goes with its Dockerfile, build bases and bus credential; its env becomes the bundle's words with mount targets folded back to host paths.
The mesh-keycloak container goes with its Dockerfile, build bases and bus credential; its env becomes the bundle's words with mount targets folded back to host paths.
The mesh-umami container goes with its Dockerfile, build bases, bus credential and state directory. The provisioner's env-file only told it umami's container-network address, so it becomes a word on the published port and the file goes.
The mesh-cloudflare-dns container goes with its Dockerfile, build bases, bus credential and state directory. MESH_RECEIVES now names the grants directory itself; the container's value pointed at a path nothing was mounted on.
The mesh-grafana container goes with its Dockerfile, build bases and bus credential; its env becomes the bundle's words with mount targets folded back to host paths.
The mesh-icecast container goes with its Dockerfile, build bases and bus credential. The bundle reaches icecast on the port this machine published rather than the container network's name.
The mesh-home-assistant container goes with its Dockerfile, build bases and bus credential. The provisions step runs node on the bundle and reads the binding files where the mesh writes them, from an env-file the mesh fills because a process's env is not given ${port:…}. It still runs again when a binding it reads changes.
The mesh-nodered container goes with its Dockerfile, build bases and bus credential. The mqtt step runs node on the bundle and reads the binding and settings files where the mesh writes them, from an env-file the mesh fills because a process's env is not given ${port:…}.
The mesh-nextcloud container goes with its Dockerfile, build bases and bus credential. occ still runs through docker exec, now with the host's own docker CLI and socket.
The mesh-minio container goes with its Dockerfile, build bases, bus credential and state directory. The client reaches minio on the published port, runs the minio-client package's mcli instead of the image's mc, and keeps mc's config, which holds the root alias, in the module's own state directory rather than a shared /tmp.
mesh-admin merged commit 64cc292d7e into main 2026-10-03 22:52:09 +00:00
mesh-admin deleted branch feat/0198-wave-1-module-code-moves 2026-10-03 22:52:09 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-catalog#245