Two findings from building the catalogue on a live mesh

The runtime every module compiles against cannot be built by the mesh, so the
one rule that would catch it moving can never fire. And a container keeps the
values it was created with, so two good applies can leave it running on neither.
This commit is contained in:
2026-09-13 01:49:02 +02:00
parent 58ad0742d8
commit 3a9ed9d3fd
2 changed files with 131 additions and 0 deletions
@@ -0,0 +1,71 @@
---
status: open
opened: 2026-09-13
located-in: []
fixed-by:
amended-design:
---
# 044 — The runtime every module builds on cannot be built by the mesh
## Symptom
Every module written in the mesh's own toolchain is compiled on top of one shared base image — the
tool runtime. The mesh can build the modules. It cannot build the base.
Two things stop it, and each is enough on its own:
- The base's container definition copies a compiled output directory that is not in the repository.
A clean clone has the sources and no compiled output, so the definition refers to something that
is not there.
- The base's dependency on the mesh's own software development kit resolves to a **sibling checkout
on disk**, not to anything a clone can fetch:
```
"node_modules/@novox/mesh-sdk" -> { "resolved": "../mesh-sdk", "link": true }
```
Both mean the same thing: the base can only be produced on a workstation that happens to have the
neighbouring repositories laid out beside it, and has run a compile step by hand first.
Observed while building four modules through the mesh from a repository and a path. All four
succeeded, and all four recorded that they were built on top of the same base — an image reference
that no module in the mesh produced, because no module could have.
## Why this matters
**This is the one artifact the whole build chain rests on, and it is the one artifact outside the
chain.** Three separate consequences, all visible today:
- *The base is pinned by hand.* Every module's container definition names it as a literal digest
written by a person. Nothing checks that digest against anything.
- *Nothing can tell when it moves.* A build records what it was built on top of, so an artifact is
stale when anything beneath it moved. That rule cannot fire for the base: the graph has edges
pointing at it, and no version on the other end, because no build ever registered one. The mesh
is therefore unable to answer "what must be rebuilt" for the change that would reach *every*
module at once.
- *Nobody can check what is in it.* The published base and the definition in the repository have
already drifted: the definition names one operating system base, and the image actually serving
every module is built on a different one. This was found by running the image, not by reading
anything, and it went unnoticed for exactly as long as nobody could rebuild it.
A rule the design states — an artifact is stale when anything it was built against moved — is
unenforceable for the artifact it matters most for. That is the shape of a wrong rule, not a
missing feature: everything downstream reports "nothing to rebuild" and is believed.
## How it would be checked
The check is the fix's own test: clone the repository alone, into an empty directory, and build it.
Nothing else present, no neighbouring checkouts, no compile run first. If that produces the base,
the mesh can produce it too, because that is all the mesh does.
## Open questions
- Where should the software development kit come from, for a build that has only one repository?
The mesh already knows how to run a package registry as a module, and it already publishes
container images to one of its own — so this may be the same answer twice rather than a new one.
- Is the base a module like any other, or is it one of the few things a new mesh must arrive
carrying? It behaves like both: everything is built on top of it, and it is built out of the same
sources as everything else.
- If it becomes an ordinary module, what stops the circularity — the base is built by the builder,
and the builder is built on the base?
@@ -0,0 +1,60 @@
---
status: open
opened: 2026-09-13
located-in: []
fixed-by:
amended-design:
---
# 045 — A container keeps the values it started with
## Symptom
A module reads its database credentials from a file the mesh composes. The file was written, then
rewritten with a different value once the mesh had more to say — the provider's address was not
known the first time and was the second.
The container went on running with the first version, indefinitely, and reported nothing. Its own
view:
```
DATABASE_URL=postgresql://…@:20000/… what the container holds
DATABASE_URL=postgresql://…@<provider>:20000/… what the file on disk says
```
A module may declare that it restarts when one of its files changes, and this one now does. That
did not help: the restart fires when the file changes *during an apply*, and the change had already
happened before the declaration naming it arrived. Applying again is a no-op, because nothing
changed that time either. The only thing that recovered it was removing the module from the machine
and putting it back.
## Why this matters
**Reconciliation compares intentions, not what is actually running.** Two intentions in a row can
both be applied successfully and still leave a container holding values from neither, because a
container captures its environment once, when it is created, and nothing afterwards re-reads it.
The failure is silent in the worst way: the container is up, the machine reports success, the mesh
reports every module current, and the thing inside is using a credential the mesh no longer
believes in. Nothing in the system is in a position to notice — the host knows what it wrote, and
the module knows what it read, and no one compares the two.
It is not specific to credentials. Any composed value delivered through a file a container reads at
start has the same shape, which is most of what the mesh delivers.
## How it would be checked
A machine reporting a module as running should be able to say *what it is running with* — not what
was last written for it. The check is then a comparison the mesh can make on every heartbeat rather
than a property of one apply: for each container, does what it holds match what the files it was
built from now say. A mismatch is a drift the mesh can act on, and today it is not expressible at
all.
## Open questions
- Should the host recreate such a container on its own, or report the drift and let the mesh decide?
Recreating is the obvious repair and is also an unannounced restart of a running service.
- Is "the files a container was created from" something the host already knows, or does a
declaration have to say it? A restart-on list is close to this, but it is written by the module
author and is therefore exactly as complete as they remembered to make it.
- Does the same gap exist for values the mesh delivers by other means than a file?