Issue 209: a bundle's own SDK copy registers into a registry the runtime never reads; design 38 WP4 note #316
@@ -191,6 +191,32 @@ tool where they need root, which they have, since the runtime runs as the node's
|
|||||||
four machines; `docker ps` shows no `mesh-nftables`; `status` is well. Then the fail2ban holder
|
four machines; `docker ps` shows no `mesh-nftables`; `status` is well. Then the fail2ban holder
|
||||||
proposed in an open change follows the same way when it lands.
|
proposed in an open change follows the same way when it lands.
|
||||||
|
|
||||||
|
*Found 2026-10-03, before the step ran:* a bundle imported in-process brings its own copy of the SDK
|
||||||
|
(WP3's *carry its dependencies*), and the SDK's tool registry is the copy's own — the first module
|
||||||
|
loaded beside the runtime would have registered its tools where the runtime never looks, and served
|
||||||
|
nothing, silently. Issue
|
||||||
|
[209](../../04-ISSUES/209-a-bundles-own-sdk-copy-registers-into-a-registry-the-runtime-never-reads/00-report.md):
|
||||||
|
the runtime now resolves every bundle's import of the SDK to its own copy, one registry and one
|
||||||
|
broker per node. Two things the package did not say, settled in the module: the filter's commands
|
||||||
|
run through `sudo` without a prompt where the runtime is not root, since the operator's account may
|
||||||
|
escalate as the operator would; and a bundle has no environment of its own, so the tool reads the
|
||||||
|
filter from the path the manifest's `filtering` names rather than from a variable the container used
|
||||||
|
to carry, a test holding the two together. Three things a review of the change found: the module's
|
||||||
|
own bus credential and state directory went with the container, since nothing reads them once the
|
||||||
|
runtime speaks with the node's (the shell module of WP5 declares neither); the `iptables` package the
|
||||||
|
image used to carry is now declared on the host; and that the operator's account may escalate without
|
||||||
|
a prompt is a fact about the machine the mesh neither declares nor checks — true on all four today,
|
||||||
|
and when it is not, the tool names it by how it failed, which is the only check there is until a
|
||||||
|
record says where the fact belongs.
|
||||||
|
*Built 2026-10-03* (mesh-tools `7152148` for issue 209, mesh-catalog `db5e7c8`). *Proven live
|
||||||
|
2026-10-03, on all four machines*: `node-packet-filter.rules`, `reload` and `remove` answer from the
|
||||||
|
node's runtime on each — `rules` and the module's own tool list the mesh's table, `reload` loads the
|
||||||
|
file and answers with the table, `remove` refuses the mesh's own table by name — `docker ps` shows no
|
||||||
|
`mesh-nftables` on any, the container's credential is gone with it, and `status` is well. One thing
|
||||||
|
the step found is issue
|
||||||
|
[210](../../04-ISSUES/210-the-host-re-creates-the-nodes-runtime-on-every-reconcile/00-report.md):
|
||||||
|
the host re-creates the runtime's process on every reconcile.
|
||||||
|
|
||||||
## WP5 — The shell, on a server first
|
## WP5 — The shell, on a server first
|
||||||
|
|
||||||
*mesh-catalog #224, already written. Half a day to assign and prove.*
|
*mesh-catalog #224, already written. Half a day to assign and prove.*
|
||||||
|
|||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
status: resolved
|
||||||
|
opened: 2026-10-03
|
||||||
|
located-in:
|
||||||
|
- mesh-tools
|
||||||
|
fixed-by: mesh-tools #32
|
||||||
|
amended-design:
|
||||||
|
---
|
||||||
|
|
||||||
|
# 209 — A bundle's own copy of the SDK registers into a registry the runtime never reads
|
||||||
|
|
||||||
|
## What was observed
|
||||||
|
|
||||||
|
2026-10-03, preparing [design 38](../../03-DESIGN/01-to-be/38-building-the-operators-machine.md)
|
||||||
|
WP4 — the first module whose tools bundle the node's runtime would load beside its own. Before the
|
||||||
|
manifest changed, a probe on the laptop did what the runtime does: a process holding one copy of
|
||||||
|
`@novox/mesh-sdk` imported a bundle in another directory that carried its own copy, and the bundle
|
||||||
|
called `registerModuleTools` as every tools entrypoint does.
|
||||||
|
|
||||||
|
```
|
||||||
|
registrations seen by host after importing bundle: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
The import succeeds, the bundle registers, and the runtime's `collectTools` sees nothing. The
|
||||||
|
runtime would log the bundle as loaded and answer `tools` for the module with an empty list: silent,
|
||||||
|
and indistinguishable from a module that serves nothing by choice.
|
||||||
|
|
||||||
|
## Why it matters beyond this instance
|
||||||
|
|
||||||
|
WP3 found that *a TypeScript bundle must carry its dependencies*, and the toolchain copies them in
|
||||||
|
so a bundle starts anywhere. Among them is the SDK. The runtime imports a bundle in-process, and
|
||||||
|
the language resolves a bare import from the importing file's own tree — so every bundle brings a
|
||||||
|
second SDK into the process: its own registry of tools and its own broker handle. The SDK's registry
|
||||||
|
is a module-level list, and the runtime reads only the one it imported itself.
|
||||||
|
|
||||||
|
Every module from WP4 on is loaded this way. The runtime's tests never met it because their fixture
|
||||||
|
bundles sit under the runtime's own tree and resolve the same copy. Nothing in design 38 WP1 or WP3
|
||||||
|
says which SDK a loaded bundle speaks to, and the one-runtime record
|
||||||
|
([ADR 0175](../../02-DECISIONS/0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md))
|
||||||
|
assumes without saying that it is the runtime's.
|
||||||
|
|
||||||
|
## Diagnosis
|
||||||
|
|
||||||
|
Owner mesh-tools (`node-tools`): the runtime imports a bundle's entrypoint and counts the
|
||||||
|
registrations that appear after it, through the SDK it imported; the bundle's `import "@novox/mesh-sdk/tools"`
|
||||||
|
resolves to the copy under the bundle's own `node_modules`. **Fix direction:** the runtime resolves
|
||||||
|
every import of the SDK, from whichever bundle, as if the runtime had written it — one registry,
|
||||||
|
one broker — and leaves everything else a bundle carries to the bundle's own tree. A test loads a
|
||||||
|
bundle from a directory holding its own copy of the SDK and asserts its tools are served. The
|
||||||
|
toolchain keeps copying dependencies in: a bundle launched as a process ([ADR 0188](../../02-DECISIONS/0188-a-modules-own-code-is-bundles-in-any-language-and-a-tools-bundle-speaks-mcp-to-the-runtime.md))
|
||||||
|
needs them, and an imported one is simply not allowed to bring a second SDK.
|
||||||
|
|
||||||
|
## Resolution
|
||||||
|
|
||||||
|
2026-10-03, mesh-tools #32: the runtime installs a synchronous resolve hook before the first bundle is
|
||||||
|
imported, sending every import of the SDK, from whichever bundle, to its own copy; a bundle's other
|
||||||
|
dependencies still resolve from its own tree, and a bundle launched as a process is untouched. The test
|
||||||
|
loads a bundle from a directory holding its own copy of the SDK and its own dependency, and sees its
|
||||||
|
tool served with the dependency's answer. Proven live 2026-10-03 by WP4's proof in design 38: the packet filter's bundle, the first loaded beside
|
||||||
|
the runtime's own, serves its four tools on all four machines.
|
||||||
Reference in New Issue
Block a user