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:
+71
@@ -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?
|
||||
Reference in New Issue
Block a user