diff --git a/02-DECISIONS/0037-where-a-module-lives.md b/02-DECISIONS/0037-where-a-module-lives.md new file mode 100644 index 0000000..a697d12 --- /dev/null +++ b/02-DECISIONS/0037-where-a-module-lives.md @@ -0,0 +1,103 @@ +--- +topic: building it +status: proposed +date: 2026-09-01 +deciders: jochen +reconstructed: false +rests-on: 02-DECISIONS/0009-modules-and-the-graph.md +--- + +# 37. Where a module lives + +## The question + +The mesh's own module descriptions currently sit in `examples/` inside the control plane, beside +the small programs that hand out logins. That was fine while there were three of them. It is +wrong now, and the name is doing active harm: everything in `examples/` reads as a sketch, and one +of them shipped naming a container image that nothing in the repository builds. A directory called +*the catalogue* would have made *does this actually work* the obvious question to ask of it. + +So: **one repository holding the modules we ship?** And if so, where does everything that is not +ours go? + +## What a module actually is, counted + +The system being replaced has **126 modules** on its main branch. The shape of them is the whole +argument, so it is measured rather than assumed: + +| | count | what it is | +|---|---|---| +| **the module is software** | 47 | its own source tree lives inside the module — a daemon, a service, a library | +| **helper scripts only** | 44 | no application of its own; scripts it runs at install time or offers to an agent | +| **a description and nothing else** | 35 | a package to install and some files to write | + +**Two thirds of modules contain code.** The largest is a shared library of 182 source files. A +speech-capture module carries a complete daemon — audio capture, mixing, transcription, a model +runner. Treating a module as *a description of something else* is true of barely a quarter of them. + +That kills the simplest answer. A catalogue cannot be "a folder of manifests" when most modules +are programs. + +## The four kinds, which want different homes + +**1. What the mesh is made of.** The control plane, the host, the shared library, the board. +These are not modules that happen to be ours; they are the mesh, expressed as modules so it can +install itself. They belong in the repositories that build them, which already exist. + +**2. Something the world made, that we describe.** A forge, a mail system, an identity provider, +a media server. Nobody upstream ships a description; somebody has to write one, and it is the same +description for everybody who runs it. **This is what a catalogue is for.** It is also where the +small programs that create accounts belong, because such a program is part of describing that +service, not part of the mesh. + +**3. Something we wrote, that runs somewhere.** An application, a site, a side project. The +description belongs **with the code, at the root of its own repository**, because the two change in +the same commit. A repository that gains an environment variable and a description that gains it +elsewhere will drift, and there is no mechanism that could stop it. This is already how it works +and it should stay that way. + +**4. A package and some files.** A tool, a font, a shell. Thirty-five of these, and each is a few +lines. The catalogue. + +## The proposal + +**A `mesh-catalog` repository** holding kinds 2 and 4: descriptions of software we did not write, +and the programs that provision it. Not kind 1, which is the mesh itself. Not kind 3, which lives +with its own code. + +**The mesh's list of modules is not this repository.** It is a table in the control plane, filled +by adding a description to a running mesh. The catalogue is a *source* to add from — one of +several, and the mesh already records which: every module carries where it came from, the branch +followed there, and the commit its description was read at. **Nothing needs inventing to support +modules from anywhere**; a repository of our own is simply the source we curate. + +**A description is checked by the tool, not by a test that imports the tool.** Today a test in the +control plane parses the example manifests by reaching into the control plane's internals, and +another reads the control plane's own build file to check every image a module names can be built. +Two jobs tangled. A `module check` command on the control plane's binary would let the catalogue +hold data validated from outside, and would give the same check to somebody describing their own +application in their own repository — which is the case that matters most and currently has no +check at all. + +## What this costs, and the argument against + +**It is early.** Ten modules exist, four of them ours. Moving ten files is a morning; moving a +hundred is a week — but the hundred is not here yet, and splitting now adds a second repository to +release across before there is anything to release. + +The counter is that the tangle is already producing faults rather than merely threatening to. A +manifest naming an unbuildable image, and a test reading a build file two directories up, are both +symptoms of one repository doing two jobs. And the moment the first module is adopted on a real +machine, the descriptions stop being examples and become the thing deployments come from. **That +is the moment this becomes urgent, and it is close.** + +## What it does not settle + +**Where a provisioning program's image is published**, and how a description pins it. A description +names an image by digest; the image is built from the catalogue; the catalogue must therefore both +produce an image and refer to it, which is the same knot the bootstrap has and solves by writing +the digest down after building. + +**Whether kind 4 deserves a module at all.** Thirty-five descriptions that say *install this and +write these files* may be better as one module with settings than as thirty-five modules. Left +open deliberately; it is a question about the shape of the catalogue, not about whether to have one.