--- 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.