Files
hq/01-RESEARCH/011-the-module-graph/00-overview.md
T
jschoubben 902739acb6 Research 011 — the module graph
The proposal to split modules into provisioning services and applications was
worked through and abandoned, for a reason worth keeping: it cannot be filed
consistently. A git forge is consumed as a service and operated through a web
interface; an analytics service grants tracking identity and is a dashboard.

The operator's correction is the sharper form — what runs on the machine is a
supervised container, not something a user started. That is a fact about HOW a
thing runs, not about what kind of thing it is. So it is a facet, and 0002
survives: everything is a module.

What the catalogue is missing is not a taxonomy but a graph. Grouping asserts
relationships; a graph records them. Five declarations, of which two exist:
requires/provides a resource (yes), requires/excludes another module (no),
requires a node capability (no). Plus interface modules that carry no
implementation, with adapters providing them.

Recorded because it matters: this is a package manager's model, and pacman
already has all of it — depends, conflicts, and provides as virtual packages,
which is exactly the interface/adapter idea. Arriving there independently is
evidence for the shape. It is also a warning about what not to reimplement.

Working position on capabilities, to be tested: intrinsic ones (hardware,
architecture, network position) are detected and never installed, and a module
requiring one it lacks is impossible rather than unresolved. Provided ones (a
display server, a container runtime) are not a separate kind of thing — they are
modules that provide a capability, so "may the mesh install a capability" is not
policy, it is dependency resolution. Issue 007 then bears directly: an installed
package is not a capability.

Also captured: the operator's assessment that the machinery around a module —
scheduled tasks, hooks, migrations, config and env — is worth keeping, seeds are
not, and the integration is wrong enough to need a major refactor. Research 005
found supporting evidence from another direction, that the densest apparent
coupling in the catalogue is manifest boilerplate churn.

The first open question is the one that decides whether this is progress: what
does the graph DELETE? If modules gain declarations and lose nothing, it is
motion.
2026-08-25 23:19:57 +02:00

7.7 KiB

status, initiated, touches
status initiated touches
active 2026-08-25
02-DECISIONS/0002-everything-is-a-module.md
02-DECISIONS/0017-modules-outside-the-core-are-grouped-by-domain.md
02-DECISIONS/0036-a-node-is-a-managed-machine.md
03-DESIGN/00-as-is/02-modules-and-manifests.md
03-DESIGN/00-as-is/10-module-catalogue.md
04-ISSUES/007-an-installed-package-is-not-a-capability/00-report.md

011 — The module graph

What is being investigated

Whether the catalogue's missing structure is a graph — modules declaring what they need, what they offer, and what they exclude — and what that replaces.

ADR 0017 proposes grouping modules by domain. Research 005 measured that proposal and found its evidence holds in exactly one place — reachability — which ADR 0037 has since absorbed into the host. The measured case for domain grouping has therefore been consumed by a decision taken for unrelated reasons, and what remains is fifty modules that co-change with nothing.

That leaves the original complaint unanswered: the catalogue records what was installed rather than what anything is for, and nothing in the system can see a relationship between two modules. Grouping asserts relationships. A graph records them.

Why now

A proposal to split modules into provisioning services and applications was worked through and abandoned in favour of one concept with facets, for a reason worth keeping:

  • The split cannot be filed consistently. A git forge is consumed as a service and operated through a web interface. An analytics service grants tracking identity and is a dashboard somebody reads. An identity provider grants authentication and has an admin console.
  • The operator's correction is the sharper form: what runs on the machine is a supervised container, not something a user started. That is a fact about how a thing runs, not about what kind of thing it is — so it is a facet, not a taxonomy.

Filing decisions that follow from nothing are the disease research 005 measured. A second taxonomy would reproduce it.

So ADR 0002 survives, and the question becomes what a module must be able to declare.

The shape being investigated

Five declarations, of which two exist today.

Declaration Today Notes
requires a resource — a database, a bucket yes provisioning, ADR 0005
provides a resource yes as above
requires another module no the dependency edge — the graph's substance
excludes another module no installing A makes B unavailable
requires a node capability no a graphical session, a container runtime, an architecture

And one structural idea on top: an interface module carries no implementation. Adapters provide it. An assistant interface with several model-provider adapters; a terminal interface with several terminal adapters. A dependent names the interface and never an implementation.

Prior art to measure against, not invent past. This is a package manager's model, and the platform's own package manager already has all of it: depends is the dependency edge, conflicts is exclusion, and provides is the interface — several packages provide one virtual name, and a dependent names the virtual one. That the design arrived at the same shape independently is evidence for it. It is also a warning: dependency resolution, version constraints, conflict handling and rollback are a long-solved and easily-botched problem, and the effort should establish what to delegate rather than reimplement.

Capabilities, and what may be installed

The operator's formulation: system specs are capabilities, and capabilities unlock installable modules — you cannot install a graphical application on a node with no display server.

The question that follows is whether the mesh may install a capability. The effort's working position, to be tested:

  • Intrinsic capabilities — hardware, architecture, network position — are facts about a machine. They are detected, never installed, and a module requiring one it does not have is not unresolved but impossible on that node.
  • Provided capabilities — a display server, a container runtime — are not a separate kind of thing at all. They are modules that provide a capability, and requiring one is an ordinary edge the graph resolves by installing it.

If that holds, "may the mesh install a capability" is not a policy question. It is dependency resolution, and the only genuinely new thing is detection.

Which is where issue 007 bears directly: an installed package is not a capability. A provided capability is not real because a package is present — it is real when it is present, running and working, and the difference is exactly the class of fault this repository keeps recording.

What it touches beyond the catalogue

The operator's assessment is that the machinery around a module is wanted and its integration is not: scheduled tasks, hooks, migrations, configuration and environment settings are worth keeping; seeds are not; and the current integration is wrong enough to need a major refactor.

That is a claim to test rather than adopt. Research 005 already found supporting evidence from a different direction — that the densest apparent coupling in the catalogue is manifest boilerplate churn, cross-cutting changes to the machinery applied N times — which is what an integration being wrong looks like from the outside.

Open questions

Question Why it is open
What does the graph delete? If modules gain declarations and lose nothing, this is motion rather than progress. The effort has not finished until it names what stops existing.
Where does resolution happen — mesh or platform package manager? The mesh must model mesh-level edges. Whether it also resolves operating-system packages, or delegates, decides whether a solver has to be written.
Is an interface a module, or a name? Arch makes it a name that packages claim. Making it a module gives it a manifest, an owner and a place to document the contract — and a thing with no implementation to install.
What does an exclusion mean for something already installed? Refuse the install, or make the conflict visible and let it be decided. The second is a policy surface; the first is a package manager.
Does node adoption scan for capabilities, applications, or both? The operator proposes scanning an adopted node and enabling what it finds. Under the working position above, the scan is for capabilities — but a machine with a terminal already installed is also a module already satisfied, and whether that is adoption or drift is undecided.
One installation image, or several? Proposed: pre-built images carrying different capability sets, so a machine is adopted quickly. Several images bake capability sets at image time, which is the filing problem in a new form and reintroduces what detection exists to avoid. One image carrying the host and nothing else is ADR 0038's one binary installed by hand, automated. The effort should settle which.
What happens to domain grouping? ADR 0017 is still proposed. If the graph is the answer, 0017 is superseded rather than narrowed — its text is never edited.