Document the whole module surface, and keep it true with a test
A reference table goes stale the day somebody adds a field, so this one points at mesh-catalog/modules/showcase — a module that uses all of it — and a test that fails when it stops doing so. Read the module when the table disagrees with it. Two rows in the coverage survey were stale because of this week's work: systemd units were a file plus a service, which made every author write unit syntax and is why "process" exists; and building from source was images only, where a bundle now names a language and lets the mesh choose the toolchain. And the two rows at the bottom of the resource table are the interesting ones. "action" is refused to modules outright — the link may not carry a command, so a module needing something done ships a program that reconciles. "service" installs no unit by design, right for software shipping one and wrong for code the mesh built, which has none until the mesh writes it. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
@@ -225,9 +225,16 @@ answered to the asker and kept nowhere.
|
||||
| kind | is |
|
||||
|---|---|
|
||||
| **image** | built from a Dockerfile in this repository |
|
||||
| **bundle** | this module's own code, compiled by the toolchain its language implies, then packed |
|
||||
| **archive** | a directory in this repository, packed |
|
||||
| **upstream** | an image somebody else built, mirrored into the mesh's own registry |
|
||||
|
||||
**The second was added later and is why most modules now need no Dockerfile.** An archive packs a
|
||||
directory as it stands, so shipping compiled output meant compiling somewhere first — and that
|
||||
meant every module repeating a recipe that is easy to get wrong in ways that fail elsewhere. The
|
||||
whole surface a module has, and a module that exercises all of it, are in
|
||||
[`18-building-a-module`](18-building-a-module.md).
|
||||
|
||||
**The third exists because a module usually runs software it did not write.** A database module
|
||||
ships configuration and a provisioner and does not build a database. Naming the upstream reference
|
||||
directly would need every machine to reach a public registry, and would pin to a tag its owner can
|
||||
|
||||
@@ -24,7 +24,7 @@ checklist: what is already sayable, what is deliberately not, and what is missin
|
||||
| **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 |
|
||||
| **systemd units** | 17 | a `process`, which the mesh writes the unit for. *Was: a `file` for the unit and a `service` for its state — which made every module author write unit syntax, and is why `process` exists ([`18`](18-building-a-module.md))* |
|
||||
| **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 |
|
||||
@@ -33,7 +33,7 @@ checklist: what is already sayable, what is deliberately not, and what is missin
|
||||
| **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` |
|
||||
| **images built from source** | 2 | `build.artifacts` — an `image` from a Dockerfile, or a `bundle`, which names a language and lets the mesh choose the toolchain ([`18`](18-building-a-module.md)) |
|
||||
|
||||
## Deliberately not sayable
|
||||
|
||||
|
||||
@@ -148,3 +148,75 @@ makes the drift worse while hiding it.
|
||||
| Nothing is announced that was not made | A build made to fail announces nothing, and the catalogue's graph is unchanged afterwards. |
|
||||
| A build keeps what it was told | A catalogue started after a build asks for what it missed and receives the manifest and the artifacts stood on, not a summary. |
|
||||
| The toolchain is the mesh's, not the author's | Changing the toolchain makes every module built against it stale, and each names what moved. |
|
||||
|
||||
## The whole surface, in one place
|
||||
|
||||
**A reference, and it is kept true by a test rather than by care.** A table like this goes stale the
|
||||
day somebody adds a field, so `mesh-catalog/modules/showcase` is a module that uses all of it, and
|
||||
`TestTheShowcaseModuleIsAValidManifest` fails when it stops doing so. Read the module when this
|
||||
disagrees with it.
|
||||
|
||||
### What a module declares
|
||||
|
||||
| field | is |
|
||||
|---|---|
|
||||
| `module`, `version`, `slug` | its identity; the slug is the short name generated names are built from |
|
||||
| `capabilities` | what a machine must have for this to run there |
|
||||
| `provides` | **the shared seat** — several modules may fill one capability and coexist |
|
||||
| `claims` | **the exclusive seat** — two modules claiming one thing in a scope cannot both be assigned there |
|
||||
| `serves` | what a consumer must know to connect. The mesh fills in the assigned port ([ADR 0038](../../02-DECISIONS/0038-the-mesh-assigns-the-port.md)) |
|
||||
| `requires` | what must be provided by something on the same node |
|
||||
| `binds` | where the mesh writes what a requirement resolved to |
|
||||
| `secrets` | where the mesh seals the credential for a requirement |
|
||||
| `own-secrets` | secrets that are the module's own — a superuser, a broker account |
|
||||
| `emits` / `consumes` | the event graph: 1:many, broadcast, no credential |
|
||||
| `listens` | the port **its own software** uses, and from where. Filtering is computed from these |
|
||||
| `contributes` / `receives` | values one module adds to another's configuration, and the other half |
|
||||
| `accesses` | operator-owned paths it may use and must not own ([ADR 0051](../../02-DECISIONS/0051-shared-data-is-the-operators.md)) |
|
||||
| `certificate` | a certificate for a name it serves |
|
||||
| `grants` | credentials it must create for its consumers |
|
||||
| `filtering` | rules beyond its own ports |
|
||||
| `computed` | marks a module the control plane generates rather than an author writing |
|
||||
| `build.artifacts` | what it produces |
|
||||
|
||||
### What it builds
|
||||
|
||||
| kind | is |
|
||||
|---|---|
|
||||
| `bundle` | its own code, compiled by the toolchain its language implies, then packed |
|
||||
| `archive` | a directory, packed as it stands |
|
||||
| `image` | built from a Dockerfile — for software that needs a particular base |
|
||||
| `upstream` | somebody else's image, mirrored and pinned by a digest this mesh assigned |
|
||||
|
||||
### What it puts on a machine
|
||||
|
||||
| resource | is | a module may |
|
||||
|---|---|---|
|
||||
| `directory` | a directory with a mode and an owner | ✅ |
|
||||
| `file` | literal content, with `${bound:…}` and `${secret:…}` filled in | ✅ |
|
||||
| `user` | a login | ✅ |
|
||||
| `access` | a pre-existing path it may use and must not own | ✅ |
|
||||
| `archive` | files fetched by digest and unpacked | ✅ |
|
||||
| `package` | a package that must be present | ✅ |
|
||||
| `network` | a named container network | ✅ |
|
||||
| `container` | an image, in three modes | ✅ |
|
||||
| `process` | **its own code**, in three modes | ✅ |
|
||||
| `service` | an **existing** unit put into a state | for software shipping its own unit |
|
||||
| `action` | a command to run | ❌ **refused** — [ADR 0005](../../02-DECISIONS/0005-the-node-host.md) |
|
||||
|
||||
**The two at the bottom are the interesting rows.** `action` is refused outright: the link may not
|
||||
carry a command, so a module needing something done ships a program that reconciles — which is what
|
||||
a run-once `process` is. `service` installs no unit by design, which is right for software that
|
||||
ships one and wrong for code the mesh built, which has no unit until the mesh writes it.
|
||||
|
||||
### How its code runs, and what that code can be
|
||||
|
||||
| mode | is | | shape | loaded by |
|
||||
|---|---|---|---|---|
|
||||
| *(default)* | a unit restarted when it exits | | tools | a tool host, over the broker |
|
||||
| `run-once` | run to completion; what follows is gated on it | | event consumer | the same host, reacting |
|
||||
| `schedule` | a timer; a missed fire happens when the machine returns | | provisioner | invoked when a consumer is granted |
|
||||
| | | | a process | the machine's supervisor |
|
||||
|
||||
**Tools, hooks and consumers are not further modes**, which is the test of whether three is the
|
||||
right number: they are loaded by a tool host, and a tool host is a process that stays up.
|
||||
|
||||
Reference in New Issue
Block a user