Issue 009 (fixed) + Issue 008 (resolved via ADR 0053) — module-runtime config & provider contract #21
@@ -0,0 +1,98 @@
|
||||
---
|
||||
status: accepted
|
||||
date: 2026-09-03
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
supersedes: 0017-modules-outside-the-core-are-grouped-by-domain.md
|
||||
extends: 0002-everything-is-a-module.md
|
||||
---
|
||||
|
||||
# 45. What a module is
|
||||
|
||||
## Context
|
||||
|
||||
[ADR 0002](0002-everything-is-a-module.md) settled that everything is a module, but never said what a
|
||||
module *is* beyond "a directory the mesh processes." That gap let the catalogue's breadth read as a
|
||||
smell: a module can carry a container, a built image, tools, a provisioner, migrations, health,
|
||||
config, seat claims, requires and provides — so much that the unit seemed ill-defined.
|
||||
[ADR 0017](0017-modules-outside-the-core-are-grouped-by-domain.md) tried to organise modules by
|
||||
domain, which is the wrong axis. This record states what a module is, drawn from the cases that
|
||||
stress-tested it: the shell, i3-vs-sway, umami, and "database."
|
||||
|
||||
## Decision
|
||||
|
||||
**A module is one self-contained piece of software the mesh installs and manages** — everything
|
||||
needed to make that one thing real and integrable: what runs, the seats it claims, what it provides
|
||||
to other modules, what it requires from them, and what operates it.
|
||||
|
||||
The **software is the module's identity.** Capabilities, seats and provisioned resources are the
|
||||
**relationships *between* modules**, not what a module is — and that is what binds a module into one
|
||||
thing. umami is bound by *being umami*: its container runs umami, its provisioner creates umami sites,
|
||||
its tools query umami, its `requires` gets umami a database. Every feature serves the one software.
|
||||
|
||||
### The three relationships
|
||||
|
||||
1. **Shared seat** — several modules fulfil a capability and coexist; one may be default. bash, zsh
|
||||
and fish all join `shell`.
|
||||
2. **Exclusive seat** — modules contend for a single slot; one holds it. i3 (needs x11) and sway
|
||||
(needs wayland) contend for `display-session`.
|
||||
3. **Provide / require** — a provider ships the **provisioner** that creates instances of the
|
||||
resource it offers and returns sealed credentials; a consumer requires it and the mesh wires the
|
||||
credential in. Symmetric: umami requires a database *and* provides analytics.
|
||||
|
||||
### Interfaces are mesh-owned; providers adapt to them
|
||||
|
||||
The mesh **defines the interface** for a capability — the provider-neutral contract of what a
|
||||
consumer receives and how it integrates. Both sides conform: a provider's provisioner **adapts** its
|
||||
software's real API to the mesh contract; a consumer depends on the **interface**, never on a
|
||||
provider. Swap one provider for another and the consumer does not change.
|
||||
|
||||
### The naming rule — draw the interface at the consumer's real coupling
|
||||
|
||||
Name a `provides`/`requires` at the **widest boundary across which the consumer genuinely does not
|
||||
care which implementation serves it**:
|
||||
|
||||
- Where the consumer's coupling is thin — an analytics embed snippet and dashboard, opaque to it —
|
||||
the mesh defines a neutral interface (`analytics`) and providers (umami, amumi) adapt. Swappable
|
||||
across vendors.
|
||||
- Where the consumer **speaks a protocol** — a database's wire protocol and query dialect — the
|
||||
interface *is* the protocol: `postgres-database`, `mssql-database`, `mongodb-database`. Swappable
|
||||
only among protocol-compatible implementations, **never across**, because the application cannot
|
||||
cross it either. "database" is not a capability; the protocol is.
|
||||
- **Never false genericity.** A name must not promise a swap the contract cannot deliver
|
||||
([research 005](../01-RESEARCH/005-domain-grouping/analysis.md)).
|
||||
|
||||
This is [ADR 0027](0027-the-product-is-novox-mesh.md)'s rule made general — "names the protocol, not
|
||||
the product; a database names the engine because the app targets it" — with the reason stated: the
|
||||
contract sits where the coupling is.
|
||||
|
||||
### What is not a module
|
||||
|
||||
- A **library** (built against, never deployed — [ADR 0044](0044-what-the-sdk-holds-and-refuses.md)).
|
||||
- A **control-plane context** (the mesh itself — [ADR 0015](0015-mesh-brokers-nodes-host-agents-think.md)).
|
||||
|
||||
A **swappable machine mechanism** (a firewall — ufw, nftables) *is* a module implementing a
|
||||
capability. The host hardcodes no firewall, supervisor, package manager or runtime; it owns only the
|
||||
generic apply primitives and platform detection, so it runs where none of those exist — an Android
|
||||
phone has no ufw, systemd, pacman or Docker.
|
||||
|
||||
## Consequences
|
||||
|
||||
- **Supersedes [ADR 0017](0017-modules-outside-the-core-are-grouped-by-domain.md).** Modules are
|
||||
organised by their relationships (seats, provisions), not grouped into domain folders.
|
||||
- **Refines [ADR 0002](0002-everything-is-a-module.md).** Everything the mesh runs and integrates is
|
||||
a module — but a module is defined by the *software it delivers*, not by being a bucket of features.
|
||||
- The target is a **self-fulfilling mesh**: declared wants bound to swappable modules, provisioners
|
||||
wiring credentials, nothing hardcoded. The control plane's whole job is the binding.
|
||||
- Converting a module from the old system includes pulling its per-module code out of the shared SDK
|
||||
([ADR 0044](0044-what-the-sdk-holds-and-refuses.md)) and shipping its provisioner as an adapter to a
|
||||
mesh interface — a de-coupling, not just a move.
|
||||
|
||||
## References
|
||||
|
||||
- [ADR 0002](0002-everything-is-a-module.md) — everything is a module; this says what one is.
|
||||
- [ADR 0015](0015-mesh-brokers-nodes-host-agents-think.md) — contexts are the mesh, not modules.
|
||||
- [ADR 0017](0017-modules-outside-the-core-are-grouped-by-domain.md) — superseded.
|
||||
- [ADR 0027](0027-the-product-is-novox-mesh.md) — protocol-not-product, generalised here.
|
||||
- [ADR 0044](0044-what-the-sdk-holds-and-refuses.md) — per-module code lives in the module.
|
||||
- [research 011](../01-RESEARCH/011-the-module-graph/00-overview.md) — the graph of these relationships.
|
||||
Reference in New Issue
Block a user