Files
hq/02-DECISIONS/0046-a-module-configuration-is-its-assignments-not-its-manifest.md
T

5.2 KiB

topic, status, date, deciders, reconstructed, extends
topic status date deciders reconstructed extends
what runs on it accepted 2026-09-04 jochen false 0027-a-provision-names-what-the-consumer-is-coupled-to.md

46. A module's configuration is its assignment's, not its manifest's

Context

A module is assigned to a node — assign <node> <module>, 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 <module> [--node <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 0045) 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 0044) 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 0043: 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 0011: 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 0027 — what is provisioned on declaration; its per-instance values are the assignment's.
  • ADR 0011 — a managed thing is changed through the mesh, not by editing it; settings are that, for configuration.
  • ADR 0045 — the firewall follows the effective listens.from, so making from a setting makes exposure a setting.
  • ADR 0044 — the provider whose zone and ingress are settings, not manifest constants.