From e898a3ec4405b285281c810e70e26c93554c592f Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 4 Sep 2026 21:05:55 +0200 Subject: [PATCH] =?UTF-8?q?ADR=200051=20=E2=80=94=20a=20module's=20configu?= =?UTF-8?q?ration=20is=20its=20assignment's,=20not=20its=20manifest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A module is assigned to a node (there is no mesh assignment; 'mesh' is a scope). The manifest is what the module IS, plus defaults; the configurable values are settings, carried by the assignment — per-node or mesh-wide, applied at resolution, changeable live (what a meshboard edits). Extends settings from a config file's content to the manifest fields marked settable: foremost listens.from (postgres from:mesh by default, from:anywhere per node — the firewall follows), and a provider's own config (a registrar's zone/domain/ ingress). Static config in a manifest is config in the wrong place: it cannot vary per node and cannot change without a rebuild. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF --- ...ion-is-its-assignments-not-its-manifest.md | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 02-DECISIONS/0051-a-module-configuration-is-its-assignments-not-its-manifest.md diff --git a/02-DECISIONS/0051-a-module-configuration-is-its-assignments-not-its-manifest.md b/02-DECISIONS/0051-a-module-configuration-is-its-assignments-not-its-manifest.md new file mode 100644 index 0000000..f0ffa13 --- /dev/null +++ b/02-DECISIONS/0051-a-module-configuration-is-its-assignments-not-its-manifest.md @@ -0,0 +1,87 @@ +--- +status: proposed +date: 2026-09-04 +deciders: jochen +reconstructed: false +extends: 0005-capabilities-are-provisioned-on-declaration.md +--- + +# 51. A module's configuration is its assignment's, not its manifest's + +## Context + +A module is assigned to a node — `assign `, always to a machine; there is no +assignment to the mesh. "Mesh" is a *scope*, not a place: a `provides` or a `claim` scoped `mesh` +reaches the whole mesh, but the module still runs on a node. So the two kinds of thing a module can +carry are the manifest (what the module *is*) and, separately, what it should do *here* — which +differs by deployment and by node. + +The mesh already has the second: **settings**. `settings set [--node ]` — with a node +it is that machine's, without it the whole mesh's — layered over what the module declares and applied +at resolution, changeable without editing the module and without a rebuild. That is the surface a +meshboard would edit. + +But settings today reach only a module's **config-file content** (a mergeable file the module owns). +Configuration that is not a file has been landing in the manifest instead, statically — a registrar's +zone and domain, the address public names point at, and, most sharply, `listens.from`. That last one +is the tell: whether a port is open to the private overlay or to the public internet is a +*per-node deployment choice* — the same database internal on one machine and public on another — and +a value fixed in the manifest is one value for every machine, so it cannot be. Static configuration in +the manifest is configuration in the wrong place: it cannot vary per node, and it cannot change +without a new module version. + +## Decision + +### The manifest is identity and defaults; the assignment's settings are the configuration + +A module's manifest declares what it is — what it provides, requires and claims, the shape of its +resources — and, for anything configurable, a **default**. The values that make a running instance +*this* instance are settings, carried by the assignment: per-node, or mesh-wide when no node is named, +applied over the defaults at resolution. Change one and the next reconcile carries it; nothing is +edited on a machine and nothing is rebuilt. + +### Settings drive the configurable fields the manifest marks, not only file content + +Settings extend beyond a config file's content to the manifest fields a module declares settable — +foremost: + +- **`listens.from`**: a module declares its safe default (`from: mesh`), and a per-node setting + raises or lowers it. postgres declares `listens: [{ port: 5432, from: mesh }]`; on the machine that + should expose it, a setting makes that port `from: anywhere`. Same module, different exposure, and + the firewall ([ADR 0050](0050-a-machine-firewall-is-the-sum-of-what-it-listens-on.md)) is computed + from the effective value, so the packet filter follows the setting. +- **A provider's own configuration**: a registrar's zone, domain and the ingress its names point at + ([ADR 0049](0049-a-public-name-is-provisioned-like-any-capability.md)) are mesh-wide settings, not + manifest constants — one mesh's Cloudflare zone is not another's, and the module description is the + same for both. + +### Unset is the default, and an unknown setting is refused + +A field with no setting keeps the manifest's default, so a module runs correctly configured by nobody. +A setting that matches no settable field — like a config value that reaches no file today — is named, +not silently dropped, so a misspelled setting is found rather than believed (the discipline of +`UnusedSettings`, and of [ADR 0048](0048-a-module-broker-account-is-scoped-by-emits-and-consumes.md): +a declaration is enforced or it is a comment). + +## Consequences + +- The postgres case works: one module, `from: mesh` by default, `from: anywhere` where a setting says + so — internal on ace, public on novox, changeable live. +- Provider modules stop carrying a mesh's specifics: `cloudflare-dns` describes *a Cloudflare + registrar*, and *which* zone and ingress is a setting, so the same module serves every mesh. +- Configuration becomes a thing a meshboard manages — set per node or mesh-wide, applied on the next + reconcile — rather than a manifest edit and a rebuild ([ADR 0004](0004-managed-files-are-generated-never-edited.md): + the way you change a managed thing is not by editing it). +- What a manifest may not do is grow a value that differs per machine; if it differs per machine it is + a setting, and the manifest holds only the default. + +## References + +- [ADR 0005](0005-capabilities-are-provisioned-on-declaration.md) — what is provisioned on + declaration; its per-instance values are the assignment's. +- [ADR 0004](0004-managed-files-are-generated-never-edited.md) — a managed thing is changed through + the mesh, not by editing it; settings are that, for configuration. +- [ADR 0050](0050-a-machine-firewall-is-the-sum-of-what-it-listens-on.md) — the firewall follows the + effective `listens.from`, so making `from` a setting makes exposure a setting. +- [ADR 0049](0049-a-public-name-is-provisioned-like-any-capability.md) — the provider whose zone and + ingress are settings, not manifest constants.