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.