Files
hq/02-DECISIONS/0002-everything-is-a-module.md
T
jschoubben c0b35652d0 The numbering is the flow: decisions are 02, design is 03
papa-hq reads 01 research -> 03 decision -> 02 design. The order is a
scar, not a choice: 02-DESIGN existed from its initial commit, and when
adr/ was finally promoted on 2026-07-13 it took the next free number
rather than its place in the sequence. By then design was too settled to
renumber.

hal-hq was three commits old, so it is not. adr/ becomes 02-DECISIONS and
02-DESIGN becomes 03-DESIGN, and following the folder numbers now walks
the process in the order it happens: research produces a decision, the
decision authorises a design.

00-GENESIS becomes 00-META, matching papa's rename from the same
restructure.

Every path reference rewritten across documents, frontmatter, playbooks
and skills. All links resolve; all 58 frontmatter blocks parse and their
path fields still point at files that exist.
2026-08-23 18:05:11 +02:00

3.3 KiB

status, date, deciders, reconstructed
status date deciders reconstructed
accepted 2026-03-14 jochen true

2. Everything is a module, and one manifest describes all of them

Reconstructed after the fact from the evidence cited below.

Context

The mesh carries several kinds of thing: containerised services with data and ports, pure capability providers with no service at all, and bare markers whose only content is that a node has them. Before this decision these were separate concepts with separate handling — the earlier vocabulary was capabilities, and services were installed by a different path than tools.

Every distinct kind of thing needs its own install path, its own change detection, its own place in the delivery pipeline, and its own documentation. Three kinds means three of each, and every new feature has to be built three times or, more commonly, once — leaving two kinds quietly unsupported.

Considered options

  1. Separate concepts per kind — a service registry, a tool registry, a node feature flag list. Rejected: it is what existed, and the cost was paid in every cross-cutting change.
  2. One manifest, kind inferred from directory contents. Chosen.
  3. One manifest with an explicit type: field on every module. Partly adopted — a service still declares itself — but the general rule became inference, because a declared list and the directory it describes drift, and the directory is the one that is true.

Decision

Everything the mesh installs is a module: a directory with a manifest. The manifest declares identity, environment variables, what the module provides, what it requires, and how it is exposed. What kind of module it is follows from what the directory contains:

Contains Is
a compose definition a service
a tools directory a capability provider
a daemon or unit directory a long-running process
a configs directory a source of managed files
nothing but a manifest a flag — presence is the whole content

A module may be several of these at once. Each is a feature, and the delivery pipeline addresses features, not modules.

The mesh's own components are modules on exactly these terms. They get no privileged install path, no separate registry, and no exemption from the pipeline.

Consequences

  • One mechanism to learn, one to document, one to fix. A pipeline improvement reaches everything the mesh carries.
  • Dogfooding stops being a discipline and becomes structural: if the mesh's own components need an exception, the machinery is unfinished, and that is visible immediately.
  • Feature detection from directory contents means a directory rename silently changes what a module is. This has bitten repeatedly — a hook named for a feature the module does not have is skipped without complaint.
  • The manifest becomes load-bearing and grows. It is now the largest single point of coupling in the mesh.

References

  • Rename capabilities → modules across the entire codebase, 2026-03-14.
  • Merge fail2ban, ufw, firewall apps into modules, 2026-03-15 — the first modules to arrive by conversion rather than by creation.
  • Knowledge base: modules, modules/manifest-reference, conventions/modules.
  • The rename-breaks-detection shape: troubleshooting/hooks-named-for-missing-feature, troubleshooting/health-check-tools-index-false-positive.