--- status: accepted date: 2026-08-26 deciders: jochen reconstructed: false supersedes: 0017-modules-outside-the-core-are-grouped-by-domain.md --- # 44. A module declares presence, instantiation and exclusion ## Context [Research 011](../01-RESEARCH/011-the-module-graph/00-overview.md) set out to find the catalogue's missing structure. It opened with *what does a graph delete?* and the answer for the existing system was **nothing — it is already there**: 126 manifests, 103 edges, no cycles, nothing dangling, and a resolver already used for build, load and install order. So the work became a design question rather than a discovery one, worked through twenty cases and one provider in full. ## Decision ### Two kinds of edge, one graph **Presence** — the thing must exist and be reachable. Nothing is created, nothing flows back. *An editor requires a terminal. A dashboard requires a container runtime.* **Instantiation** — a provider is asked to make something *for this consumer*, and hands back what the consumer needs to use it. *A game requires a database from the store, and receives one, with credentials.* Instantiation implies presence; presence does not imply instantiation. They differ in whether something is created, whether a payload returns, whether it can be revoked, and whether the provider holds state about it — which is too much to collapse into one relation for tidiness. ### Names are concrete unless providers are genuinely substitutable A requirement names either a **concrete** thing — that module and no other — or an **abstract** name satisfied by whatever provides it. **An abstract name is legitimate only where a consumer can be switched between providers without changing.** `terminal` passes. `database` fails: a consumer speaking one store's protocol does not speak another's, so the name would promise what no provider delivers and the resolver would report a requirement satisfied that is not. **The adapter is what creates an interface.** A name has several providers *and a contract they all satisfy*, or it is not an interface. Nothing declares itself to be one. **Where there is no contract there is a tag.** *Database* remains a useful word for finding things and grouping them in a catalogue. Tags describe; edges bind; keeping them apart is what stops a second relationship appearing that looks like a dependency and is not. ### Exclusion is the third relation, and it is not derivable `excludes` names what cannot coexist with this. Two modules providing one name look interchangeable, and two things wanting one port look independent, right up until installing the second breaks the first. ### A node provides names too A node's profile is a set of provided names — a display server, a container runtime, an architecture. A module requiring one is satisfied by **the node**, exactly as one requiring a store is satisfied by another module. **So capability checking is not a separate mechanism.** There is one question — *is this name provided by anything available here?* — and a graphical application cannot land on a node without a display server for the same reason, through the same code, that it cannot land without its dependencies. This makes the host's capability detection an **input to resolution** rather than a report for a person. And what a node provides is partly *derived*: installing a container runtime makes the node provide `container-runtime` thereafter. ### Constraints, never placement A module says what must be true of a node and never which node. Which node runs what is an inventory decision, and today's catalogue decides it in manifests — a module pinning its store to a named node, so a second node cannot provide it without editing the consumer. ### Scope decides which provider, and the binding is written down A consumer of an instantiation edge declares the **scope of its own need**: one instance shared across every instance of itself, or one each. That decides without naming a node — a shared need cannot be met by something each node runs separately. The mesh then binds, and **the binding is recorded and sticky**. Not recomputed: a resolver that re-derives which store serves a consumer will one day derive a different answer and relocate a database. **Where it is recorded follows the scope** — a shared grant belongs to the module, a per-instance grant to the assignment. ### A module, a node, and an assignment Three entities. The assignment carries what belongs to neither end: where state lives, how it is reached, configuration derived from the hardware, and which provider instance serves this consumer. Recorded because the alternative was tried: modules were once node-agnostic, and it did not survive — there was nowhere for these to live. ## Consequences - **[ADR 0017](0017-modules-outside-the-core-are-grouped-by-domain.md) is superseded.** Folders assert relationships; edges record them. What grouping was for — finding things, seeing what belongs together — is a tag and a query over the graph, neither of which anybody keeps true by hand. The domain module goes with it: there is no `networking` thing to install, there are concrete modules named individually. - **Provider stops being a category**, as service and application already had. Any hosted thing can be a factory — an identity provider grants clients, a mail server grants mailboxes. It is a facet, not a kind. - **`excludes` and node capabilities do not exist in any manifest today**, so this adds declarations rather than removing them. What it removes is listed in [ADR 0045](0045-a-context-owns-its-store.md), which is the other half of this design. - **A resolver is still needed**, with version constraints and conflicts. What it delegates to the platform's package manager rather than reimplementing is **not decided here**. - **Two axes remain undeclarable**: how many instances a module should have — not derivable, and opposite for a broker and a store — and what a provider hands back, which differs between credentials and a command. ## References - [Research 011](../01-RESEARCH/011-the-module-graph/00-overview.md) — the measurement, the cases, the worked provider, and what happens to `feature`. - [ADR 0045](0045-a-context-owns-its-store.md) — the ownership half. - [ADR 0043](0043-a-declaration-is-an-ordered-list-of-owned-resources.md) — what the graph produces for a node. - [ADR 0002](0002-everything-is-a-module.md) — survives; this says what a module declares.