Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ffb7eec55 | ||
|
|
7499f1e50c | ||
|
|
214b486a50 | ||
|
|
90b44a48df | ||
|
|
860331dc37 |
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ The console asks the `mesh-controller` seat's `tools` verb beside the modules an
|
||||
tools as `<seat>.<verb>` — `mesh-controller.status`, `mesh-controller.push` and the other ten. A call
|
||||
to `<prefix>.<name>` reaches the seat when the prefix is a seat declaring that verb, and the module
|
||||
otherwise; `seat:<seat>.<verb>` says so outright. When the control plane does not answer, the list
|
||||
names `mesh-controller (seat)` as not answering and carries the modules' tools regardless.
|
||||
names `mesh-controller (seat)` as not answering and carries the modules' tools regardless. The
|
||||
`mesh-controller` *module* is always named as not answering: it serves no module tools, only its seat's.
|
||||
|
||||
## Around it
|
||||
|
||||
|
||||
@@ -186,7 +186,8 @@ disagrees with it.
|
||||
| `grants` | credentials it must create for its consumers |
|
||||
| `filtering` | rules beyond its own ports |
|
||||
| `computed` | marks a module the controller generates rather than an author writing |
|
||||
| `build.artifacts` | what it produces |
|
||||
| `build.artifacts` | what it produces; an artifact's `context` may be a URL or a path on the git seat (`seat: git`), composed by the mesh that builds it |
|
||||
| `names-on-purpose` | on a resource: each name it means to name — the world's federation server, a registry that built an application the mesh does not — with its reason. A definition names no installation, and a test says so ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)) |
|
||||
|
||||
**A container mounts only what the manifest declares**
|
||||
([ADR 0091](../../02-DECISIONS/0091-a-mount-is-declared-three-ways.md)). A bind mount the module
|
||||
@@ -234,7 +235,7 @@ counts as a copy and what as a base.
|
||||
| resource | is | a module may |
|
||||
|---|---|---|
|
||||
| `directory` | a directory with a mode and an owner | ✅ |
|
||||
| `file` | literal content, with `${bound:…}` and `${secret:…}` filled in | ✅ |
|
||||
| `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 | ✅ |
|
||||
| `archive` | files fetched by digest and unpacked | ✅ |
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
---
|
||||
layer: to-be
|
||||
status: proposed
|
||||
code: []
|
||||
updated: 2026-09-26
|
||||
status: in-progress
|
||||
code: [mesh-controller internal/catalogue]
|
||||
updated: 2026-09-30
|
||||
decisions:
|
||||
- 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md
|
||||
- 02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md
|
||||
- 02-DECISIONS/0115-one-assignment-of-a-module-per-node.md
|
||||
- 02-DECISIONS/0113-the-vault-makes-every-secret.md
|
||||
- 02-DECISIONS/0114-a-shared-credential-rotates-over-two-credentials.md
|
||||
@@ -171,6 +172,15 @@ make a new external key, so rotating one means an operator handing over a new va
|
||||
assignment, and the route provider answers. A public name already held by another assignment is
|
||||
refused, like any other singular thing.
|
||||
|
||||
**Built so far, 2026-09-30** ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)):
|
||||
the operator's value in its first form — `${setting:<key>}` in a file's content, from the assignment's
|
||||
settings layers, refused by name when nothing set it; a module told the name its route composes
|
||||
(`${bound:<route>:name}`); a build context on the git seat; and the check that no definition names an
|
||||
installation, with `names-on-purpose` for the names a definition means. The host's directory in its
|
||||
first form is [`${dir:<id>}`](../../04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md),
|
||||
a placed directory under the node's root. Each is this design's provider in the shape the existing
|
||||
placeholders have, not yet the one requirement form below; they are phase 1's first cases.
|
||||
|
||||
## How a definition reads what was resolved
|
||||
|
||||
**One form, naming a requirement and a field of its contract.** A definition that needs the database's
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
layer: to-be
|
||||
status: in-progress
|
||||
status: implemented
|
||||
code: [mesh-controller, mesh-tools]
|
||||
updated: 2026-09-30
|
||||
decisions:
|
||||
@@ -139,6 +139,24 @@ verb answers every seat's tools from the records, because the console cannot rea
|
||||
console lists a role's tools beside the modules' own and resolves `<seat>.<verb>` to the seat when the
|
||||
seat declares that verb. Which verbs each *other* seat serves stays a decision per seat, still untaken.
|
||||
|
||||
## What shipped, 2026-09-30
|
||||
|
||||
mesh-controller PRs 166 and 167, mesh-tools PR 21. Verified on the live mesh the same evening: the
|
||||
console on a workstation lists the twelve verbs as `mesh-controller.<verb>` beside 67 module tools, and
|
||||
`mesh-controller.nodes` and `mesh-controller.status` answer through it with what the commands print.
|
||||
|
||||
Two things shipped bent. **The grant arrived after the holder started**: the controller composes the
|
||||
bus's user list and a push delivers it, so the first controller to serve its seat subscribed before
|
||||
the broker's list named the grant, the server refused all twelve subscriptions, and the client never
|
||||
retried — a holder now rebinds a refused subscription every thirty seconds (PR 167), and a controller
|
||||
roll-out that adds a grant is followed by a push to the broker node. **A JSON verb's answer was parsed
|
||||
from both output streams**, so `status --json`'s warnings hid the document as data; the answer is now
|
||||
parsed from standard output alone (mesh-controller PR 168, pending). The `output` field carried it
|
||||
either way.
|
||||
|
||||
What stays as designed and not built: which verbs any *other* seat serves, and §3 for module-declared
|
||||
seats' schemas beyond the names their manifests already list.
|
||||
|
||||
## What this does not settle
|
||||
|
||||
- Which verbs each seat should serve. That is a decision per seat, and the reason to do it slowly: a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
layer: to-be
|
||||
status: in-progress
|
||||
status: implemented
|
||||
code: [mesh-catalog modules/records]
|
||||
updated: 2026-09-30
|
||||
decisions:
|
||||
@@ -71,6 +71,20 @@ harmless. The mesh session of design 15, when it exists, calls this rather than
|
||||
| a sync against an unreachable origin leaves the checkout standing and says why | silence and success never look alike |
|
||||
| live: through the console, `records_search` for a phrase that appears only in a design document here returns it | issue 006's closing check |
|
||||
|
||||
## What shipped, 2026-09-30
|
||||
|
||||
mesh-catalog PR 183, then PR 185. Verified on the live mesh the same evening: `records` assigned to the
|
||||
control node with `{"repository": …}` as its setting, its checkout at the repository's `main` with 478
|
||||
documents, its five tools listed by the console beside every other tool, and — ADR 0025's check —
|
||||
`records_search` for a phrase from this document's title returned it from where it is written, with
|
||||
the commit. The first live search missed: the phrase chosen from ADR 0025 straddled a line break under
|
||||
emphasis, and the reader matched single lines. PR 185 matches a line together with the next and
|
||||
ignores emphasis marks, which the module's test now covers; until it rolls, a phrase that wraps is one
|
||||
to shorten.
|
||||
|
||||
The reader's `consumes` names the forge module's event rather than the `git` seat, because the seat
|
||||
declares none; a merge into the repository was seen and pulled within seconds.
|
||||
|
||||
## What this does not settle
|
||||
|
||||
- Ranking or meaning. A search that understands a question is the session's job, not the reader's.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
status: located
|
||||
status: resolved
|
||||
opened: 2026-08-23
|
||||
located-in: [mesh-catalog modules/records, mesh-catalog modules/mesh-console]
|
||||
fixed-by:
|
||||
amended-design: 02-DECISIONS/0025-the-design-record-is-read-not-copied.md
|
||||
fixed-by: ADR 0153; mesh-catalog PR 183 (records), PR 185 (a phrase that wraps)
|
||||
amended-design: 03-DESIGN/01-to-be/35-reading-the-record.md
|
||||
---
|
||||
|
||||
# 006 — This repository is not indexed into the knowledge base, and the claim that it is holds up a decision
|
||||
@@ -193,3 +193,16 @@ forge and answering `records_search`, `records_read`, `records_list`, `records_s
|
||||
[35 — Reading the record](../../03-DESIGN/01-to-be/35-reading-the-record.md). The module's test runs
|
||||
0025's check against a repository it makes; this record closes when the same check passes through the
|
||||
console on the live mesh, and says so below.
|
||||
|
||||
## Resolved, 2026-09-30
|
||||
|
||||
The check ADR 0025 names passed on the live mesh: through the console on a workstation,
|
||||
`records_search` for a phrase that appears in one design document here returned that document and the
|
||||
commit it was read at, from a checkout the mesh keeps and nobody copied. What this record asked on
|
||||
2026-08-23 — *does the searcher find it without already suspecting it exists?* — is answered by where
|
||||
the tool sits: in the same list as the forge's and the mesh's own, described as the thing to search
|
||||
before forming a hypothesis. Reachable became surfacing when the surface became a list.
|
||||
|
||||
Open beside it, and not this record's: the mesh has no symptom-indexed memory at all since the
|
||||
cut-over ([as-is 07](../../03-DESIGN/00-as-is/07-knowledge.md) says so), and the lessons of these
|
||||
days are in this repository by hand.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
status: located
|
||||
status: resolved
|
||||
opened: 2026-09-26
|
||||
located-in: [mesh-controller internal/catalogue/declaration.go, mesh-catalog modules/keycloak, mesh-catalog modules/minio, mesh-catalog modules/nextcloud]
|
||||
fixed-by:
|
||||
amended-design:
|
||||
fixed-by: mesh-controller PR 149 (a module is told the name it is served under), PR 169 (an operator's value, a context on the seat); mesh-catalog PR 188; ADR 0155
|
||||
amended-design: 03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md
|
||||
---
|
||||
|
||||
# 122 — A module cannot ask for its own public name, so three manifests wrote this mesh's names into the catalogue
|
||||
@@ -114,3 +114,18 @@ particular to one installation, and also has nowhere to live but the definition.
|
||||
ADR 0112 would put it), and if so what reads it — the provisioner, or the module's own values?
|
||||
- What check would notice the next one? A definition naming a public domain is detectable in the
|
||||
shape of the value, which is more than nothing, and less than a rule.
|
||||
|
||||
## Resolved, 2026-09-30
|
||||
|
||||
The open questions, answered in order. **A module names what it will be reached at** through the
|
||||
binding of the route it contributes: `${bound:route:name}`, or `:name-<local>` for several
|
||||
contributions, is the composed public name; `:internal-name` the private one (controller PR 149). The
|
||||
identity provider, the object store's console and the automation tool's webhook now read it there.
|
||||
**The scheme is the module's**, because it is true of the route the proxy terminates and every instance
|
||||
wrote `https://` in front of the name. **An adopted resource's name is a setting** on the assignment;
|
||||
so is every other operator's value — a mail domain, a site name, the address a proxy forwards from —
|
||||
as `${setting:<key>}` in the file the software reads
|
||||
([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)).
|
||||
**The check that notices the next one** is the shape of the value, as this record guessed it would be,
|
||||
and it is more than nothing: it found forty-two, and the catalogue passes it now
|
||||
([issue 134](../134-a-definition-may-still-name-the-mesh/00-report.md)).
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
status: open
|
||||
status: resolved
|
||||
opened: 2026-09-28
|
||||
located-in: [mesh-catalog, mesh-controller internal/catalogue]
|
||||
fixed-by:
|
||||
amended-design:
|
||||
fixed-by: mesh-controller PR 169 (the check, module check, the catalogue-wide test); mesh-catalog PR 188 (the catalogue that passes it); ADR 0155
|
||||
amended-design: 03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md
|
||||
---
|
||||
|
||||
# 134 — A definition may still name the mesh, and the check that would say so does not exist
|
||||
@@ -64,3 +64,17 @@ that.
|
||||
hostnames above are the first real cases.
|
||||
- Should a build context name a repository on the git seat rather than by URL, and if so, what does
|
||||
that mean for a context in *another* mesh's forge?
|
||||
|
||||
## Resolved, 2026-09-30
|
||||
|
||||
The check exists: `InstallationProblems`, run by `module check` and by a catalogue-wide test. Run over
|
||||
the 77 definitions it found 42 values, not 15 — the by-hand count had missed a second name one
|
||||
character after the first on the same line, which is the kind of thing a check is for. The three open
|
||||
questions: **a domain in a `why` string does not break the rule**, prose is not judged, and the eight
|
||||
were rewritten anyway because this catalogue is public; **a service's public name is the name the mesh
|
||||
composes for its route**, read through the route's binding, and an operator's own value is a setting;
|
||||
**a build context names a repository on the git seat**, `seat: git` with the path, and a context in
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user