ADR 0155: a definition names no installation, how that is checked, and the three ways out

Issues 122 and 134 resolved; design 27 in progress with its first cases; design 18 names the words.
This commit is contained in:
2026-09-30 18:40:05 +02:00
parent 7499f1e50c
commit 9ffb7eec55
6 changed files with 177 additions and 11 deletions
@@ -0,0 +1,125 @@
---
topic: what runs on it
status: accepted
date: 2026-09-30
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md
---
# 155. A definition names no installation: how that is checked, and the three ways a value that did gets out
## Context
[ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md) decided that a module definition
names no node, no mesh and no host path, and said how the name half is checked: *a catalogue test
finds no domain name in any definition value*. No such test existed
([issue 134](../04-ISSUES/134-a-definition-may-still-name-the-mesh/00-report.md)). Written and run
over the 77 definitions on 2026-09-30, the check it describes finds **42 values**, in 15 definitions,
and they are of four kinds that want four different answers:
| kind | count | example |
|---|---|---|
| a service told its own public name as a literal | 5 | an identity provider's `KC_HOSTNAME`, an object store's console redirect, an automation tool's webhook URL |
| an operator's value written into the definition | 10 | a mail server's domain, site name, website, and the address it trusts a real-IP header from |
| this mesh's forge, by URL, as a recipe's build context | 2 | the builder and the proxy, which package the controller's source |
| an application built outside the mesh, pulled from this mesh's registry | 7 | four sites and tools whose repositories are the operator's own |
| the world's servers, named by upstream defaults | 10 | a Matrix homeserver's trusted key server, Element's integration manager |
| a module named after the domain it serves | 8 | one site module, with its paths and network named after it |
Not one was careless. Each was the value the software needs, and until today there was nowhere else
to put it ([issue 122](../04-ISSUES/122-a-module-cannot-ask-for-its-own-public-name/00-report.md)).
Two of the answers were built before this record: a module is told the name its route composes
(`${bound:<route>:name}`, controller PR 149, 2026-09-30), and a source may be a path on the git seat
([ADR 0111](0111-a-build-source-is-on-the-git-seat-or-external.md)). What was missing: an operator's
value in a file the software reads, the same for a build *context*, a way to say a name is meant, and
the check.
## Considered Options
**1. A string search for the installation's own names.** Rejected. The controller is as
mesh-agnostic as the definitions; it does not know which names are "this mesh's", and a check that had
to be told would be configured per installation and pass everywhere else. What it can know is the
*shape*: a name under a public top-level domain, a public address.
**2. Report every such shape.** Rejected. Eight of the 42 were `why` strings — prose the mesh never
reads, explaining what a port is for — and a check that reports those beside `KC_HOSTNAME` teaches
people to ignore the report. And a Matrix homeserver *must* name the federation's public key server;
a check with no way to say so would be a check people argue with rather than obey.
**3. Judge what the mesh acts on; let a definition say which names it means, one by one, with a
reason; exempt the world's services that a definition may name as a policy default.** Chosen.
**For an operator's value**, one option was to wait for design 27's requirement form in full. Rejected
for the reason 0112 gave against a slow operator provider: if asking a person for a value takes more
than a setting, module authors route around it and the literals come back. `${setting:<key>}` is the
operator provider in its first form, on the settings a module already has.
## Decision
**The check.** Every string value of a definition that the mesh acts on is judged for a hostname under
a public top-level domain and for a public address. Not judged: `why` and `description`, which are
prose. Allowed where they can only mean the world: the public registries an `image` may be pulled
from, the public resolvers a machine may forward to, and the public certificate authorities' ACME
directories. The container runtime's alias for its own host is the runtime's. A module's own name is a
value too. The check runs in `module check` and as a catalogue-wide test; **it does not yet refuse at
registration**, because the list it prints is the list that shrinks, and a registration that refused a
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.
**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
does not cover is still reported. The host never sees the word.
**An operator's value reaches a file as `${setting:<key>}`**, filled from the module's settings
layers — the mesh's, then the node's — the same layers a mergeable file and a contribution take, so
`settings set <module>` stays the one place a person's values go. Refused, naming the key and the
command, when nothing set it: a default for a mail domain would be the literal this removes, and a
blank written silently would be a service that comes up wrong somewhere that names nothing.
**A build context may live on the git seat.** `context: {"seat": "git", "repository": "<owner>/<name>"}`
is composed by the mesh that builds it: the request carries each seat's clone base, and a builder told
no base for a seat a context names refuses the build by the seat's name rather than guessing a forge.
**A module is named for what it is.** The site module named after its domain is `website`.
## Consequences
- **The catalogue names no installation, and a test says so.** The forty-two became zero the same day,
by the four answers above; seven of them are declared on purpose and stay visible as the list to
shrink — four applications the mesh does not build yet.
- **An operator's values are the assignment's.** The mail module takes its domain, its site name, its
website and the address it trusts a real-IP header from as settings; a mesh that installs it without
them is refused at composition, by name, which is the right moment. The module's own README says
which.
- **What got harder:** a manifest reviewer has one more word to read, and `names-on-purpose` on an
application's image is a debt visible in the definition until the application is built here. A
reader of `settings set` output sees more keys than files, because a key a file asks for is a
destination too.
- **Not decided here:** ADR 0112's requirement form (design 27) still replaces `${setting:…}` and the
other placeholders when it lands; this is its first case, the way [ADR 0038](0038-the-mesh-assigns-the-port.md)
was for ports. Host paths ([issue 119](../04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md))
are the next step of the same group, and the registry's name ([issue 123](../04-ISSUES/123-the-image-registry-is-named-after-a-role/00-report.md))
the one after.
## How this is checked
| Rule | Checked by |
|---|---|
| A value naming an installation is reported at its path, in the definition's words | a catalogue unit test over a definition with a hostname in an env value and a public address in a file |
| Prose, the world's registries in an image, public resolvers, ACME directories and the runtime's own alias are not reported | the same tests |
| A name declared on purpose is not reported; a name beside it that is not declared is | a test with a homeserver's config |
| 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/` |
| `${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 |
## References
- [ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md) — extended: the check it promised, and the operator provider's first form
- [ADR 0111](0111-a-build-source-is-on-the-git-seat-or-external.md) — a source on the seat; now a context too
- [issue 122](../04-ISSUES/122-a-module-cannot-ask-for-its-own-public-name/00-report.md), [issue 134](../04-ISSUES/134-a-definition-may-still-name-the-mesh/00-report.md) — what this closes
- [27 — A module requires, the mesh resolves](../03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md) — where this sits in the larger design
- mesh-controller PR 169, mesh-catalog PR 188 — the check, the words, and the catalogue that passes it
+1
View File
@@ -258,6 +258,7 @@ python3 00-META/checks/index.py fail if stale
- **0147** — [A module anchors the mesh's authority on a machine, and takes it away again](0147-a-module-anchors-the-meshs-authority.md)
- **0150** — [A module's own code runs as supervised processes under the module's one account](0150-a-modules-own-code-runs-as-supervised-processes-under-one-account.md)
- **0152** — [The operator's surface is a module the mesh assigns: the console](0152-the-operators-surface-is-a-module-the-console.md)
- **0155** — [A definition names no installation: how that is checked, and the three ways a value that did gets out](0155-a-definition-names-no-installation-and-how-that-is-checked.md)
### How it is built