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.
73 lines
4.6 KiB
Markdown
73 lines
4.6 KiB
Markdown
---
|
|
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:<key>}` 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.
|