Issue 009 (fixed) + Issue 008 (resolved via ADR 0053) — module-runtime config & provider contract #21
@@ -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 <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 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.
|
||||
Reference in New Issue
Block a user