From ae099482a9e18c1987df1f32534b2b1791c6d978 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 26 Aug 2026 22:48:20 +0200 Subject: [PATCH] 011: twenty cases, and two axes nothing covers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before settling a schema, what a module can actually be. Twenty kinds of thing, with the hard ones at the end because they are the point. The ordinary nine are unsurprising: a supervised service, a system package with configuration, an application a person launches, a command-line tool, a library that never runs, a one-shot task, a scheduled one, an adapter, and a standalone application whose only difference is where its source lives. The eleven that break a naive schema are where the work is. Something that is a service AND an application — a git forge is consumed as a remote and operated through a web interface, and neither reading is wrong. Something that provides and consumes, because provider and consumer are ends of edges rather than kinds of module. Something the mesh installs that then becomes a node CAPABILITY, which means a node's provides-list is partly derived from what is installed on it and not only detected. Something that must be adopted rather than installed. Something that is a set rather than a thing. Something with exactly one instance for the whole mesh, where assigning it twice is not redundancy but two meshes. Something that is not software at all — a firewall policy, a DNS record, pure desired state, which fits the host's declaration model exactly and an installable package model not at all. An agent. The host itself, which is not a module and needs a schema that can say so. And the things the mesh depends on and does not control, which are why a node can be perfectly configured and still not work. Nine axes come out of it. Two are covered by nothing anyone has proposed: HOW MANY INSTANCES a thing may have, and WHETHER TWO CAN COEXIST — `excludes` covers part of the second and nothing covers the first. And one question the cases sharpen: is "runs" a property or a kind? The axes say property — one schema with a field saying how it runs, `never` included. The alternative is several kinds of module with different schemas, which is the taxonomy this effort already rejected once for services and applications. --- .../011-the-module-graph/00-overview.md | 5 + 01-RESEARCH/011-the-module-graph/cases.md | 128 ++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 01-RESEARCH/011-the-module-graph/cases.md diff --git a/01-RESEARCH/011-the-module-graph/00-overview.md b/01-RESEARCH/011-the-module-graph/00-overview.md index d9e12b6..ae77ea9 100644 --- a/01-RESEARCH/011-the-module-graph/00-overview.md +++ b/01-RESEARCH/011-the-module-graph/00-overview.md @@ -17,6 +17,11 @@ touches: 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. +**[`cases.md`](cases.md) enumerates what a module can be** — twenty kinds of thing the mesh has +to install, run, own or know about — and extracts the axes a manifest must express. Two of those +axes appear in no current thinking: **how many instances** a thing may have, and **whether two +can coexist**. + **The design is in [`proposal.md`](proposal.md): one kind of edge.** A module provides names and requires names, and that single relation absorbs requiring a module, requiring a resource, and the interface-and-adapter idea. An abstract name is legitimate **only where providers are diff --git a/01-RESEARCH/011-the-module-graph/cases.md b/01-RESEARCH/011-the-module-graph/cases.md new file mode 100644 index 0000000..8c0485c --- /dev/null +++ b/01-RESEARCH/011-the-module-graph/cases.md @@ -0,0 +1,128 @@ +# What a module can be + +Every kind of thing the mesh has to install, run, own or know about, before deciding what a +manifest says. Written to be argued with: a case here that turns out not to exist should be +struck, and one that is missing is a hole in whatever schema follows. + +The hard cases are at the end, and they are the point. + +## The ordinary cases + +**1 — A supervised service.** A container the mesh runs and keeps running. Nobody starts it; +it is simply up. *A relational store, a message broker, an object store, a dashboard.* + +**2 — A system package with configuration.** Not a container. Installed into the machine, +configured through files, run by the service manager. *A firewall, a resolver, an overlay.* +Note: [ADR 0037](../../02-DECISIONS/0037-the-host-applies-it-does-not-decide.md) says applying +these is the host's job — so what the module contributes is the *deciding*, not the doing. + +**3 — An application a person launches.** Installed on a node, started by a human, running only +while they use it. *An editor, a chat client, a file manager, a terminal.* + +**4 — A command-line tool.** Installed, on the path, run when invoked. No service, no window. +*A formatter, a query client, a backup utility.* + +**5 — A library.** Never runs at all. Consumed at build time by other modules. *An SDK.* + +**6 — A one-shot task.** Runs once, changes something, exits. *A schema migration, a data +import, a seed.* + +**7 — A scheduled task.** Runs repeatedly on a timer, exits each time. *A backup, a prune, a +report.* + +**8 — An adapter.** Exists to make several unlike things look alike behind one name. *A model +provider behind an assistant interface.* + +**9 — A standalone application in its own repository.** Same shape as any of the above; the +difference is only where its source lives +([ADR 0010](../../02-DECISIONS/0010-applications-live-in-their-own-repository.md)). Worth +listing because a schema that assumes a monorepo path would exclude it. + +## The cases that break a naive schema + +**10 — Something that is a service *and* an application.** A git forge is consumed by other +modules as a remote and a registry, *and* operated by a person through a web interface. A web +analytics service grants a tracking identity *and* is a dashboard somebody reads. Neither is a +service-or-application choice; both are true simultaneously. + +**11 — Something that provides to others *and* consumes from others.** The store provides +databases and needs a filesystem. The forge provides a registry and needs a database. Provider +and consumer are not kinds of module; they are ends of edges. + +**12 — Something the mesh installs that then becomes a node capability.** The container runtime +is installed *by* the mesh, and once it works, the node **provides** `container-runtime` to +everything else. So a module can change what its node provides. The node's provides-list is +therefore partly derived from what is installed on it, not only detected from what was already +there — and the two have to agree. + +**13 — Something that must be adopted rather than installed.** The machine already has the +package manager, the container runtime, possibly the version control system, each with +configuration somebody chose. The module does not install it; it takes it over +([research 012](../012-the-minimum-viable-node/00-overview.md)). + +**14 — Something that is a set, not a thing.** *Core infrastructure* is not installable — it is +a name for the concrete modules a working node needs. Whether that is a module whose only +content is `requires`, or a query over the graph, or a pinned list outside the catalogue, is +undecided and is one of the sharper questions here. + +**15 — Something with one instance for the whole mesh.** There is one mesh database, not one per +node. Assigning it to two nodes is not redundancy, it is two meshes. Contrast with a terminal, +where per-node is the only sensible reading. + +**16 — Something that may be installed several times over.** Two terminals coexist happily. Two +things wanting port 443 do not. Two message brokers might be fine or might be a split brain, +and nothing in *provides* and *requires* distinguishes those. + +**17 — Something that is not software at all.** A firewall policy. A DNS record. A certificate. +It owns no binary, runs nothing, and is entirely *desired state* — which is the one case that +fits the host's declaration model exactly and fits an installable-package model not at all. + +**18 — An agent.** The mesh's own premise is that agents are participants. An agent has an +identity, a licence, a node it runs on, and work it does. Whether that is a module, a record in +the control plane, or something else is not obvious, and getting it wrong shapes everything +about how agents are assigned. + +**19 — The host itself.** Tier 0 installs everything else and is installed by hand. It is not a +module, and a schema that cannot say so has a bootstrap problem hiding in it. + +**20 — Something the mesh depends on and does not control.** A domain registrar, an upstream +resolver, a certificate authority, an electricity supply. Almost certainly not modules — but +the mesh's health depends on them, and they are the reason a node can be perfectly configured +and still not work. + +## What varies across the cases + +The list above matters less than this. These are the axes a manifest has to express, and each +one is a question the schema must answer or deliberately refuse. + +| Axis | Range | Sharpest case | +|---|---|---| +| **Does it run?** | supervised · launched by a person · once · on a timer · never | 5, 6, 7, 17 | +| **Who starts it?** | the mesh · a human · nothing | 1 vs 3 | +| **Where does it come from?** | container image · system package · our source · already on the machine | 12, 13 | +| **Does it provide to other modules?** | a resource · an abstract name · nothing | 8, 11 | +| **Does it hold state?** | yes, and it matters where · no | 1 vs 3 | +| **How many instances?** | one per mesh · one per node · many per node | 15, 16 | +| **Can two coexist?** | yes · no · only with different settings | 16 | +| **Is it installed or adopted?** | installed · adopted · either, depending on the machine | 13 | +| **Does installing it change what the node provides?** | yes · no | 12 | + +**Two of these are not in any current thinking**, and both come from the hard cases: +*how many instances* (15) and *can two coexist* (16). `excludes` covers part of the second and +nothing covers the first. + +## Questions the cases raise + +- **Is "runs" a property or a kind?** The axes suggest a property — one schema, with a field + saying how it runs, `never` included. The alternative is several kinds of module with + different schemas, which is the taxonomy [research 011](00-overview.md) already rejected once + for services and applications. +- **Is an agent a module?** (18) If yes, the schema carries identity and licensing. If no, the + mesh has two catalogues. +- **Is core infrastructure a module?** (14) A module whose only content is `requires` is either + elegant or a category pretending to be a thing — the same trap `database` was. +- **What names one-per-mesh?** (15) Nothing in provides, requires or excludes says it, and + getting it wrong means two of something that must be one. +- **Where does a policy live?** (17) Pure desired state fits the host's declaration exactly. + Whether it is a module at all, or something the control plane derives and no catalogue entry + exists for, is open.