--- 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: 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 ## What was observed Setting the mail module's operator values — `domain`, `sitename`, `website`, `proxy-address` — so that its environment file could read them as `${setting:…}` ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)), and then planning the control node, showed the four keys in places nothing asked for them: - in every **route** the module contributes to the proxy, beside `label`, `endpoint` and `port`; - in the **database** it contributes to the store's provider, beside the database's `name`; - in the `smtp` facts every **consumer** of its mail provision is bound to. Nothing broke: a provider ignores a key it does not read. But a proxy now receives a mail server's `proxy-address` and `website` as if they were route facts, a consumer of mail is told the site's name, and a reader of `plan` cannot tell which of a contribution's keys the module meant and which leaked in. ## Why this is here Settings are one flat map per module, laid over every mergeable file, every contribution and every served fact alike (`settle`). That was the right generality when a setting *was* a contribution's override — a route's label is the example the code gives. It stops being right the day a setting is an operator's value for one file, which ADR 0155 made ordinary. The design permits a value to travel where nobody sent it, silently, and every consumer of a provision reads a map that grows with the provider's unrelated settings. [ADR 0112](../../02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md) and design 27 already say where this ends: a requirement has a contract, and a value goes to the requirement that asked for it. Until that form exists, this is the cost of the placeholder being the first case of it. ## Open questions - Should a key a file asks for with `${setting:}` be withheld from contributions and served facts, or should a contribution's overrides live under their own key (`contributes`, `serves`)? The second is the shape design 27 draws; the first is the smaller change and keeps the leak from widening while it is drawn. - 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.