Every manifest in the system being replaced was read and every key counted, then set against what the new one can express. Three findings worth more than the table. **The most-used key was already covered and I expected a gap.** Depending on another module — 65 manifests, the commonest thing any of them says — is a requirement naming a module, which already means that module rather than anything providing the name. **The largest real gap is tool servers: 56 modules, over half.** A module can already run one; what is missing is anything saying it offers tools. That is plausibly a provision rather than new vocabulary, which would need nothing added — not yet decided, and recorded as undecided. **The gap most worth closing is health, at seven modules.** The mesh knows a container is running, which is not whether it answers, and this project has paid for that distinction twice. An action with a verify is exactly the right shape and may not arrive over the link, so a module cannot declare one. Two things are missing deliberately and say so: stage hooks, because the link may not carry an action and a module needing setup ships a program; and flavours, retired in favour of claims. Config merging is missing and should stay missing. A mechanism that understands TOML gets asked for YAML, then INI, which is how the thing being replaced became unholdable. Also records what the survey found that is not about coverage: manifests that had stopped matching what was actually brokered, one fact derived in two places giving two answers, and a live listing returning credentials in plaintext.
121 lines
6.2 KiB
Markdown
121 lines
6.2 KiB
Markdown
---
|
|
layer: to-be
|
|
status: designed
|
|
code: []
|
|
updated: 2026-09-01
|
|
decisions:
|
|
- 02-DECISIONS/0009-modules-and-the-graph.md
|
|
- 02-DECISIONS/0005-the-node-host.md
|
|
- 02-DECISIONS/0027-a-provision-names-what-the-consumer-is-coupled-to.md
|
|
---
|
|
|
|
# What a module must be able to say
|
|
|
|
**Measured, not guessed.** 127 manifests in the system being replaced were read and every key
|
|
counted, then set against what the new manifest can express. This document is the coverage
|
|
checklist: what is already sayable, what is deliberately not, and what is missing.
|
|
|
|
*Surveyed 2026-09-01. Counts are modules, not occurrences, unless stated.*
|
|
|
|
## Already sayable
|
|
|
|
| what it says | used by | how it is said here |
|
|
|---|---|---|
|
|
| **depends on another module** | 65 | `requires` naming a module. A requirement naming a module means *that module*, not anything providing the name |
|
|
| **system packages** | 28 | the `package` shape |
|
|
| **a container** | 48 | the `container` shape, pinned by digest |
|
|
| **systemd units** | 17 | a `file` for the unit, a `service` for the state it should be in |
|
|
| **how to reach it** | 17 | `serves`, with the mesh adding which machine and where |
|
|
| **a public name** | 11 | requiring `route` and contributing the name |
|
|
| **ports it opens** | 11 | `listens`, from which filtering is computed |
|
|
| **data directories, and who owns them** | 38 | `directory` with `owner`; never removed while holding anything ([ADR 0030](../../02-DECISIONS/0030-data-outlives-the-mesh-that-declared-it.md)) |
|
|
| **what it provides and requires** | 15 + 2 | `provides` / `requires`, named for what the consumer is coupled to |
|
|
| **restart when something changes** | 11 | `restart-on` |
|
|
| **a generated credential** | 20 | `own-secrets`, sealed to the machine |
|
|
| **a value that differs per node** | 43 | settings, and `computed` for what only the mesh knows |
|
|
| **images built from source** | 2 | `build.artifacts` |
|
|
|
|
## Deliberately not sayable
|
|
|
|
**Stage hooks — 36 modules.** Arbitrary code at install, configure and start. **The link may not
|
|
carry an action** ([ADR 0005](../../02-DECISIONS/0005-the-node-host.md)): what may be pushed is
|
|
bounded by form, and a command to run is not a form. A module needing setup logic ships a program
|
|
that reads what the mesh delivered and reconciles — which is what the provisioners are, and they
|
|
are ~350 lines each including the reasoning.
|
|
|
|
**Flavours — 6 modules.** Variants of one module. Retired in favour of claims
|
|
([ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md)): two display servers are two
|
|
modules that both claim the seat, and adding a third changes nothing anywhere else. What is lost
|
|
is `extends` chains, which were doing inheritance and are better as separate modules.
|
|
|
|
## Missing, and what each would take
|
|
|
|
Ordered by how many modules need it.
|
|
|
|
### Tool servers — 56 modules
|
|
|
|
**The largest single gap.** Over half the modules ship a `tools/` directory that becomes tools an
|
|
agent can call on that node. Nothing in the new manifest says *this module offers tools*.
|
|
|
|
A module can already run the server — it is a container or a service. What is missing is the
|
|
convention that makes it reachable: something has to know the tools exist and route to them. That
|
|
is plausibly not a manifest feature at all but a **provision** — a module provides `tools`, the
|
|
session on that node requires them — which would need no new vocabulary. **Not yet decided.**
|
|
|
|
### Schema migrations — 14 modules
|
|
|
|
A module with a database needs its schema brought up to date before it runs. The mesh does this
|
|
for its own contexts and has no way for a *module* to declare it. The provisioner pattern covers
|
|
it — a program that runs migrations and exits — but nothing expresses *this must happen before
|
|
that starts*, which is the actual requirement.
|
|
|
|
### Configuration merging — 18 modules, 134 files
|
|
|
|
Files assembled from a module's default plus per-node overrides, with a strategy (`replace`,
|
|
`merge`) and a format (`toml`, `yaml`, `json`). Settings already merge into a file's content; what
|
|
is missing is format-aware merging.
|
|
|
|
**And it should stay missing.** A mechanism that understands TOML will be asked for YAML, then
|
|
INI — which is how the arrangement being replaced became something nobody could hold in their
|
|
head. The module knows its own format because it wrote the rest of the file.
|
|
|
|
### Health checks — 7 modules
|
|
|
|
`{type: port|url, expect: …}`. The mesh knows whether a container is running, which is not the
|
|
same as whether it answers — a distinction this project has paid for twice already. An `action`
|
|
with a `verify` is exactly this shape, but actions may not arrive over the link, so a module
|
|
cannot declare one.
|
|
|
|
**This is the gap most worth closing**, because *running* and *answering* being conflated is a
|
|
class of fault, not an inconvenience.
|
|
|
|
### Theme knobs — 3 modules, 101 values
|
|
|
|
`{theme: {kind: color|font|string, label}}` — declared so a ricing tool can offer them. Settings
|
|
already carry the value; what is missing is the **metadata** saying a value is presentable and
|
|
what kind it is. Small, self-contained, and only interesting once something presents them.
|
|
|
|
### Event routing — 2 modules
|
|
|
|
`{routing-key: tool}`, generating a consumer. Two modules; wait for a third before deciding.
|
|
|
|
### Publishing a package — 6 modules
|
|
|
|
Modules published to a registry and consumed as libraries. This is a *build* output the mesh does
|
|
not deliver to a node, so it may not belong here at all.
|
|
|
|
## What the survey found that is not about coverage
|
|
|
|
**Declaration and reality had drifted in the system being replaced.** Several live provisions are
|
|
brokered by modules whose manifests declare nothing — a speech-to-text engine served to a consumer
|
|
on another node, an object-store bucket held by a module whose manifest mentions only its
|
|
database. **A manifest that does not have to be true stops being true**, which is the argument for
|
|
resolution refusing rather than warning.
|
|
|
|
**Two derivations of the same fact.** A module's kind was computed in two places from different
|
|
evidence — one from the manifest, one from what is on disk — producing different labels for the
|
|
same module. There is one derivation here, and there should stay one.
|
|
|
|
**A live listing returned credentials in plaintext.** Not a coverage question, but the reason
|
|
sealing is worth its inconvenience.
|