From 36454d7e4a0add9d5f586e2b16c3290e9a877b65 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 30 Sep 2026 22:36:20 +0200 Subject: [PATCH 1/2] Issues 173 and 174 resolved; the installation check refuses at registration (ADR 0155) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- ...no-installation-and-how-that-is-checked.md | 3 ++ 03-DESIGN/01-to-be/18-building-a-module.md | 2 +- .../27-a-module-requires-the-mesh-resolves.md | 20 ++++++++++-- .../00-report.md | 2 +- .../00-report.md | 2 +- .../00-report.md | 31 +++++++++++++++++-- .../00-report.md | 24 ++++++++++++-- 7 files changed, 72 insertions(+), 12 deletions(-) diff --git a/02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md b/02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md index 0916793..9deb8e5 100644 --- a/02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md +++ b/02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md @@ -67,6 +67,8 @@ registration**, because the list it prints is the list that shrinks, and a regis manifest whose only remedy is a merge elsewhere would refuse the mesh's own catalogue on the day the check landed. It moves to registration when the list has been empty for a release. +> **Progressive insight — 2026-09-30.** The list was empty the day the check landed — every remaining name declared with its reason — and the operator asked for registration to refuse at once rather than after a release. It does, since mesh-controller PR 175: `module add` and a build's result are refused in the check's words, naming the way out, and the build stays recorded. The decision stands; only the day moved. + **A name a definition means is declared with its reason.** `names-on-purpose` on a resource maps each such name to why: *the federation's public key server, the world's*; *built outside the mesh, from the application's own repository, until that repository is a build source on the git seat*. A name the map @@ -113,6 +115,7 @@ no base for a seat a context names refuses the build by the seat's name rather t | An image from an installation's registry needs a reason | a test without and with the word | | A module named after a domain is reported | a test | | No definition in the catalogue names an installation | `TestNoCatalogueManifestNamesAnInstallation` over the checkout, and `module check modules/` | +| A definition naming an installation is refused at registration, and one declaring its names passes | `TestRegistrationRefusesADefinitionNamingAnInstallation` (2026-09-30) | | `${setting:key}` fills from the layers, node over mesh; refused by name when unset; not stray when set | three tests | | A context on a seat is cloned from the base the mesh sent; a seat with no base is refused by name | the builder's test | diff --git a/03-DESIGN/01-to-be/18-building-a-module.md b/03-DESIGN/01-to-be/18-building-a-module.md index 59a3019..fc5b7c7 100644 --- a/03-DESIGN/01-to-be/18-building-a-module.md +++ b/03-DESIGN/01-to-be/18-building-a-module.md @@ -234,7 +234,7 @@ counts as a copy and what as a base. | resource | is | a module may | |---|---|---| -| `directory` | a directory with a mode and an owner | ✅ | +| `directory` | a directory with a mode and an owner, **placed by the mesh** under the node's root: `place: "."` is the assignment's own root, `place: "mesh"` the mesh's directory for the module, a pathless one sits beneath the root by its id; a stated path is the placement for data that must stay where it is, and may itself sit beneath a placed one (`${dir:}/…`). Everything else names it as `${dir:}` ([issue 119](../../04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md), [174](../../04-ISSUES/174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md)) | ✅ | | `file` | literal content, with `${bound:…}`, `${secret:…}`, `${dir:…}`, `${port:…}`, `${machine:…}` and `${setting:…}` filled in — the last an operator's value from the assignment's settings, refused by name when unset ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)) | ✅ | | `user` | a login | ✅ | | `access` | a pre-existing path it may use and must not own | ✅ | diff --git a/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md b/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md index b124d11..15de309 100644 --- a/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md +++ b/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md @@ -184,9 +184,21 @@ placeholders have, not yet the one requirement form below; they are phase 1's fi *Phase 3, in part (2026-09-30):* every definition's **own** data directory is placed; the conversion moved no data, proven by resolving both catalogues with the controller's rule and comparing ([issue 119](../../04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md)). -What the mesh writes *for* a module is still placed by the definition +What the mesh writes *for* a module was still placed by the definition ([issue 174](../../04-ISSUES/174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md)), -which is the gap this design answered on 2026-09-26 and has not built. +the gap this design answered on 2026-09-26; built later the same day: a directory saying +`place: "mesh"` is `/mesh/`, a directory beneath a placed one states its path as +`${dir:}/` and moves with it, and the same proof — both catalogues resolved and compared — +shows forty-eight definitions naming the paths they named before. + +*The operator's value travels only where it is asked for (2026-09-30, +[issue 173](../../04-ISSUES/173-a-modules-settings-reach-every-fact-it-contributes/00-report.md)):* +a setting overrides a key a contribution or a served fact declares and adds none; a file keeps taking +any key. A provider that must tell its consumers an operator's value — a mail server's domain, an +identity provider's issuer — declares it in what it serves as `${setting:}`, and it is refused by +name when nothing sets it. That is the contract half of this design's operator provider in the shape +the placeholder allows: the definition says which values reach which requirement, and nothing else +does. *How it is checked:* the unit tests named in issue 173, and the plan comparison that closed it. ## How a definition reads what was resolved @@ -396,7 +408,9 @@ writes (`/var/lib/mesh/`: sealed credentials, composed bindings) need a module-visible reservation. They do not: a module *requires* a `host-path` and receives a location; what the mesh writes for the module is the mesh's plumbing, placed where the mesh chooses and mounted in — never part of the module's contract. One reservation per -requirement, `//`. +requirement, `//`. *(Built 2026-09-30: the mesh's directory for a module is +`/mesh/`, named in the definition as a placed directory and nowhere as a path — +issue 174.)* **Resolution happens in the controller, at declaration composition.** The node receives concrete paths exactly as today — the wire format and the host's apply do not change for diff --git a/04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md b/04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md index 9d9dbf7..40bf774 100644 --- a/04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md +++ b/04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md @@ -153,7 +153,7 @@ not exist. **What remains is not this record's.** The 232 host paths still in the catalogue are where the mesh writes what it makes for a module, under `/var/lib/mesh/`; design 27 says the mesh places -those itself, and it does not yet. That is [issue 174](../174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md). +those itself, and it does not yet. That is [issue 174](../174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md). *Placed since later the same day: `place: "mesh"` — issue 174 is resolved.* The defects this record listed under *where that has already gone wrong* are unchanged by this and stay in 174's scope where they concern the mesh's files; the operator's shared data stays an access ([ADR 0051](../../02-DECISIONS/0051-shared-data-is-the-operators.md)). diff --git a/04-ISSUES/134-a-definition-may-still-name-the-mesh/00-report.md b/04-ISSUES/134-a-definition-may-still-name-the-mesh/00-report.md index 11138b4..ff02a28 100644 --- a/04-ISSUES/134-a-definition-may-still-name-the-mesh/00-report.md +++ b/04-ISSUES/134-a-definition-may-still-name-the-mesh/00-report.md @@ -77,4 +77,4 @@ composes for its route**, read through the route's binding, and an operator's ow another mesh's forge stays a URL, which the check reports and `names-on-purpose` would declare. The seven values that remain are declared with their reason — four applications built outside the mesh — and are the list that shrinks ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)). -Registration does not refuse yet; it will when the list has been empty for a release. +Registration does not refuse yet; it will when the list has been empty for a release. *2026-09-30, later the same day:* it refuses — the list was empty the day the check landed, and the operator asked for it (mesh-controller PR 175); `module add` and a build's result are refused in the check's words, with the way out, and the build stays recorded. diff --git a/04-ISSUES/173-a-modules-settings-reach-every-fact-it-contributes/00-report.md b/04-ISSUES/173-a-modules-settings-reach-every-fact-it-contributes/00-report.md index da113f7..ecd5d20 100644 --- a/04-ISSUES/173-a-modules-settings-reach-every-fact-it-contributes/00-report.md +++ b/04-ISSUES/173-a-modules-settings-reach-every-fact-it-contributes/00-report.md @@ -1,9 +1,9 @@ --- -status: located +status: resolved opened: 2026-09-30 located-in: [mesh-controller internal/catalogue/settings.go (settle), mesh-controller internal/catalogue/declaration.go (composed, ownNames)] -fixed-by: -amended-design: +fixed-by: mesh-controller PR 175 (a setting overrides a declared key and adds none; `${setting:…}` in a served or contributed value); mesh-catalog PR 196 (mail declares its domain, the identity provider its issuer) +amended-design: 03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md --- # 173 — A module's settings reach every fact it contributes, not only the file that asked @@ -45,3 +45,28 @@ asked for it. Until that form exists, this is the cost of the placeholder being - What does a consumer do with a served key it did not expect? Today: nothing, silently. A served map is not checked against what the provision's contract says it carries, because there is no such contract yet. + +## Resolved, 2026-09-30 + +**A setting overrides a key a contribution or a served fact declares, and adds none.** A file keeps +taking any key, because a configuration file is where an operator adds things; a contribution and a +served fact are a contract the other side reads, and a setting made for one of the module's files is +no part of it. A key that lands nowhere — no mergeable file, no `${setting:…}` asking for it, no +contribution or served fact declaring it — is named as stray when the node is planned, rather than +dropped. + +The first open question is answered the smaller way, and it turned out to be the right one: the two +keys consumers actually read through the leak — the mail provider's `domain`, the identity provider's +`issuer` — are now **declared** by the provider in what it serves, as the operator's value +(`${setting:domain}`, `${setting:issuer}`), filled from the same setting that used to leak and refused +by name when nothing sets it. So the contract says what travels, which is the shape design 27 draws, +without a second key for overrides. The second question stands: a consumer still checks nothing +against a contract, because there is none yet; what it is told is now only what the provider +declared. + +*How it was checked:* the plans of all four nodes, under the running controller and the one with the +rule, compared resource by resource — every key that disappears from a contribution is a leaked file +setting or one of the mesh's own words (`expose`, `endpoints`), and nothing a provider reads goes +away; the two served keys were declared before the controller rolled. Unit tests: a setting a route +never declared does not reach the proxy; a served value nothing sets is refused by name; the setting +a served fact asks for is not stray. diff --git a/04-ISSUES/174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md b/04-ISSUES/174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md index e1ae843..00477e4 100644 --- a/04-ISSUES/174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md +++ b/04-ISSUES/174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md @@ -1,9 +1,9 @@ --- -status: located +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: -amended-design: +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 @@ -46,3 +46,21 @@ test that proved 119 can prove again with the rule extended. - A binding file today lands wherever `binds` says; 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, `/mesh/`, 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}/` 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. -- 2.54.0 From 52e9df0f02ab0f8eb6834beed41e319b100003f5 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 1 Oct 2026 00:04:10 +0200 Subject: [PATCH 2/2] Issue 153 resolved: an assignment places a module's directories and its accesses mesh-controller PR 176 and mesh-catalog PR 198. Designs 27 and 18 carry the words: places, accesses, ${access:}, the default a definition still holds while the catalogue converts. --- 03-DESIGN/01-to-be/18-building-a-module.md | 2 +- .../27-a-module-requires-the-mesh-resolves.md | 6 ++++ .../00-report.md | 28 +++++++++++++++++-- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/03-DESIGN/01-to-be/18-building-a-module.md b/03-DESIGN/01-to-be/18-building-a-module.md index fc5b7c7..0a8b34c 100644 --- a/03-DESIGN/01-to-be/18-building-a-module.md +++ b/03-DESIGN/01-to-be/18-building-a-module.md @@ -237,7 +237,7 @@ counts as a copy and what as a base. | `directory` | a directory with a mode and an owner, **placed by the mesh** under the node's root: `place: "."` is the assignment's own root, `place: "mesh"` the mesh's directory for the module, a pathless one sits beneath the root by its id; a stated path is the placement for data that must stay where it is, and may itself sit beneath a placed one (`${dir:}/…`). Everything else names it as `${dir:}` ([issue 119](../../04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md), [174](../../04-ISSUES/174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md)) | ✅ | | `file` | literal content, with `${bound:…}`, `${secret:…}`, `${dir:…}`, `${port:…}`, `${machine:…}` and `${setting:…}` filled in — the last an operator's value from the assignment's settings, refused by name when unset ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)) | ✅ | | `user` | a login | ✅ | -| `access` | a pre-existing path it may use and must not own | ✅ | +| `access` | a pre-existing path it may use and must not own, **named by id** and placed by the assignment (`accesses: {: }` on its settings); mounts say `${access:}`; a path in the definition is the default an assignment replaces, tolerated while the catalogue converts ([issue 153](../../04-ISSUES/153-an-adopted-machines-data-cannot-be-placed-where-it-is/00-report.md)) | ✅ | | `archive` | files fetched by digest and unpacked | ✅ | | `package` | a package that must be present | ✅ | | `network` | a named container network | ✅ | diff --git a/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md b/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md index 15de309..c6fada1 100644 --- a/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md +++ b/03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md @@ -137,6 +137,12 @@ lost is a named volume, not a directory ([ADR 0030](../../02-DECISIONS/0030-data the assignment says nothing; - **a placement**, where the assignment puts one directory elsewhere: on a second disk, or where an adopted machine's data already is ([ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md)). + *Built 2026-10-01 ([issue 153](../../04-ISSUES/153-an-adopted-machines-data-cannot-be-placed-where-it-is/00-report.md)):* + `places` on the assignment's settings, by directory id, with an owner where the data already has + one; and `accesses`, by access id, for the operator's data — an access has an id and its mounts + name it as `${access:}`. Both validated as `endpoints` is: an id the definition does not + declare is refused. *How it is checked:* the controller's placement tests, and the + path-preservation proof extended to accesses. **An operator's shared data** is an access, as before ([ADR 0051](../../02-DECISIONS/0051-shared-data-is-the-operators.md)): never created, owned or removed by the mesh. The module requires read or read-write access. Where diff --git a/04-ISSUES/153-an-adopted-machines-data-cannot-be-placed-where-it-is/00-report.md b/04-ISSUES/153-an-adopted-machines-data-cannot-be-placed-where-it-is/00-report.md index f2907ef..d5a3a3a 100644 --- a/04-ISSUES/153-an-adopted-machines-data-cannot-be-placed-where-it-is/00-report.md +++ b/04-ISSUES/153-an-adopted-machines-data-cannot-be-placed-where-it-is/00-report.md @@ -1,11 +1,11 @@ --- -status: open +status: resolved opened: 2026-09-29 located-in: - mesh-controller internal/catalogue/dir_into.go (dirsFor: a stated path or //, nothing else) - mesh-controller (accesses: the path is the manifest's literal) -fixed-by: -amended-design: +fixed-by: mesh-controller PR 176 (`places` and `accesses` on an assignment, `${access:}`, an owner the data already has); mesh-catalog PR 198 (ten definitions name their accesses by id) +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] --- # 153 — An adopted machine's data cannot be placed where it is @@ -55,3 +55,25 @@ The two assignment halves 0112 decided: a setting that places a declared directo path on this node, and a setting that says where an access's data is — both validated like `endpoints` (unknown ids refused), and an access placed by the assignment still never created, chowned or removed. + +## Resolved, 2026-10-01 + +The two assignment halves ADR 0112 decided exist. On an assignment's settings, `places` puts a +declared directory (by id) at a path on this node, with an owner where the data already has one — +`{"config": "/where/it/is", "data": {"path": "…", "owner": "1001:2000"}}` — and `accesses` says where +the operator's data is, by the access's id. Both are validated the way `endpoints` is: an id the +definition does not declare is refused, naming what it does declare; a relative path and a +non-numeric owner are refused; an access nothing places and whose definition carries no path is +refused with the setting to write, rather than mounted as nothing. A placed directory is still the +mesh's — created, owned as said, removed when empty and undeclared. A placed access is still the +operator's — mounted, never created, owned or removed. + +An access now has an **id**, and the definition's mounts name it as `${access:}`, so a placement +moves the mount with it. Ten catalogue definitions were given ids; each keeps its path as the default +an assignment may replace, so the machine that said nothing received exactly the paths it had before +(the path-preservation proof, extended to accesses). That default is still a host path in a +definition, tolerated as the transition: the media modules on the control node hold it until their +assignments say where the data is, and then the defaults go. + +What the home server's assignments say next is the operator's: per media module, `places` for the +configuration on the second disk and `accesses` for the pool, with the owner the predecessor ran as. -- 2.54.0