A setting overrides a key a contribution or served fact declares and adds none; a provider that must
tell its consumers an operator's value declares it as ${setting:…} (173). The mesh's own files for a
module are a placed directory, `place: "mesh"`, and forty-eight definitions name no host path for
them (174). Registration refuses a definition naming an installation, the day the list emptied
rather than a release later (0155, progressive insight; 134). Designs 27 and 18 carry the rules.
4.1 KiB
status: resolved
opened: 2026-09-30
located-in: [mesh-controller internal/catalogue/dir_into.go, mesh-controller internal/catalogue/declaration.go, mesh-catalog modules]
fixed-by: mesh-controller PR 175 (place: "mesh", a directory beneath a placed one, the proof test resolving both sides); mesh-catalog PR 197 (48 definitions converted)
amended-design: [03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md, 03-DESIGN/01-to-be/18-building-a-module.md]
174 — The mesh's own files for a module are placed by the definition, not by the mesh
What was observed
After every module's own data directory was placed by the mesh
(issue 119), the catalogue
still carries 232 host paths in 50 definitions, all of one kind: where the mesh writes what it
makes for the module — its sealed bus credential (own-secrets.broker), its merged config file, its
bindings — under /var/lib/mesh/<module>/…, and the directory resource that creates that subtree.
Not one of those files is the module's. The mesh mints the credential, composes the binding, merges
the config; the definition only says where to put them, and says it the same way seventy times.
Why this is here
Design 27's answer to what sits beneath a node's root (2026-09-26) is that the mesh's writes need no
module-visible reservation: what the mesh writes for a module is the mesh's plumbing, placed where
the mesh chooses and mounted in, never part of the module's contract. The definition today names
that place, so a definition is not yet free of host paths — and a node whose root is elsewhere would
place the module's data there and the mesh's files still under /var/lib/mesh.
The path-preserving test that let issue 119 close does not cover this: it proves a placed directory resolves to what was named, and these are not placed.
What it would take
A word for "the mesh's file for this module", or none: own-secrets values, a merged config file and
a binding could be named by key alone, with the mesh choosing <root>/mesh/<module>/<key> and
mounting it where the container says. The container side of the mount already exists in every
definition (/run/secrets/broker, /run/config/config.json); only the host side would go. The
change is in the controller, once, and then a mechanical edit of fifty definitions, which the same
test that proved 119 can prove again with the rule extended.
Open questions
- Does
own-secretskeep its map shape with the value becoming the container path rather than the host path, or does the mount stay where it is and the host side become a placeholder the mesh fills,${mesh:<key>}? - A binding file today lands wherever
bindssays; a module's code reads it from an environment variable naming the container path. If the host side is the mesh's, is the container side still the definition's to choose? It should be: it is the software's contract.
Resolved, 2026-09-30
The word is the one issue 119 introduced, with a second place: a directory saying place: "mesh" is
the mesh's directory for the module, <root>/mesh/<module>, beside the assignment's own root and
under the same node setting. The mesh's files keep their map shape and the container side of every
mount stays the definition's — only the host side changed, to ${dir:mesh-state}/…. A directory that
sat beneath the mesh's (a forge's runtime state, a manager's output) states its path as
${dir:mesh-state}/<rest> and moves with it; one that sits elsewhere by adoption (the registry's data)
keeps its literal path as the exception it is.
Forty-eight catalogue definitions and the controller's own manifest were converted mechanically. The proof is the same test that let issue 119 close, now resolving both checkouts before comparing, because the earlier manifest already placed its own directories: resolved on the default root, every converted definition names exactly the paths it named before. Nothing moved.
Both open questions are answered by keeping what exists: the map stays, the host side is placed; the container side is the software's contract and stays where the definition says.