The glossary's word is catalog: replace the spelling catalogue in running prose
mesh/delivery merged: source.main moved to novox/hq@c2f4bb2d485d
mesh/delivery-group group words/catalog merged: every member is merged
mesh/check pass ea0c62355c1c on eec73b8af0d7 | carried from 292232dad226 | unit 39 run, 0 kept, 1 skipped | integration 39 run, 0 kept, 1 skipped | co…
mesh/delivery merged: source.main moved to novox/hq@c2f4bb2d485d
mesh/delivery-group group words/catalog merged: every member is merged
mesh/check pass ea0c62355c1c on eec73b8af0d7 | carried from 292232dad226 | unit 39 run, 0 kept, 1 skipped | integration 39 run, 0 kept, 1 skipped | co…
This commit is contained in:
@@ -13,7 +13,7 @@ words_test.go the glossary's retired words are not used, no daemon's line is
|
||||
```
|
||||
|
||||
A failing test fails `go test`, so it can be a gate rather than a report. With `MESH_CATALOG_DIR` set, the words
|
||||
test also compares the catalogue's copies of the retired words and the daemons' lines with the glossary.
|
||||
test also compares the catalog's copies of the retired words and the daemons' lines with the glossary.
|
||||
|
||||
**Why this exists.** Until now nothing in this repository was verified by anything but reading,
|
||||
which is how a superseded decision stayed live in the constitution for days and in
|
||||
@@ -99,7 +99,7 @@ quotations, struck-through text, link targets, comments and frontmatter are take
|
||||
`03-DESIGN/`, `AGENTS.md`, `README.md`, and issues opened from 2026-10-07. Decision records and research
|
||||
efforts live in the knowledge base, which holds a proposed record to the same words (the table above). It
|
||||
also fails when a glossary head word heads two entries or is also retired.
|
||||
[`words-allowed.md`](words-allowed.md) names a document that could not be reworded at once, with a date. With `MESH_CATALOG_DIR` set it compares the catalogue's copy
|
||||
[`words-allowed.md`](words-allowed.md) names a document that could not be reworded at once, with a date. With `MESH_CATALOG_DIR` set it compares the catalog's copy
|
||||
of the tools' retired words (`retired-words`) with the glossary.
|
||||
|
||||
It also fails on a **daemon's line** (ADR 0272,
|
||||
@@ -107,7 +107,7 @@ its insight of 2026-10-09): the operator types one command, `nox`, and a documen
|
||||
controller's or the node-engine's binary followed by one of its commands. The lines are the check's own list
|
||||
(`daemonLines` in the words test); a command that is a line only with an argument after it is matched as a line is written. Looked
|
||||
for in the same documents, from 2026-10-09 for issues, in the document's own words — code blocks and
|
||||
spans included, quotations and block quotes taken out. With `MESH_CATALOG_DIR` set it compares the catalogue's copy
|
||||
spans included, quotations and block quotes taken out. With `MESH_CATALOG_DIR` set it compares the catalog's copy
|
||||
(`retired-lines`) with the list.
|
||||
|
||||
It failed on something real before it passed: 581 uses of retired words in 63 documents on its first run, besides the glossary itself and research 034 —
|
||||
|
||||
@@ -35,7 +35,7 @@ import (
|
||||
// struck-through text, link targets, HTML comments and frontmatter are taken out. A word's plural is the word.
|
||||
// unique no head word heads two entries, and none is also struck through on a *Not:* line.
|
||||
// lines no daemon's line in a checked document (ADR 0272, its insight of 2026-10-09): the operator types `nox`.
|
||||
// copy with MESH_CATALOG_DIR set to a checkout of the catalogue, its copies of the retired words and the
|
||||
// copy with MESH_CATALOG_DIR set to a checkout of the catalog, its copies of the retired words and the
|
||||
// daemons' lines equal this glossary's and this list (skipped, with its reason, when unset).
|
||||
//
|
||||
// Checked documents: 00-META/, 03-DESIGN/ (both layers), AGENTS.md and README.md, always; an issue opened on or after
|
||||
@@ -396,7 +396,7 @@ func checkUnique(retired []retiredWord, heads []string) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// linesList is the daemons' lines as the catalogue's copy lists them: "<daemon> <command>", with " …" when an
|
||||
// linesList is the daemons' lines as the catalog's copy lists them: "<daemon> <command>", with " …" when an
|
||||
// argument must follow.
|
||||
func linesList() []string {
|
||||
var out []string
|
||||
@@ -479,26 +479,26 @@ func readList(file string) ([]string, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// checkCopy compares the catalogue's copies of the retired words and the daemons' lines with this glossary's. A
|
||||
// checkCopy compares the catalog's copies of the retired words and the daemons' lines with this glossary's. A
|
||||
// difference names what the copy should list, so regenerating it is copying that from the failure.
|
||||
func checkCopy(catalogue string, retired []retiredWord) []string {
|
||||
var out []string
|
||||
copyWords, err := readList(path.Join(catalogue, "retired-words"))
|
||||
if err != nil {
|
||||
return []string{"the catalogue's copy of the retired words cannot be read: " + err.Error()}
|
||||
return []string{"the catalog's copy of the retired words cannot be read: " + err.Error()}
|
||||
}
|
||||
sort.Slice(copyWords, func(i, j int) bool { return strings.ToLower(copyWords[i]) < strings.ToLower(copyWords[j]) })
|
||||
want := toolsList(retired)
|
||||
if strings.Join(copyWords, "\n") != strings.Join(want, "\n") {
|
||||
out = append(out, "the catalogue's retired-words differs from the glossary: it should list "+strings.Join(want, ", "))
|
||||
out = append(out, "the catalog's retired-words differs from the glossary: it should list "+strings.Join(want, ", "))
|
||||
}
|
||||
copyLines, err := readList(path.Join(catalogue, "retired-lines"))
|
||||
if err != nil {
|
||||
return append(out, "the catalogue's copy of the daemons' lines cannot be read: "+err.Error())
|
||||
return append(out, "the catalog's copy of the daemons' lines cannot be read: "+err.Error())
|
||||
}
|
||||
sort.Strings(copyLines)
|
||||
if strings.Join(copyLines, "\n") != strings.Join(linesList(), "\n") {
|
||||
out = append(out, "the catalogue's retired-lines differs from this check's list: it should list, one per line:\n"+strings.Join(linesList(), "\n"))
|
||||
out = append(out, "the catalog's retired-lines differs from this check's list: it should list, one per line:\n"+strings.Join(linesList(), "\n"))
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -532,7 +532,7 @@ func TestWordsNoDaemonLineWritten(t *testing.T) { expectNone(t, checkLines(rootF
|
||||
func TestWordsCatalogueCopyAgrees(t *testing.T) {
|
||||
catalogue := os.Getenv("MESH_CATALOG_DIR")
|
||||
if catalogue == "" {
|
||||
t.Skip("NOT COMPARED: MESH_CATALOG_DIR is not set, so the catalogue's copy of the list was not read")
|
||||
t.Skip("NOT COMPARED: MESH_CATALOG_DIR is not set, so the catalog's copy of the list was not read")
|
||||
}
|
||||
retired, _, _ := parseGlossary(readFile(rootFS(), glossaryFile))
|
||||
expectNone(t, checkCopy(catalogue, retired))
|
||||
@@ -691,7 +691,7 @@ func TestAllowanceHoldsAWordBackUntilItsDateAndShrinks(t *testing.T) {
|
||||
expectProblem(t, checkRetired(mapOf(tree("an overlay\n", "")), retired, nil, "2026-10-23"), `AGENTS.md:1: "overlay" is retired`)
|
||||
}
|
||||
|
||||
// H5d: the daemons' lines and the catalogue comparison.
|
||||
// H5d: the daemons' lines and the catalog comparison.
|
||||
|
||||
func TestLinesCatchesADaemonsLineButNotProse(t *testing.T) {
|
||||
fsys := mapOf(map[string]string{
|
||||
|
||||
+14
-14
@@ -66,7 +66,7 @@ is listed under [Homonyms](#homonyms), with the qualified form each domain uses.
|
||||
## Module — what a module is and declares
|
||||
|
||||
**Purpose.** To say what one module is, in the one document every other domain reads.
|
||||
**Recorded in** the catalogue, one manifest per module. **Upstream of** every other domain; its words
|
||||
**Recorded in** the catalog, one manifest per module. **Upstream of** every other domain; its words
|
||||
are a published vocabulary, fixed by the manifest's schema. **Decided by** ADR 0040, 0126, 0174, 0188,
|
||||
0207, 0236, 0262.
|
||||
**Uses:** module, seat, config value (Config), provision (Provisioning), data class (Data), health (Health
|
||||
@@ -134,11 +134,11 @@ downstream of Module and Placement. **Decided by** ADR 0262, 0277, 0348.
|
||||
module has moved to their config names (ADR 0348 §1, issue 630).
|
||||
What a "flavor" once varied is a config value or a separate module.
|
||||
*Not:* ~~flavor~~
|
||||
- **predefined key** — a key of the catalogue's `predefined.json`, the fixed list of keys for which mesh
|
||||
- **predefined key** — a key of the catalog's `predefined.json`, the fixed list of keys for which mesh
|
||||
config and node config hold values, each with its type, description, validation and whether it is
|
||||
trusted, such as `font-size`. A config value may name the predefined key it falls back to, of its own
|
||||
type; one that names none resolves from its default and the module's own value only. The list is the
|
||||
catalogue build's: a change to it is validated with every manifest naming a key, and removing a key still
|
||||
catalog build's: a change to it is validated with every manifest naming a key, and removing a key still
|
||||
named or still holding a value is refused (ADR 0348).
|
||||
- **trusted** — said of a config value that decides what runs or who can reach what. At every level it is
|
||||
set only at the controller's terminal or through `config` with propose, on the operator's Approve; a
|
||||
@@ -235,19 +235,19 @@ every other domain has something to run on. **Recorded by** the controller and t
|
||||
## Placement — what runs on which node
|
||||
|
||||
**Purpose.** To decide, send and apply what each node runs, and to say why.
|
||||
**Recorded by** the controller's `inventory` database; from ADR 0549, by the catalogue's placement files, which
|
||||
**Recorded by** the controller's `inventory` database; from ADR 0549, by the catalog's placement files, which
|
||||
the indexer reads onto the bus. **Upstream of** Provisioning, Connectivity, Data
|
||||
and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 0252, 0261, 0411, 0539, 0549, 0559.
|
||||
**Uses:** node, machine, module, seat, manifest (Module), config value (Config).
|
||||
|
||||
- **assignment** — a module put on a node. Its config values on that node (module-assignment config,
|
||||
Config) are `mesh-config`'s, never part of what the controller records (ADR 0348 §9).
|
||||
- **placement file** — the catalogue's file for one node: the modules placed on it and, for each, its pins, its
|
||||
host ports and its bindings to the providers it requires. It changes only by a pull request on the catalogue,
|
||||
- **placement file** — the catalog's file for one node: the modules placed on it and, for each, its pins, its
|
||||
host ports and its bindings to the providers it requires. It changes only by a pull request on the catalog,
|
||||
and the indexer reads it at each trunk merge into its state `placement`, one key per node, which every reader
|
||||
of placement watches (ADR 0549 §1 to §3). Until ADR 0547's third milestone the controller's assignments are
|
||||
kept equal to it.
|
||||
- **placement candidate** — a placement file of one node as an open catalogue pull request has it at its head,
|
||||
- **placement candidate** — a placement file of one node as an open catalog pull request has it at its head,
|
||||
kept by the indexer in its state `placement-candidates` under `<group>.<node>` while the pull request is open.
|
||||
Every node composes it in **shadow** (composed and compared, never applied) and answers in its own have whether
|
||||
what it would run is the same; the composition check passes only when every placed node has answered the same
|
||||
@@ -502,9 +502,9 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel
|
||||
is built from; this entry narrows it to the files within, and that use reads as *the repository holding
|
||||
the build source*. Sharing a repository is not a dependency, and no build source is a whole repository
|
||||
holding another module's files.
|
||||
- **catalogue** — what the mesh holds of every module, at which commit; and the repository the
|
||||
- **catalog** — what the mesh holds of every module, at which commit; and the repository the
|
||||
modules live in. `mesh-catalog` and `catalog_modules` are identifiers and keep their spelling.
|
||||
*Not:* ~~catalog~~ (hq)
|
||||
*Not:* ~~catalogue~~
|
||||
- **delivery** — one commit in one repository on its way to the nodes, from its pull request's head
|
||||
being announced to one of its final states: one pull request, one status, one note
|
||||
(ADR 0239).
|
||||
@@ -554,7 +554,7 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel
|
||||
nothing collects it; the word goes to a *Not:* line with the as-is update after that delivery.
|
||||
- **mesh-delivery** — the repository of the delivery domain, holding its parts as modules prefixed
|
||||
`mesh-delivery-` (ADR 0429 §2), made by the forge module from its config (ADR 0428). Until release 3 of
|
||||
ADR 0429 also the module in the catalogue that owns deliveries and delivery groups, holding the mesh-scoped
|
||||
ADR 0429 also the module in the catalog that owns deliveries and delivery groups, holding the mesh-scoped
|
||||
seat of the same name: it records and decides, and the controller sends, judges and rolls back when it is
|
||||
asked. Release 3 splits that module into the parts, and then the name means the repository alone.
|
||||
- **part** — one independent process of the delivery domain with its own bus credential, which watches keys
|
||||
@@ -752,7 +752,7 @@ asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272, 0383, 03
|
||||
say `mesh-cli`. The operator also calls the mesh itself *nox mesh*. *nox* is also the agent (ADR 0414 §7): see
|
||||
[Homonyms](#homonyms), and say which is meant. It is the one command the operator types: every line the mesh addresses to the operator — a
|
||||
condition's words, a verb's answer, a README, a design — names a `nox` line, never a daemon's (the controller's
|
||||
or the node-engine's binary followed by a command), which the words test and the catalogue's check refuse
|
||||
or the node-engine's binary followed by a command), which the words test and the catalog's check refuse
|
||||
(ADR 0272,
|
||||
its insight of 2026-10-09).
|
||||
- **channel / intake** — the two kinded benches the mesh talks to its operator through: `channel`
|
||||
@@ -828,7 +828,7 @@ it was told, and the servarr apps' own libraries. **Upstream of** nothing. **Dec
|
||||
**Uses:** module, seat, config value (Config).
|
||||
|
||||
- **content** — a work a person watches, listens to or reads: a film, an album and its tracks, an
|
||||
episode, a book, a session of a sport event. It is known by an identity from a catalogue outside the
|
||||
episode, a book, a session of a sport event. It is known by an identity from a catalog outside the
|
||||
mesh, never by a file's name, and a work with no copy anywhere is still content (ADR 0302).
|
||||
- **kind** — a kind of content, declared as configuration, never code: its parts, the metadata fields
|
||||
that identify a work, the file extensions of its copies, and its folder and file templates. The first
|
||||
@@ -881,7 +881,7 @@ and no session (ADR 0278), in its plain sense.
|
||||
entry; the seat `mail-reading`, held by `mesh-mail-reader` (ADR 0459). Not *triage* (the issue tracker's
|
||||
judgement), and not `mail_read`, `mail-store`'s tool that fetches one mail's text.
|
||||
- **mail entry** — what mail reading keeps about one mail: its mail group, a one-line summary, its mark and why, a
|
||||
due date; never the body (ADR 0459 §4). Not *catalogue entry*: the catalogue is Delivery's.
|
||||
due date; never the body (ADR 0459 §4). Not *catalog entry*: the catalog is Delivery's.
|
||||
- **mark** — what a mail entry says the mail is to the operator: *matters*, *scam warning* or neither, never two
|
||||
(ADR 0459 §7). Not *kind* (Media's).
|
||||
- **mail group** — a set of mails the model put together because they belong to one subject (ADR 0459 §4). Not *delivery
|
||||
@@ -1079,7 +1079,7 @@ The words test in `00-META/checks/` (`go test ./...`, run by the checker as `mes
|
||||
word or a bare identifier in running prose in `00-META/`, `03-DESIGN/`, `AGENTS.md`, `README.md`, and
|
||||
issues opened from 2026-10-07 — code spans, quotations and link targets excepted — and on a
|
||||
head word that heads two entries or is also retired. The words retired with no scope or with *(tools)*
|
||||
are copied into the catalogue as `retired-words`, whose own repository check holds the descriptions of
|
||||
are copied into the catalog as `retired-words`, whose own repository check holds the descriptions of
|
||||
the mesh's tools to them; a change here that retires such a word changes that copy in the same delivery.
|
||||
A decision record or research effort proposed to the knowledge base is held to the same words by its
|
||||
rules `retired-word` and `daemon-line`, which read this page (ADR 0290). Homonyms are checked by review.
|
||||
|
||||
@@ -196,7 +196,7 @@ Three levels, by how much real system a test uses (ADR 0391):
|
||||
| Level | Uses | Budget | Runs |
|
||||
|---|---|---|---|
|
||||
| **Unit** | one piece of logic alone: no database, network, bus, running service or other process | well under a second per test | locally, the tests of what changed, while working; and in the forge's checks |
|
||||
| **Integration** | the real thing beside it: a database, the bus, a catalogue tree | the repository's whole suite in seconds, never over sixty | only in the forge's checks, once per push |
|
||||
| **Integration** | the real thing beside it: a database, the bus, a catalog tree | the repository's whole suite in seconds, never over sixty | only in the forge's checks, once per push |
|
||||
| **End to end** | the whole mesh, or a lab mesh, from the outside | — | only when the operator asks |
|
||||
|
||||
- **Test each behaviour at the lowest level that can prove it.** A defect found higher up gets a test at the
|
||||
@@ -209,7 +209,7 @@ Three levels, by how much real system a test uses (ADR 0391):
|
||||
|
||||
### Tests within a time budget — how it was earned
|
||||
|
||||
On 2026-10-16 a feature's fixing agents ran the catalogue's whole merge check (about ten minutes), a
|
||||
On 2026-10-16 a feature's fixing agents ran the catalog's whole merge check (about ten minutes), a
|
||||
throwaway database and a locally built gate during local development. Each round cost tens of minutes and
|
||||
caught neither real failure; the forge's checks found both.
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ import read, which every imported issue names (`imported.commit` in `issue-track
|
||||
ADR 0281).
|
||||
4. Resolve. `issue-tracker.move` to `resolved`, which the tracker takes only from `fixing`, with `fixed-by`, and, if the root cause was a design gap,
|
||||
run playbook [02](02-graduation.md) and give `amended-design`. **A core issue** — its `located-in`
|
||||
names the controller, the node-engine, the tool runner, the SDK, or the catalogue's bus, forge or build
|
||||
names the controller, the node-engine, the tool runner, the SDK, or the catalog's bus, forge or build
|
||||
agent — resolves with `replay`, the id of its replay in mesh-lab's replays register, proved to fail on
|
||||
the commit before the fix and pass on it, or with `replay-none` saying why none is possible
|
||||
(ADR 0237);
|
||||
|
||||
@@ -24,7 +24,7 @@ A small fix (one repository, one issue, about an hour of work) follows
|
||||
- **Review that keeps hardening.** One fix went through seven review rounds in five and a half hours, each
|
||||
asking for more hardening than its issue asked (issue 704, 2026-10-14). A round is for what makes the
|
||||
change wrong; everything else is an issue of its own.
|
||||
- **The first check after the whole branch is written.** A feature's catalogue branch, pushed once after
|
||||
- **The first check after the whole branch is written.** A feature's catalog branch, pushed once after
|
||||
hours of local work, met its first check on 2026-10-16 and failed on 29 problems, TypeScript errors among
|
||||
them, that a unit suite run continuously while working finds at once.
|
||||
- **A green pull request nobody marks.** An approved, green pull request that was not marked ready waited
|
||||
@@ -157,7 +157,7 @@ issue whether it is being fixed in between, after the delivery, or waits, and on
|
||||
the SDK's users (they pin an SDK commit, and a pin to a commit no branch holds is a download that fails later,
|
||||
issue 1148); every program that parses a manifest strictly (search `ParseManifest` across every repository, with
|
||||
the SDK commit each pins), which must take an SDK that knows a new manifest word (2026-10-21: design 60's
|
||||
catalogue milestone found mesh-build-go and the delivery's indexer reading manifests with an SDK from before the
|
||||
catalog milestone found mesh-build-go and the delivery's indexer reading manifests with an SDK from before the
|
||||
word); the SDK's list of a defined seat's verbs, which must name a verb a module adds (2026-10-21: node-files' two
|
||||
new verbs were refused on the claim); the node-engine's decoder for a new key on the wire; the route by which a
|
||||
repository's first module registers; and a part that needs something the bus or a manifest cannot yet give, which
|
||||
@@ -225,7 +225,7 @@ issue whether it is being fixed in between, after the delivery, or waits, and on
|
||||
- **An agent that finds the code differs from its context file stops and reports** (file:line); the lead
|
||||
settles it, updates the context file, and continues the agent.
|
||||
- **Two repositories that depend on each other** keep their order in the context files: the reader before the
|
||||
writer, the controller's new manifest word before the catalogue manifest that uses it.
|
||||
writer, the controller's new manifest word before the catalog manifest that uses it.
|
||||
- **Several agents in one repository** (only for a large repository with independent areas): each works on a
|
||||
local branch of its own from the feature branch's head and lands by fast-forward under a lock
|
||||
(`flock <feature worktree>.lock git -C <feature worktree> merge --ff-only <its branch>`), after its unit
|
||||
@@ -339,13 +339,13 @@ issue whether it is being fixed in between, after the delivery, or waits, and on
|
||||
ask named them): mesh-delivery takes a pull request's issues from the words `issue <n>` and `issues <n>` in its
|
||||
description (the plural yields its first number only), so write `issue 1608, issue 1646`, and name any other
|
||||
issue in words, never by `issue <n>` or `issues <n>`.
|
||||
**A catalogue pull request that removes a module, or a subject a module names, is also judged by the
|
||||
controller's unit tests against the catalogue's branch**, because those tests read the catalogue and no check
|
||||
of the catalogue runs them: on 2026-10-20 mesh-catalog #381 removed the old delivery module, and every
|
||||
controller check failed afterwards on a test of the catalogue's subjects until issue 1406 removed the name
|
||||
mesh-issues still listened to. A controller's check does not find it, since a check reads the catalogue's main
|
||||
(also in a delivery group that holds the catalogue's pull request); the lead runs mesh-controller's unit
|
||||
suite with `MESH_CHECK_BESIDE` naming a directory that holds the catalogue's branch, as mesh-controller's
|
||||
**A catalog pull request that removes a module, or a subject a module names, is also judged by the
|
||||
controller's unit tests against the catalog's branch**, because those tests read the catalog and no check
|
||||
of the catalog runs them: on 2026-10-20 mesh-catalog #381 removed the old delivery module, and every
|
||||
controller check failed afterwards on a test of the catalog's subjects until issue 1406 removed the name
|
||||
mesh-issues still listened to. A controller's check does not find it, since a check reads the catalog's main
|
||||
(also in a delivery group that holds the catalog's pull request); the lead runs mesh-controller's unit
|
||||
suite with `MESH_CHECK_BESIDE` naming a directory that holds the catalog's branch, as mesh-controller's
|
||||
`internal/beside` says.
|
||||
2. **The checker's merge check runs the integration tests once, as the gate** (its one status, `mesh/check`). No
|
||||
second review: step 2 reviewed this tree.
|
||||
@@ -393,15 +393,15 @@ issue whether it is being fixed in between, after the delivery, or waits, and on
|
||||
head is asked once its checks pass. How it is checked: a follow-up that sits in a second pull request while the
|
||||
first still waits shows in `gitea.gitea_list_pull_requests` as two open pull requests of one lead.
|
||||
|
||||
**One exception: a catalogue change that uses a new manifest word.** The catalogue's merge check reads
|
||||
**One exception: a catalog change that uses a new manifest word.** The catalog's merge check reads
|
||||
manifests with the controller the mesh runs, so the controller's pull request is merged and delivered first;
|
||||
then `mesh-delivery.recheck` the catalogue's and merge it on the new verdict. Such work is two releases, the controller's first, and playbook 04 cuts its milestones that way.
|
||||
then `mesh-delivery.recheck` the catalog's and merge it on the new verdict. Such work is two releases, the controller's first, and playbook 04 cuts its milestones that way.
|
||||
**A new key the node-engine checks is such a word too** (issue 1051): the controller judges every machine with the
|
||||
node-engine's own validator, vendored at the commit `go.mod`'s `replace` pins, so a key added to the node-engine is
|
||||
refused by the gate until the controller's pin moves to the delivered node-engine (`go mod vendor`); the feature is
|
||||
then three releases, the node-engine's, the controller's, the catalogue's. **Every release that must merge before
|
||||
the catalogue's is on a branch name of its own** (`feat/<slug>-node-engine`, `feat/<slug>-controller`): on the
|
||||
feature's name it joins the catalogue's delivery group, and a group merges only whole, so neither merges.
|
||||
then three releases, the node-engine's, the controller's, the catalog's. **Every release that must merge before
|
||||
the catalog's is on a branch name of its own** (`feat/<slug>-node-engine`, `feat/<slug>-controller`): on the
|
||||
feature's name it joins the catalog's delivery group, and a group merges only whole, so neither merges.
|
||||
The exception ends when milestone 3 of design 47 is delivered (issue 977, ADR 0402, ADR 0403): the group's composed
|
||||
check, judged by the group's controller, is then each member's gate, and the feature is one group.
|
||||
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@ and a forge address is an operational detail (see [`README`](../README.md)).
|
||||
|
||||
| Repository | Owns |
|
||||
|---|---|
|
||||
| `hal` | The monorepo — the node runtime, the module catalogue, the delivery machinery, and the bootstrap scripts. Every core module lives here. |
|
||||
| `hal` | The monorepo — the node runtime, the module catalog, the delivery machinery, and the bootstrap scripts. Every core module lives here. |
|
||||
| `hq` | This repository, under the company organisation — mission, research, design, decisions, issue diagnosis. Company-scoped (ADR 0019); the mesh is its first product. The source of truth for *why*. Carries no implementation. |
|
||||
| *(one per application)* | Every standalone application, site or side-project gets its own repository, with `module.yml` at the root. Registered with the mesh as a build source; built and deployed by the same pipeline as anything in the monorepo. |
|
||||
| `migration` | **Private.** The record of one installation replacing the predecessor mesh with this one: the runbook, a dated log of every step and what it cost, the per-service data procedures, the readiness checks, and the scripts. Private because it is the opposite of this repository in every way that matters — it names machines, addresses, ports and paths, because a procedure that cannot be followed is not one. Where hq asks *what did we decide and why*, that repository answers *what happened on the machines, in what order, and what to do next*. Its `HANDOFF.md` is where somebody picking the work up starts. |
|
||||
@@ -48,7 +48,7 @@ Named by role, because the layout is itself part of the as-is design — see
|
||||
|
||||
| Area | Holds |
|
||||
|---|---|
|
||||
| Module catalogue | One directory per module, each with a manifest. Core modules sit under the mesh's own namespace; everything else at the top level. |
|
||||
| Module catalog | One directory per module, each with a manifest. Core modules sit under the mesh's own namespace; everything else at the top level. |
|
||||
| Node runtime | The daemon and interactive runtime that every node runs. |
|
||||
| Bootstrap scripts | First-node initialisation, joining an existing mesh, and node rescue. |
|
||||
| Shared library | The SDK every module builds against. |
|
||||
|
||||
@@ -11,7 +11,7 @@ decisions:
|
||||
|
||||
# The mesh as it stands
|
||||
|
||||
A set of machines, each running the same runtime, each loading only the parts of the catalogue
|
||||
A set of machines, each running the same runtime, each loading only the parts of the catalog
|
||||
it has been assigned. They hold no shared filesystem and make no direct connections to one
|
||||
another. What makes them a mesh is a database that knows what should run where, and a message
|
||||
broker that carries everything between them.
|
||||
|
||||
@@ -105,9 +105,9 @@ the resource rather than on the node that built the module.
|
||||
## A documented rule with no enforcement
|
||||
|
||||
Every module exposing capabilities is documented as required to declare the mesh's core runtime
|
||||
as a dependency. **Zero of the catalogue's modules do.**
|
||||
as a dependency. **Zero of the catalog's modules do.**
|
||||
|
||||
This is recorded here rather than quietly corrected, because it is the clearest instance of the
|
||||
rule this repository states about itself: a rule whose enforcement does not exist is
|
||||
indistinguishable from a wrong one, and costs more, because people believe it. Whether the rule
|
||||
or the catalogue is wrong has not been decided.
|
||||
or the catalog is wrong has not been decided.
|
||||
|
||||
@@ -24,7 +24,7 @@ is what makes a node a participant — it is reachable whether or not anyone is
|
||||
protocol. Its capability surface is larger, because it includes stand-ins for every capability
|
||||
discovered on peers.
|
||||
|
||||
The two are the same code with the same catalogue. A capability is written once and is
|
||||
The two are the same code with the same catalog. A capability is written once and is
|
||||
available to both.
|
||||
|
||||
## Anatomy naming, and what it obscures
|
||||
@@ -52,7 +52,7 @@ For each assigned module the runtime, at startup:
|
||||
4. For a service module, ensures it is installed and running.
|
||||
|
||||
Installation is idempotent and does the unglamorous work: ensure the runtime directory,
|
||||
reconcile the link from the catalogue's definition into it, generate the environment, run any
|
||||
reconcile the link from the catalog's definition into it, generate the environment, run any
|
||||
outstanding local migrations, create data directories with the right ownership, then start the
|
||||
service under supervision.
|
||||
|
||||
@@ -85,7 +85,7 @@ import is one-time and not persisted.
|
||||
Three bootstrap scripts, and which one runs depends on the situation:
|
||||
|
||||
- **First node.** Nothing exists yet, so the script stands up the database the rest of the
|
||||
mesh reads from, publishes the catalogue, and starts the mesh. This resolves the
|
||||
mesh reads from, publishes the catalog, and starts the mesh. This resolves the
|
||||
circularity of a mesh whose source of truth is itself a provisioned module.
|
||||
- **Joining.** The node registers, takes its assignment from the database, and syncs.
|
||||
- **Rescue.** A node that cannot reach the mesh is brought back far enough to.
|
||||
|
||||
@@ -9,9 +9,9 @@ decisions:
|
||||
- "ADR 0009"
|
||||
---
|
||||
|
||||
# The catalogue, and what its shape says
|
||||
# The catalog, and what its shape says
|
||||
|
||||
The catalogue holds **124 modules**. Thirty-three belong to the mesh's own domain; the other
|
||||
The catalog holds **124 modules**. Thirty-three belong to the mesh's own domain; the other
|
||||
ninety-one run *on* the mesh rather than being *of* it
|
||||
(ADR 0001).
|
||||
|
||||
@@ -30,7 +30,7 @@ By namespace, and the namespace records origin rather than purpose:
|
||||
|
||||
## What the flat level actually contains
|
||||
|
||||
Grouped by what they are *for* — a grouping the catalogue itself does not express:
|
||||
Grouped by what they are *for* — a grouping the catalog itself does not express:
|
||||
|
||||
| Purpose | Roughly |
|
||||
|---|---|
|
||||
@@ -51,7 +51,7 @@ connectivity is made four times.
|
||||
|
||||
## What the shape records
|
||||
|
||||
**The catalogue's shape records what was installed, not what anything is for.** One module is
|
||||
**The catalog's shape records what was installed, not what anything is for.** One module is
|
||||
the unit of one piece of software, because that is the only granularity the module system
|
||||
offers.
|
||||
|
||||
@@ -64,7 +64,7 @@ deliberately does not yet settle the domain list.
|
||||
|
||||
## Where the shape came from
|
||||
|
||||
The catalogue's shape is not arbitrary and it is not a series of mistakes. **This repository
|
||||
The catalog's shape is not arbitrary and it is not a series of mistakes. **This repository
|
||||
began as a dotfiles repository**, and most of what looks inexplicable is inherited from that
|
||||
directly.
|
||||
|
||||
@@ -88,7 +88,7 @@ The generalisation from *place files on my machines* to *manage a mesh of nodes*
|
||||
move and it worked. What it did not do is revisit the assumptions underneath, because they were
|
||||
never stated as assumptions — they were just how the thing already worked.
|
||||
|
||||
**This is the most useful single fact for anyone changing the catalogue**, and it is why the
|
||||
**This is the most useful single fact for anyone changing the catalog**, and it is why the
|
||||
linking principle in particular reads as a deliberate architectural choice when it is an
|
||||
inheritance. See ADR 0012, whose case
|
||||
this strengthens: the argument for links was never made *for a mesh*.
|
||||
@@ -109,10 +109,10 @@ module out of the repository safe.
|
||||
|
||||
**Placement is already decided.** A standalone application belongs in its own repository
|
||||
(ADR 0015), and reviewers reject
|
||||
it in the monorepo. The catalogue's flat level is not a dumping ground by policy; it is one by
|
||||
it in the monorepo. The catalog's flat level is not a dumping ground by policy; it is one by
|
||||
history.
|
||||
|
||||
## Known inconsistencies in the catalogue itself
|
||||
## Known inconsistencies in the catalog itself
|
||||
|
||||
Recorded because a reader will meet them:
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ decisions:
|
||||
|
||||
# The seats, as they run
|
||||
|
||||
Written from the controller's code and the catalogue's manifests on their main branches. This is
|
||||
Written from the controller's code and the catalog's manifests on their main branches. This is
|
||||
the second piece of the new shape to exist, after [the lab](11-the-lab.md), and the first that the
|
||||
predecessor has no equivalent of: there, any well-formed name became a seat by being claimed.
|
||||
|
||||
@@ -24,7 +24,7 @@ have; nothing else can add to it.
|
||||
|
||||
Five seats deliver a provision: the mesh's store delivers the relational database, the mesh's broker
|
||||
the message transport, the artifact-store seat the registry, and two more the package registry for
|
||||
one ecosystem and the git service. The remaining nine — the controller and catalogue seats, and the
|
||||
one ecosystem and the git service. The remaining nine — the controller and catalog seats, and the
|
||||
node-scope ones for the builder, the DNS port, the packet filter, intrusion prevention, the
|
||||
private network, the resolver's configuration and the showcase — mark a role without answering for
|
||||
anything a consumer requires.
|
||||
|
||||
@@ -21,7 +21,7 @@ decisions:
|
||||
**The `mesh-console` container no longer exists.** Read 2026-10-17 from `mesh-tools` (package
|
||||
`node-tools`), `mesh-host` and `mesh-controller` on their forges' main, and from the live mesh
|
||||
through `mesh_overview`, `mesh_machine`, `mesh_runtimes` and `mesh_describe`. Checked: the
|
||||
catalogue (`mesh-catalog`) carries no module named `mesh-console` any more — the name survives only
|
||||
catalog (`mesh-catalog`) carries no module named `mesh-console` any more — the name survives only
|
||||
in the glossary's retired-words list and in a test fixture — and none of the four nodes answers a
|
||||
module of that name. How the tool runner reaches a node, its count of modules and tools and the
|
||||
controller's verbs were read again on 2026-10-26, after the delivery of release 6 (ADR 0539).
|
||||
@@ -120,11 +120,11 @@ to-be document still describes it as assigned to "whichever machines an operator
|
||||
actually runs is the opposite — every node the mesh holds carries one, because it is core
|
||||
infrastructure the mesh itself depends on (discovery, the controller's own verbs, every module's
|
||||
tools), not a convenience assigned to a workstation. There is accordingly no assign or unassign verb
|
||||
for it: removing it from a node is not an operation the controller or the catalogue exposes.
|
||||
for it: removing it from a node is not an operation the controller or the catalog exposes.
|
||||
|
||||
## What this repository's prior text got wrong
|
||||
|
||||
The document this replaces described `mesh-console` as a catalogue module served by a container and
|
||||
The document this replaces described `mesh-console` as a catalog module served by a container and
|
||||
assigned only to workstations. That module and its container are gone; the manifest is not — it
|
||||
moved to `node-tools/module.json`, still declares `invokes: ["*"]`, and gained `waits` and the
|
||||
`mcp-endpoint` provision, described above. What also moved is which language builds it: the
|
||||
|
||||
@@ -18,7 +18,7 @@ machine and its licence manager; on the day this was written, three buckets exis
|
||||
## What runs
|
||||
|
||||
- **The controller** creates a key-value bucket `<module>_<name>` for every declared state, from the
|
||||
catalogue — on every start and, since the first module that declared state found it missing, on every
|
||||
catalog — on every start and, since the first module that declared state found it missing, on every
|
||||
push before the memberships that name it. A bucket nothing declares any more is reported and kept.
|
||||
Every bucket carries the mesh's caps: 256 KiB a value, 64 MiB a bucket.
|
||||
- **The grants**: the machine's runtime is granted, for each bucket a module it carries owns, writing
|
||||
@@ -46,7 +46,7 @@ machine and its licence manager; on the day this was written, three buckets exis
|
||||
|
||||
## How it is checked
|
||||
|
||||
The controller's catalogue and broker tests (names, grants, memberships, a bucket asserted in place
|
||||
The controller's catalog and broker tests (names, grants, memberships, a bucket asserted in place
|
||||
against a real server); the runtime's tests over a real bus (current values without deletions, refusals,
|
||||
the TypeScript SDK through the runtime); `module check` names a read whose owner on the shelf keeps no
|
||||
such state.
|
||||
|
||||
@@ -15,7 +15,7 @@ decisions:
|
||||
**A node record names at most two logins: the operator account, always, and an agent account, on one node of
|
||||
four. Where a node names no agent account, agents run as the operator account. Nothing ties an identity to a
|
||||
login, and a module that needs one declares it itself.** Read on 2026-10-17 from the controller's and the
|
||||
catalogue's code on main (both local clones were a few commits behind the forge, so each claim below was
|
||||
catalog's code on main (both local clones were a few commits behind the forge, so each claim below was
|
||||
also checked against a live read) and from the live node records. The change planned in ADR 0411 to 0414
|
||||
replaces this document when it is built.
|
||||
|
||||
@@ -113,4 +113,4 @@ Not here: see [`16-logins-on-the-bus.md`](16-logins-on-the-bus.md).
|
||||
tools for its status.
|
||||
- `knowledge-base.get` with `kind` decision and number 266, 409 or 410; `issue-tracker.get` with 989 or 991.
|
||||
- The code: the controller's node-record and agent-account files, and the `claude-code` module's manifest and
|
||||
guard in the catalogue. Their unit tests hold the node record's refusals and the guard's account rule.
|
||||
guard in the catalog. Their unit tests hold the node record's refusals and the guard's account rule.
|
||||
|
||||
@@ -18,7 +18,7 @@ Where the two disagree, the implementation wins and the disagreement is stated.
|
||||
| [`07-knowledge.md`](07-knowledge.md) | The mesh keeps no store: what it knows is what modules answer, and the record is read by one |
|
||||
| [`08-agents-and-work.md`](08-agents-and-work.md) | Agents as employees, tasks, workflows, and the meeting model |
|
||||
| [`09-interfaces-and-observability.md`](09-interfaces-and-observability.md) | How the mesh is reached and watched — tools, board, proxy, health, thoughts |
|
||||
| [`10-module-catalogue.md`](10-module-catalogue.md) | The catalogue's shape, and what its shape says |
|
||||
| [`10-module-catalogue.md`](10-module-catalogue.md) | The catalog's shape, and what its shape says |
|
||||
| [`11-the-lab.md`](11-the-lab.md) | The lab — the first piece of the new shape that exists, and what it does not yet do |
|
||||
| [`12-the-seats.md`](12-the-seats.md) | The seats the mesh defines, who holds one, and where a seat changes resolution |
|
||||
| [`13-the-console.md`](13-the-console.md) | The mesh's tools on the machine a person sits at, served by a module the mesh assigned there |
|
||||
|
||||
@@ -431,17 +431,17 @@ It is the same rule the node-engine follows about a service that does not exist
|
||||
(ADR 0004) — absence must be distinguishable
|
||||
from a failure to answer — applied to coverage instead of to a machine.
|
||||
|
||||
## A bed reads the catalogue it proves
|
||||
## A bed reads the catalog it proves
|
||||
|
||||
*Written 2026-09-21, from resolving [04-ISSUES/073](../../04-ISSUES/073-beds-carry-copies-of-catalogue-manifests/00-report.md);
|
||||
decided in ADR 0089.*
|
||||
|
||||
The rule above — the run rebuilds what it tests — was held for binaries and images and not for
|
||||
manifests. Beds built the manifests they install inline, as literals copied from the catalogue
|
||||
when each bed was written; the copies did not move when the catalogue did, and a catalogue change
|
||||
manifests. Beds built the manifests they install inline, as literals copied from the catalog
|
||||
when each bed was written; the copies did not move when the catalog did, and a catalog change
|
||||
was proven by no bed at all, while every bed stayed green against its copy.
|
||||
|
||||
**A bed that installs a catalogue module reads that module's manifest from the catalogue the run
|
||||
**A bed that installs a catalog module reads that module's manifest from the catalog the run
|
||||
was pointed at.** It rewrites what the lab must — a build artifact becomes the image the machine
|
||||
holds, an image is pinned, a host port is remapped where one machine carries colliding modules,
|
||||
an address may point at a stand-in the bed raises — and nothing else.
|
||||
@@ -451,15 +451,15 @@ server, a secret in the environment, a requirement edge cut so no second provide
|
||||
that is a mesh test. It does not get a name of its own if it runs the module's runtime — a
|
||||
module's name is its tool namespace and its broker scope
|
||||
(ADR 0093) —
|
||||
so it reads the catalogue and installs what the module requires; a name of its own is for a
|
||||
so it reads the catalog and installs what the module requires; a name of its own is for a
|
||||
fixture that runs no real runtime.
|
||||
|
||||
**The receipt names the catalogue's commit** with the others', so a run taken before a manifest
|
||||
**The receipt names the catalog's commit** with the others', so a run taken before a manifest
|
||||
changed says so — the same rule as for the binaries, for the same reason.
|
||||
|
||||
*How it is checked:* a unit test in the lab refuses an inline manifest literal that names a
|
||||
catalogue module unless the bed is declared, with its reason, in the test's own list, and refuses
|
||||
a declaration for a copy that is gone; the receipt test asserts the catalogue is claimed whenever
|
||||
catalog module unless the bed is declared, with its reason, in the test's own list, and refuses
|
||||
a declaration for a copy that is gone; the receipt test asserts the catalog is claimed whenever
|
||||
the run is pointed at one.
|
||||
|
||||
## Consequences
|
||||
|
||||
@@ -31,7 +31,7 @@ offered one driver, everything worked, and snapshots took **seventy-six times lo
|
||||
needed to. Nothing failed. Nothing warned. A lab in that state runs correctly and is simply too
|
||||
slow to use — and the inner loop it exists to provide quietly does not exist.
|
||||
|
||||
The second is the more dangerous shape and it is a variant the mesh has not catalogued before.
|
||||
The second is the more dangerous shape and it is a variant the mesh has not cataloged before.
|
||||
Its usual failure is *reported success and did nothing*. This is **reported success and did it
|
||||
seventy-six times slower**, which no error surface catches because nothing is wrong.
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ raises the bootstrap forge under the forge module's container name, with the mod
|
||||
its data directory, so the module holds it by the found rule; the network is the one difference a take
|
||||
has left to say. *How it is checked:* a host test joins a kept network and refuses one it cannot; a
|
||||
host test keeps a left-out module's record and hold and removes an absent module's; a bootstrap test
|
||||
holds the installer's constants to the module's manifest where the catalogue is checked out beside it.
|
||||
holds the installer's constants to the module's manifest where the catalog is checked out beside it.
|
||||
|
||||
**What filters the machine, and the found firewall kept retired** — revision, 2026-10-02
|
||||
(ADR 0168). The node-engine
|
||||
|
||||
@@ -326,7 +326,7 @@ shape moves — it names a thing and the node-engine fetches it — and the cont
|
||||
genesis must raise before the controller can exist. It still raises one, for the store and the broker,
|
||||
which is where somebody else's software belongs.
|
||||
|
||||
The mesh's own components — the node-engine, the controller, the catalogue, the builder, the vault — are
|
||||
The mesh's own components — the node-engine, the controller, the catalog, the builder, the vault — are
|
||||
delivered as binaries into directories named for their versions, by the mechanism
|
||||
ADR 0141 describes. Third-party
|
||||
software stays a container. The split is not about isolation; it is about who built the thing.
|
||||
|
||||
@@ -425,8 +425,8 @@ time.*
|
||||
A network manager rewrites `/etc/resolv.conf` on every connectivity change unless it is told not to
|
||||
(ADR 0117), so the module holding
|
||||
`node-uplink` — the one telling it — writes the file itself, and there is one owner for it, the one
|
||||
whose program would otherwise overwrite it. Each of the catalogue's managers' modules
|
||||
(NetworkManager, systemd-networkd; dhcpcd's left the catalogue with
|
||||
whose program would otherwise overwrite it. Each of the catalog's managers' modules
|
||||
(NetworkManager, systemd-networkd; dhcpcd's left the catalog with
|
||||
ADR 0226, held by no machine) renders the same template from the resolver's holders and
|
||||
requires the mesh's resolver, so a machine is refused when nothing in the mesh resolves rather than
|
||||
given a file listing nothing. The managers' own mechanisms were weighed and not used: NetworkManager's
|
||||
@@ -440,7 +440,7 @@ node declaring one path are refused. The module that wrote the file before, its
|
||||
retire. The file changes owner in one apply on each machine: the node-engine hands a whole file to the
|
||||
resource declaring its path now rather than removing it first, so a machine is never without it.
|
||||
*Checked by the controller's tests that the uplink modules carry one identical template and that
|
||||
nothing else in the catalogue writes the path, a resolution test refusing a second writer, and the
|
||||
nothing else in the catalog writes the path, a resolution test refusing a second writer, and the
|
||||
node-engine's handover test, in which the file is present at every step of the apply.*
|
||||
|
||||
**A machine's names are one seat's** (ADR 0223).
|
||||
@@ -643,7 +643,7 @@ to stop working.*
|
||||
### The name is a label, not a domain
|
||||
|
||||
*2026-09-09, from running the whole mesh in the lab.* A route contribution carried its public name
|
||||
in full — the forge as `git.example.tld`, spelt out in the module. Pointing the same catalogue at a
|
||||
in full — the forge as `git.example.tld`, spelt out in the module. Pointing the same catalog at a
|
||||
different domain — a lab standing in for production, or a second operator's mesh — meant rewriting
|
||||
that name on every routed module. The mesh was holding a **map of names to services**, which is the
|
||||
one thing it must not: a public name is two facts owned by two different places, and neither is the
|
||||
@@ -976,7 +976,7 @@ run-once step before it starts. The step names the binding it reads and the prox
|
||||
step, so when the authority moves the root is fetched again and the proxy is recreated with it
|
||||
(ADR 0099).
|
||||
*How it is checked:* the route-forwarding bed installs the authority, the proxy and a consumer
|
||||
from the catalogue and asserts the routed name is served.
|
||||
from the catalog and asserts the routed name is served.
|
||||
|
||||
**And a machine trusts that authority because a module put its root in its trust store**
|
||||
(ADR 0147). The proxy's fetch
|
||||
|
||||
@@ -57,10 +57,10 @@ produced from there. A module whose path is the repository root is the ordinary
|
||||
not a separate shape.
|
||||
|
||||
*Corrected 2026-09-12. This section previously said the manifest was at the root, full stop, which
|
||||
made a repository hold exactly one module. Nothing that exists is shaped that way: the catalogue
|
||||
made a repository hold exactly one module. Nothing that exists is shaped that way: the catalog
|
||||
holds sixty-seven modules in their own directories, the system being replaced has always built a
|
||||
module from a repository and a path, and the root-only reading left every existing module
|
||||
unbuildable — pointed at the catalogue the builder finds no manifest, pointed at a module's source
|
||||
unbuildable — pointed at the catalog the builder finds no manifest, pointed at a module's source
|
||||
it finds no manifest either.*
|
||||
|
||||
## A manifest is checked where it is written
|
||||
@@ -84,7 +84,7 @@ read without a store (ADR 0122
|
||||
keeps the store authoritative, so a claim on a mesh seat is judged fully only at registration, and the
|
||||
check says that too).
|
||||
|
||||
*How it is checked:* the controller's test runs the check over the catalogue checkout beside it and
|
||||
*How it is checked:* the controller's test runs the check over the catalog checkout beside it and
|
||||
over a manifest with a known fault, and asserts the first passes and the second names the fault; the
|
||||
test that used to be the only check, `TestEveryCatalogueManifestParses`, now stands beside a command
|
||||
anybody can run.
|
||||
@@ -218,7 +218,7 @@ for, arrived at by an implementation detail.
|
||||
until it is told otherwise; the mesh changing its mind is not a machine acting on it, and
|
||||
collapsing the two is how a mesh comes to report success for something that has not happened.
|
||||
|
||||
*Checked end to end: a commit, a build, a catalogue entry, and a machine that ends up running what
|
||||
*Checked end to end: a commit, a build, a catalog entry, and a machine that ends up running what
|
||||
the source says — with both halves that make the answer trustworthy. It is still running the old
|
||||
one until it is pushed, and it stops being reported as behind once it has caught up, because a
|
||||
status that says "behind" for ever is one nobody reads.*
|
||||
@@ -398,7 +398,7 @@ that distinction once already.*
|
||||
## The four the loop cannot build
|
||||
|
||||
*2026-09-12. Written down because it keeps being rediscovered as if it were new, once per
|
||||
component. It is one rule; the instances are listed below. Revised the same day, when the catalogue
|
||||
component. It is one rule; the instances are listed below. Revised the same day, when the catalog
|
||||
gained an owner and turned out to be a fourth — which is the argument for stating the rule rather
|
||||
than the list.*
|
||||
|
||||
@@ -412,23 +412,23 @@ have a route and one does not:
|
||||
| The controller | It is what installs modules. Nothing can install it before it runs. | **Carried inside the installer** and published once there is a registry (ADR 0067) |
|
||||
| The registry | It *is* where artifacts are delivered from. A store cannot be delivered through itself. | **Pulled from the public internet** — its image is an ordinary public one, never built ([`04-ISSUES/029`](../../04-ISSUES/029-the-artifact-store-cannot-be-delivered-by-the-artifact-store/00-report.md)) |
|
||||
| The builder | It is what builds. Nothing builds it before it runs. | Built at genesis by the init builder (ADR 0070) |
|
||||
| The catalogue | It owns the module graph, and nothing can be resolved or installed without it. | Built at genesis by the init builder (ADR 0070) |
|
||||
| The catalog | It owns the module graph, and nothing can be resolved or installed without it. | Built at genesis by the init builder (ADR 0070) |
|
||||
|
||||
**How they arrive is settled and not yet built.** The installer carries an init builder, which
|
||||
clones the source and builds the controller, the catalogue and the builder before a mesh exists
|
||||
clones the source and builds the controller, the catalog and the builder before a mesh exists
|
||||
to install anything. Two things about that are open and named in
|
||||
ADR 0070: where the init builder
|
||||
clones from, given the forge normally runs on the mesh it would be rebuilding, and what it
|
||||
publishes into, given the registry is installed later in the order today.
|
||||
|
||||
**There is a fourth, and the list was closed too early.** The catalogue owns the module graph
|
||||
**There is a fourth, and the list was closed too early.** The catalog owns the module graph
|
||||
(ADR 0070), so it cannot be
|
||||
installed by something that needs it in order to install anything — the same shape as the other
|
||||
three. This section previously said the list was closed at three, which was written before the
|
||||
catalogue had an owner and is corrected here rather than left to be reasoned from.
|
||||
catalog had an owner and is corrected here rather than left to be reasoned from.
|
||||
|
||||
**And the answer for all four is now one mechanism, not four special cases.** Genesis carries an
|
||||
*init builder* and builds the core modules on the machine — controller, catalogue and builder —
|
||||
*init builder* and builds the core modules on the machine — controller, catalog and builder —
|
||||
rather than carrying a finished image of any of them. So the question is no longer "how does this
|
||||
one get here first?" asked once per component; it is answered once, by the thing that is carried
|
||||
being a builder rather than a result.
|
||||
|
||||
@@ -102,7 +102,7 @@ anything on the machine that can talk to the runtime. So a secret reaches a proc
|
||||
the file the node-engine wrote and points the program at it, and the mesh's own programs take a `_FILE`
|
||||
twin for every variable that carries a credential. Software that reads only its environment is
|
||||
not forbidden; it is **declared**, on the container, with a reason, so the manifest says which
|
||||
secrets are exposed that way. **Checked** at composition: the catalogue engine refuses a
|
||||
secrets are exposed that way. **Checked** at composition: the catalog engine refuses a
|
||||
`${secret:…}` in a container's `env` outright, and a secret-carrying file named in `env-file`
|
||||
unless the container carries the declaration.
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ the one the mesh runs for its own: it offers a place to push, and claims that ro
|
||||
machine.
|
||||
|
||||
**A rule was enforced only at the far end.** A module may not declare an action, and the node-engine
|
||||
refused one correctly — but the controller accepted it into the catalogue, resolved it and
|
||||
refused one correctly — but the controller accepted it into the catalog, resolved it and
|
||||
pushed it, so the refusal arrived on a machine with nothing tying it back to the manifest. The
|
||||
rule held; it was just unusable, which is the same shape as the network shape that cost five
|
||||
failing tests before anyone read the node-engine's log. It is now refused where it is written.
|
||||
|
||||
@@ -132,7 +132,7 @@ adopted as modules, and the machine's service is still reachable.
|
||||
|
||||
Genesis ends with a mesh of one that runs, and that is not the same as a mesh that works. What it
|
||||
has is a controller, a store, a queue and a registry. What it cannot yet do is **produce
|
||||
anything** — and almost every module in the catalogue is waiting to be produced, because a manifest
|
||||
anything** — and almost every module in the catalog is waiting to be produced, because a manifest
|
||||
names what its artifacts are and nothing has made them.
|
||||
|
||||
So installing continues:
|
||||
@@ -172,7 +172,7 @@ need no registry. The gitea module's own server is an upstream image, so buildin
|
||||
|
||||
**The store genesis raises is the `postgres` module's server, so adopting it keeps it** (ADR 0338, the
|
||||
store's twin of the bus's ADR 0333). The bundle raises the store under the name, on the image, in the data
|
||||
directory and with the superuser password file that the catalogue's `postgres` module declares, and every step
|
||||
directory and with the superuser password file that the catalog's `postgres` module declares, and every step
|
||||
of the bundle and of the installer reaches it by that name. At the store step the module declares the
|
||||
container the bundle raised, so the store is adopted where it runs and is not restarted. The module does not
|
||||
change, so no running mesh's store moves. *How it is checked:* a test reads the module's manifest and holds
|
||||
@@ -190,7 +190,7 @@ something it made rather than by a placeholder.
|
||||
mesh running; building it from its own repository and path is what makes it upgradeable. The first
|
||||
time that happens is the moment the mesh stops depending on the installer for anything.
|
||||
|
||||
**And then the catalogue.** Every module with source of its own is built the same way. Until this
|
||||
**And then the catalog.** Every module with source of its own is built the same way. Until this
|
||||
has happened a mesh can install only what is public or carried, which is the foundation and little
|
||||
else.
|
||||
|
||||
@@ -233,7 +233,7 @@ was brought up the supported way, so the rule that it must be is, today, unenfor
|
||||
|
||||
**Nothing asks for the core modules yet.** Installing ends with a builder that works — a raised
|
||||
machine will build the shared base and a module on top of it when asked — and nothing asks. The
|
||||
paragraphs above describing the catalogue being built are a thing somebody now types, rather than a
|
||||
paragraphs above describing the catalog being built are a thing somebody now types, rather than a
|
||||
thing that cannot happen.
|
||||
|
||||
**A module's declaration still has to be copied onto the machine by hand.** The installer reads the
|
||||
@@ -242,7 +242,7 @@ longer among them: it lives in the controller's own repository
|
||||
(ADR 0069), the build the
|
||||
installer runs reports it with the artifact resolved, and genesis registers that
|
||||
([issue 072](../../04-ISSUES/072-the-controllers-manifest-exists-twice/00-report.md)). The
|
||||
builder's manifest belongs in the same repository and is still read from the catalogue; that is
|
||||
builder's manifest belongs in the same repository and is still read from the catalog; that is
|
||||
the last of this.
|
||||
|
||||
**A machine has no account for a registry that asks for one.** The mesh grants a consumer a
|
||||
@@ -275,7 +275,7 @@ issue tracker holds it as issue 602.
|
||||
| An image is named exactly | A machine refuses a bundle naming an image by tag. The refusal is exercised, not assumed. |
|
||||
| The installer is what installed this | **Nothing.** See above. |
|
||||
| The builder can arrive on a fresh mesh | The installer carries it and installs it as its last step, and the genesis bed raises a machine by running the installer. A bed that raises one any other way fails its own acceptance check. |
|
||||
| The builder is assigned before the package registry exists | The build-agent manifest's own test holds the requirement optional and the genesis document declared and issued outside the mesh; the controller's resolution test over the real catalogue assigns the builder with no provider, lists the requirement unbound, and binds it once gitea is offered. The builder's tests refuse a package, an `.npmrc` image and an `@novox/` TypeScript build with neither binding nor genesis document, before anything is built, and build a Go artifact; they read the genesis document only without a binding. The installer's test seals the document at the packages step and gives the builder no `serves` setting and no `npm-password`. **Not yet proved live:** a genesis bed from bare passing the builder, packages, sdk and base steps. |
|
||||
| The builder is assigned before the package registry exists | The build-agent manifest's own test holds the requirement optional and the genesis document declared and issued outside the mesh; the controller's resolution test over the real catalog assigns the builder with no provider, lists the requirement unbound, and binds it once gitea is offered. The builder's tests refuse a package, an `.npmrc` image and an `@novox/` TypeScript build with neither binding nor genesis document, before anything is built, and build a Go artifact; they read the genesis document only without a binding. The installer's test seals the document at the packages step and gives the builder no `serves` setting and no `npm-password`. **Not yet proved live:** a genesis bed from bare passing the builder, packages, sdk and base steps. |
|
||||
| The controller a mesh runs is one it built | The genesis bed asserts the running controller is pinned to a digest this mesh's own registry serves, for an image built from a named repository and commit — not one the installer carried. |
|
||||
| A core module is built rather than only carried | The controller is rebuilt from its own repository and path, and the running mesh is upgraded to the result — the same path any module takes. |
|
||||
| Installing produced a mesh that can produce | A module with source of its own is asked for and comes back pinned to a digest this mesh's registry assigned, not to a placeholder. **Done by hand on a raised machine, not yet by a bed** — it built the shared base and then a module naming that base. Nothing automated asserts it, which makes this the weakest check on this page. |
|
||||
|
||||
@@ -43,7 +43,7 @@ Turning a module's source into artifacts the mesh can pin, publish and deliver
|
||||
truthfully what was produced and what it was produced against.
|
||||
|
||||
Everything else is somebody else's: *what* to build is the controller's, *what a build means* is
|
||||
the catalogue's (ADR 0072), *where an artifact runs* is the
|
||||
the catalog's (ADR 0072), *where an artifact runs* is the
|
||||
controller's again. The builder's whole responsibility is the middle.
|
||||
|
||||
## The language
|
||||
@@ -83,7 +83,7 @@ output into a second stage; and sets an environment variable naming the compiled
|
||||
module repeats it. Miss any step and the failure arrives somewhere else — as a crash loop, a
|
||||
placeholder digest, a module that builds and does nothing.
|
||||
|
||||
The evidence that this is too hard is in the catalogue: **most modules are not converted**, and the
|
||||
The evidence that this is too hard is in the catalog: **most modules are not converted**, and the
|
||||
two converted during one session were each wrong twice before they were right, against a working
|
||||
example sitting open in the next window.
|
||||
|
||||
@@ -163,8 +163,8 @@ makes the drift worse while hiding it.
|
||||
| A module with its own code needs no Dockerfile | A module declaring only a language builds, and its artifact is pinned to a digest the mesh's registry assigned. |
|
||||
| An archive is produced and delivered | A module declaring an archive is built, and the machine assigned it has the unpacked files — verified on the machine, not in the build's own output. |
|
||||
| A Dockerfile still works | A module that declares one builds exactly as before, because software that needs a particular base has not stopped existing. |
|
||||
| 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. |
|
||||
| Nothing is announced that was not made | A build made to fail announces nothing, and the catalog's graph is unchanged afterwards. |
|
||||
| A build keeps what it was told | A catalog 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
|
||||
@@ -207,7 +207,7 @@ declared in one of three ways, for the three things a path can be: the module's
|
||||
or file resource, or where a secret, grant or contribution lands), the operator's (an `accesses`
|
||||
entry), or the machine's (a facility a declared capability grants — `container-runtime` grants its
|
||||
socket). *How it is checked:* the parser refuses an undeclared mount naming the path and the three
|
||||
remedies, and a test parses every manifest in the catalogue beside the checkout.
|
||||
remedies, and a test parses every manifest in the catalog beside the checkout.
|
||||
|
||||
### What it builds
|
||||
|
||||
@@ -247,7 +247,7 @@ counts as a copy and what as a base.
|
||||
| `directory` | a directory with a mode and an owner, **placed by the mesh** under the node's root: `place: "."` is the assignment's own root, `place: "mesh"` the mesh's directory for the module, a pathless one sits beneath the root by its id; a stated path is the placement for data that must stay where it is, and may itself sit beneath a placed one (`${dir:<id>}/…`). Everything else names it as `${dir:<id>}` ([issue 119](../../04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md), [174](../../04-ISSUES/174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md)) | ✅ |
|
||||
| `file` | literal content, with `${bound:…}`, `${secret:…}`, `${dir:…}`, `${port:…}`, `${machine:…}` and `${setting:…}` filled in — the last an operator's value from the assignment's settings, refused by name when unset (ADR 0155) unless the manifest gives a preference a default (ADR 0262) | ✅ |
|
||||
| `user` | a login | ✅ |
|
||||
| `access` | a pre-existing path it may use and must not own, **named by id** and placed by the assignment (`accesses: {<id>: <path>}` on its settings); mounts say `${access:<id>}`; a path in the definition is the default an assignment replaces, tolerated while the catalogue converts ([issue 153](../../04-ISSUES/153-an-adopted-machines-data-cannot-be-placed-where-it-is/00-report.md)) | ✅ |
|
||||
| `access` | a pre-existing path it may use and must not own, **named by id** and placed by the assignment (`accesses: {<id>: <path>}` on its settings); mounts say `${access:<id>}`; a path in the definition is the default an assignment replaces, tolerated while the catalog converts ([issue 153](../../04-ISSUES/153-an-adopted-machines-data-cannot-be-placed-where-it-is/00-report.md)) | ✅ |
|
||||
| `archive` | files fetched by digest and unpacked | ✅ |
|
||||
| `package` | a package that must be present | ✅ |
|
||||
| `network` | a named container network | ✅ |
|
||||
@@ -292,7 +292,7 @@ This is the bus's shared-work pattern, not a build-specific one: any module decl
|
||||
`accepts` has its work shared by its holders the same way. Building is the first use.
|
||||
|
||||
*Built and proven live 2026-10-03.* `build-agent` holds `node-build-agent` on all four machines; the first
|
||||
build taken by a workstation's agent was a catalogue module at 09:35 UTC; the one-holder `builder` is
|
||||
build taken by a workstation's agent was a catalog module at 09:35 UTC; the one-holder `builder` is
|
||||
retired. The switch found five gaps, each an issue: a worker whose type changed stranded its holder
|
||||
([206](../../04-ISSUES/206-a-seats-worker-changing-type-strands-the-holder-and-the-build-that-would-fix-it/00-report.md)),
|
||||
a re-made worker replayed the stream's history ([207](../../04-ISSUES/207-a-re-made-worker-replayed-every-ask-the-stream-kept/00-report.md)),
|
||||
@@ -316,7 +316,7 @@ controller's seat verb through the mesh MCP server — reads it back afterwards.
|
||||
id beside it, and `build` says the id it asked with. The mesh keeps no second copy: the stream is the
|
||||
log. The mesh MCP server's `build` tool asks and answers at once with the id; the outcome is taken in — the
|
||||
build recorded, the module registered with its source — by whoever hears it, the waiting command or
|
||||
the daemon following the role's event, so a build nobody waited for still reaches the catalogue
|
||||
the daemon following the role's event, so a build nobody waited for still reaches the catalog
|
||||
([issue 176](../../04-ISSUES/176-the-consoles-build-tool-neither-waits-nor-registers/00-report.md)). A viewer of builds, when one is built, is a subscriber over these subjects and the outcome; the
|
||||
builder needs nothing more for it.
|
||||
|
||||
@@ -383,7 +383,7 @@ than retried for ever. Live: the store's size before and after the first nightly
|
||||
*2026-09-29 —
|
||||
ADR 0142.*
|
||||
|
||||
The toolchain list was typescript and python, and only typescript had a base module in the catalogue.
|
||||
The toolchain list was typescript and python, and only typescript had a base module in the catalog.
|
||||
Meanwhile the controller — written in the language this project is mostly written in — was built as
|
||||
an image from a hand-written Dockerfile, which is the per-repository incantation this whole mechanism
|
||||
exists to abolish.
|
||||
|
||||
@@ -171,7 +171,7 @@ knows. Putting the Plex client anywhere else is the shared-library disease with
|
||||
- **Which private registry.** That there *is* one is settled —
|
||||
ADR 0014 says each module consumes its dependencies
|
||||
from the private registry, the mesh's own shared library included. Which software serves it is
|
||||
not: the catalogue holds `verdaccio`, and a git host usually serves package registries too.
|
||||
not: the catalog holds `verdaccio`, and a git host usually serves package registries too.
|
||||
- **And the bootstrap does not have one.** ADR 0014 assumes a registry exists; on a fresh mesh
|
||||
nothing has installed one when the first SDK is built. That is the same pivot as everything else
|
||||
and it has not been designed.
|
||||
|
||||
@@ -65,14 +65,14 @@ and waits — which looks exactly like a builder with no work.
|
||||
|
||||
**Genesis ends with a mesh that runs, which is not the same as a mesh that works.** It has a controller, a store, a broker, a registry and a builder. It holds no module graph, has no private
|
||||
network, no packet filter, and cannot resolve a name. Calling that "installed" is what let the
|
||||
catalogue be missing from a test for weeks without anything complaining.
|
||||
catalog be missing from a test for weeks without anything complaining.
|
||||
|
||||
| # | step | what happens | why it is here |
|
||||
|---|---|---|---|
|
||||
| 13 | the shared base is built | the toolchain and runtime every module with code of its own stands on | nothing else with code can be built until it exists |
|
||||
| 14 | a store module is built and run | a database **provider**, which the foundation's store is not | the foundation's store is the controller's own memory, and offers nothing to anything |
|
||||
| 15 | the catalogue is built and run | the module graph | without it the mesh cannot say what it holds, what a change reaches, or what must be rebuilt |
|
||||
| 16 | the catalogue asks for what it missed | the builds made before it existed are replayed | on a fresh mesh those are always the base, the store and the catalogue itself ([issue 050](../../04-ISSUES/050-the-catalogue-knows-nothing-built-before-it/00-report.md)) |
|
||||
| 15 | the catalog is built and run | the module graph | without it the mesh cannot say what it holds, what a change reaches, or what must be rebuilt |
|
||||
| 16 | the catalog asks for what it missed | the builds made before it existed are replayed | on a fresh mesh those are always the base, the store and the catalog itself ([issue 050](../../04-ISSUES/050-the-catalogue-knows-nothing-built-before-it/00-report.md)) |
|
||||
| 17 | the controller is rebuilt from its own repository | and rolled out through the module path | the moment the mesh stops depending on the installer for anything |
|
||||
| 18 | `mesh-wireguard` is assigned **and the node placed** | a private network | assigning installs the module; placing says where this machine is on it. Both, or the machine has no peers. Assigned by its own name since ADR 0226 retired the `networking` bundle |
|
||||
| 19 | the packet filter is assigned | rules generated from what modules declared | until this, every rule the mesh computes has never been applied to anything |
|
||||
|
||||
@@ -44,7 +44,7 @@ after the phases that change its build path are in — not before.
|
||||
|
||||
**Why here.** The Go controller and the TypeScript SDK disagree about what a grant carries
|
||||
(`consumer` is the module in one, the node in the other). That is exercised by the installer's own
|
||||
provisioning — the catalogue's database — so it belongs before more is built on it.
|
||||
provisioning — the catalog's database — so it belongs before more is built on it.
|
||||
|
||||
- [x] 1.1 the drift was not live — inspection showed the wire agrees (contributions file; envelope
|
||||
required headers). The dead types that disagreed are removed, ADR 0074 and doc 19 corrected
|
||||
@@ -91,7 +91,7 @@ a protocol that agrees, a registry to publish to. Issue 051.
|
||||
the controller's records and the database of each module that asks for one. *(Adopted in place: the module's
|
||||
server names the same container and the same pinned upstream image the foundation runs, so the
|
||||
applier reconciles it rather than raising a second postgres. Proven 22/22 in the one-node lab —
|
||||
the catalogue, a `postgres-database` consumer, gets its database from it. Follow-up: the store
|
||||
the catalog, a `postgres-database` consumer, gets its database from it. Follow-up: the store
|
||||
binds `0.0.0.0` from genesis so a mesh consumer can reach it, but the packet filter is
|
||||
installed later — a brief pre-filter window where `mesh-store` is open before `from: mesh`
|
||||
clamps it; bring the filter up earlier or bind narrower at genesis.)*
|
||||
|
||||
@@ -11,7 +11,7 @@ decisions:
|
||||
|
||||
# 23 — Choosing a provider
|
||||
|
||||
**Where it stands, 2026-10-17** (the records cleanup, issue 1072; playbook 07, "The design's state is written back"). Built on main: mesh-controller's internal/catalogue/resolve.go implements the model described here, and designs 26, 27 and 29 rely on it. Not yet checked against this text clause by clause, nor given an as-is: issue 1074.
|
||||
**Where it stands, 2026-10-17** (the records cleanup, issue 1072; playbook 07, "The design's state is written back"). Built on main: mesh-controller's `internal/catalogue/resolve.go` implements the model described here, and designs 26, 27 and 29 rely on it. Not yet checked against this text clause by clause, nor given an as-is: issue 1074.
|
||||
|
||||
A provision is named for what the consumer's code is coupled to
|
||||
(ADR 0027):
|
||||
|
||||
@@ -185,7 +185,7 @@ recovery addresses both alike.
|
||||
|
||||
### A provider with one credential
|
||||
|
||||
*Decided 2026-10-01 (ADR 0158); the controller's half built the same day (mesh-controller PR 184): the offer's word, the need carrying the shared secret's name, the vault's one value under one generation stamp, remade for every holder on a later binding or a rotation, the rotate command sending every holder. What remains is each provider's definition saying `credential` and `taken`, with a start that applies the file — the media catalogue's work.*
|
||||
*Decided 2026-10-01 (ADR 0158); the controller's half built the same day (mesh-controller PR 184): the offer's word, the need carrying the shared secret's name, the vault's one value under one generation stamp, remade for every holder on a later binding or a rotation, the rotate command sending every holder. What remains is each provider's definition saying `credential` and `taken`, with a start that applies the file — the media catalog's work.*
|
||||
|
||||
Software that holds one credential — a download client's web password, an indexer's one API key —
|
||||
cannot give each consumer a login, so ADR 0048's form does not fit it and its values were accepted
|
||||
@@ -202,7 +202,7 @@ acceptance. *How it is checked:* the rows of ADR 0158's table; the controller's
|
||||
### A credential the provider's software issued
|
||||
|
||||
*Decided 2026-10-12 (ADR 0305, issue 526), accepted on the operator's approval the same day; built on
|
||||
branches in the controller and the media catalogue.*
|
||||
branches in the controller and the media catalog.*
|
||||
|
||||
Some software is given its one credential rather than told it: a media server signs in to its vendor and
|
||||
keeps the token the vendor issued. The mesh can neither make nor rotate such a value, and a copy typed for
|
||||
|
||||
@@ -129,7 +129,7 @@ mesh already has a shape for that — a seat's `accept` subjects, on a work queu
|
||||
holders, which is what a build queue shared by several machines *is*. Keeping a second mechanism for it
|
||||
meant two things to reason about and two places for a permission to be wrong. A build's outcome is the
|
||||
seat's own event — which is why the control branch loses its copy too: one publish reaches whoever
|
||||
asked, the controller that records it and the catalogue that places it — the fan-out a shared exchange gave for free, as a derived subject rather than a
|
||||
asked, the controller that records it and the catalog that places it — the fan-out a shared exchange gave for free, as a derived subject rather than a
|
||||
configured topology.
|
||||
|
||||
**Revised 2026-09-26** ([design 29](32-what-a-module-declares.md)): a module's events and tools
|
||||
@@ -179,7 +179,7 @@ Core NATS is at-most-once. Everything the mesh must not lose lives in a JetStrea
|
||||
| EVENTS | `mesh.mod.*.event.>` and `mesh.seat.*.event.>`; and `mesh.again.>` (ADR 0264) | limits (age, size), durable consumer per subscribing module | a subscriber that was down catches up; after `max-deliver` attempts the advisory feeds `mesh.events.dead` (its own small stream). *2026-10-08, ADR 0264:* that stream is DEAD_LETTERS, below; each consumer also filters `mesh.again.<consumer>.>`, where a message it gave up on is delivered again to it alone. *2026-10-01:* a build's whole log is here too, as the build seat's `log.<build id>` events (ADR 0157) — one subject per build, a week of retention, read back by `builds --log <id>` with a consumer that is gone when the reading is done |
|
||||
| DEAD_LETTER_NOTICES | the server's maximum-deliveries advisory | work queue, a week; one consumer (the controller), no `max-deliver` | the server's word that a consumer gave up on a message, kept whether or not a controller is listening, until the controller has copied the message (ADR 0264) |
|
||||
| DEAD_LETTERS | `mesh.events.dead.<stream>.<consumer>` | limits: no age, 256 MiB, refuses new when full; a day's de-duplication | every message a consumer gave up on, with its consumer, subject, deliveries and when, until a person delivers it again or drops it with why (ADR 0264); a `max-deliveries` condition is open per consumer while it holds any |
|
||||
| `KV_<module>_<name>` | `$KV.<module>_<name>.>` | the newest value per key — as many past values as the owner declared — no age unless the owner declared one; a value at most 256 KiB, a bucket at most 64 MiB | a module's state (ADR 0201): one per name in a manifest's `state`, created from the catalogue on every raise, so it exists before its owner runs anywhere; kept when the module is unassigned, because what it holds is data |
|
||||
| `KV_<module>_<name>` | `$KV.<module>_<name>.>` | the newest value per key — as many past values as the owner declared — no age unless the owner declared one; a value at most 256 KiB, a bucket at most 64 MiB | a module's state (ADR 0201): one per name in a manifest's `state`, created from the catalog on every raise, so it exists before its owner runs anywhere; kept when the module is unassigned, because what it holds is data |
|
||||
|
||||
Tool calls and heartbeats stay on core NATS: a lost heartbeat is the next heartbeat; a lost tool
|
||||
call is a timeout the caller already handles.
|
||||
@@ -321,7 +321,7 @@ signing hierarchy for nothing.
|
||||
|
||||
## 5. The broker as a module
|
||||
|
||||
`nats` is a catalogue module claiming the seat `mesh-broker`
|
||||
`nats` is a catalog module claiming the seat `mesh-broker`
|
||||
(ADR 0079: the seat
|
||||
is the server, and the server changes). It declares one container (a single binary; **JetStream on a host
|
||||
directory bind, not a named volume** — revision, second review:
|
||||
@@ -410,7 +410,7 @@ bridged. It is three things:
|
||||
for anyone else — and nothing on control, nodes or builds. It is issued, sealed to the person's
|
||||
own key, and revoked, like a module's.
|
||||
2. **A client that speaks the bus**: a small program on the workstation that connects as that user
|
||||
over TLS, lists tools by asking the catalogue (`mesh.tools.mesh-catalog.catalog_tools`), and
|
||||
over TLS, lists tools by asking the catalog (`mesh.tools.mesh-catalog.catalog_tools`), and
|
||||
turns each tool into a call — as an MCP server for an agent, and as a command line for a person.
|
||||
It uses the sdk's `Broker` contract on the NATS runtime, so it is the same code path a module's
|
||||
tools use, not a second protocol.
|
||||
@@ -606,7 +606,7 @@ find what changed and why.
|
||||
decides it.
|
||||
- The heartbeat interval and the controller's "quiet" threshold on core NATS without persistence —
|
||||
the same numbers as today are proposed.
|
||||
- Whether the person's client is a catalogue module (runs on an enrolled workstation node) or a
|
||||
- Whether the person's client is a catalog module (runs on an enrolled workstation node) or a
|
||||
standalone program (runs anywhere with credentials). Both, in that order, is proposed.
|
||||
- Leaf nodes: a NATS leaf per machine would make every module's connection local and survive the
|
||||
hub's restart. Deliberately out of scope; noted so it is not forgotten.
|
||||
|
||||
@@ -98,7 +98,7 @@ nobody argued for is an entry nobody can explain.
|
||||
|
||||
**What deserves one** (ADR 0161). A provision the
|
||||
mesh's own code dereferences by name is delivered by a mesh seat its provider claims — the store, the
|
||||
bus, the vault, the artifact store, the catalogue. A provision a module merely offers may have several
|
||||
bus, the vault, the artifact store, the catalog. A provision a module merely offers may have several
|
||||
providers, and a consumer with several is a person's choice. A fact of the shape *exactly one machine
|
||||
is X* — the hub — is not a seat, because a seat is held by a module assignment and points at it; it is
|
||||
a placement with a capacity of one, kept by the store, refused by name when a second is placed, and
|
||||
@@ -114,7 +114,7 @@ capability is.
|
||||
ADR 0110): a module
|
||||
declares its own seats with their protocols, so the seats a mesh has are the mesh's own **plus
|
||||
every registered module's**. The set is still closed — a seat named nowhere is refused — but it is
|
||||
computed from the catalogue rather than maintained by hand, which is the property 0110 actually
|
||||
computed from the catalog rather than maintained by hand, which is the property 0110 actually
|
||||
needed and the table could not keep.
|
||||
|
||||
**And the mesh's own half is data, named for its scope.** Revision, 2026-09-27, reconciling two
|
||||
@@ -125,7 +125,7 @@ the set out of compiled code into a table the controller owns, so a rename is on
|
||||
rebuild of everything that names one.
|
||||
|
||||
So the set has two halves and neither is written out here: the mesh's own, which the controller holds
|
||||
as rows, and every registered module's, which is computed from the catalogue. What this document keeps
|
||||
as rows, and every registered module's, which is computed from the catalog. What this document keeps
|
||||
is what a seat *is* — the rest would be a third copy, stale the first time somebody renamed one, which
|
||||
is the fault ADR 0122 exists about.
|
||||
|
||||
@@ -142,7 +142,7 @@ convention, which later seats departed from.
|
||||
| `mesh-broker` | — | mesh | `mesh-bus` | the broker carrying the mesh's own bus |
|
||||
| `mesh-vault` | — | mesh | `secret` | the vault (in the seed since ADR 0161; the earlier *reserved* named an effect no rule produced) |
|
||||
| `mesh-artifact-store` | `the-artifact-store` (renamed 2026-09-30, ADR 0156) | mesh | `artifact-store` | the artifact registry |
|
||||
| `mesh-catalog` | `the-catalogue` | mesh | — | the catalogue |
|
||||
| `mesh-catalog` | `the-catalogue` | mesh | — | the catalog |
|
||||
| `mesh-npm-package-registry` | `npm-package-registry` | mesh | `npm-package-registry` | the forge |
|
||||
| `mesh-git` | `git` | mesh | `git` | the forge |
|
||||
| `mesh-build-machine` | `the-build-machine` | node | — | a builder |
|
||||
@@ -158,7 +158,7 @@ convention, which later seats departed from.
|
||||
|
||||
The controller holds **the mesh's own** entries in code, and a test asserts their size and that
|
||||
every one names the record that made it a seat. A module's seats are not here and never will be —
|
||||
they are read from the catalogue. **This table and
|
||||
they are read from the catalog. **This table and
|
||||
ADR 0126 govern, and code that
|
||||
disagrees is what is wrong.** The implementation in progress predates several
|
||||
things here: seats held by assignments rather than claimed by definitions, the `mesh-vault` seat and
|
||||
@@ -326,7 +326,7 @@ checked as their tables say:
|
||||
|
||||
| Rule | Checked by |
|
||||
|---|---|
|
||||
| The set is closed, and every mesh entry names its decision | 0118: a unit test on the mesh's own entries. **The refusal of an unknown seat is at registration, not in the parser** — correction of fact, 2026-09-27: a module may hold a seat *another* module declares, which is the point of naming the seat and not its provider, so whether a claimed name exists is a fact about the whole catalogue and a manifest in isolation cannot be judged on it. Registration tests cover an invented name and a name another module declares; the parser still refuses a claim on the mesh's own `mesh-*`/`node-*` namespace and a scope that disagrees with a declaration in the same manifest. |
|
||||
| The set is closed, and every mesh entry names its decision | 0118: a unit test on the mesh's own entries. **The refusal of an unknown seat is at registration, not in the parser** — correction of fact, 2026-09-27: a module may hold a seat *another* module declares, which is the point of naming the seat and not its provider, so whether a claimed name exists is a fact about the whole catalog and a manifest in isolation cannot be judged on it. Registration tests cover an invented name and a name another module declares; the parser still refuses a claim on the mesh's own `mesh-*`/`node-*` namespace and a scope that disagrees with a declaration in the same manifest. |
|
||||
| The set is derived, and enumerating it is a query | 0118: the overview lists the mesh's own plus every registered module's, asserted against a fixture mesh. |
|
||||
| `mesh-*` is the mesh's, and a module may not declare one | 0118: a registration test refusing a manifest that declares any `mesh-*` seat, naming the prefix. |
|
||||
| Two modules cannot declare the same seat | 0118: a registration test; the second is refused and the first untouched. |
|
||||
@@ -339,7 +339,7 @@ checked as their tables say:
|
||||
| `secret` has one provider, the holder of `mesh-vault` | 0161: a second claimant of the seat is refused by name (`CanHold`); *correction of fact, 2026-10-01: no parser rule ever reserved the word, the seat does the work*. |
|
||||
| A singular fact about machines is a placement of capacity one, refused by name | 0161: the private network command's test for a second hub; the store's unique index. |
|
||||
| A holder of `node-uplink` is the dialect the machine runs | 0161: the node-engine reports `uplink-<manager>` in its profile with every report; a resolution test refuses the other holder naming the capability. |
|
||||
| A seat's holder has the seats it needs beside it: the resolver configuration is refused at `assign` without the uplink held on its node, and the uplink's last holder cannot be taken from beneath it | 0220: seat-dependency tests on the definition, on a synthetic claimant, at assign, at unassign and at composition, and one reading the catalogue for the uplink's possible holders. |
|
||||
| A seat's holder has the seats it needs beside it: the resolver configuration is refused at `assign` without the uplink held on its node, and the uplink's last holder cannot be taken from beneath it | 0220: seat-dependency tests on the definition, on a synthetic claimant, at assign, at unassign and at composition, and one reading the catalog for the uplink's possible holders. |
|
||||
| A replicated seat has every holder on record and composes on each; a seat held once still refuses a second holder, on record or not; `--add` is refused for it | 0223: resolution and composition tests with two holders on record and a third machine, with two claimants and nothing on record, and on `mesh-store`; a unit test that only `mesh-dns-resolver` is replicated, surviving the store's rows; store tests keeping several holders once each, a handover leaving one, and unassigning one taking only its row. |
|
||||
| A retired seat leaves the set once nothing claims it, from the compiled set and the store's table both | 0220: the closed-set test's count, and the store migration that deletes the row. |
|
||||
| A node seat names only literal absolute paths; every path has one seat naming it outright, and stepping back does not cycle | 0287: a unit test over the compiled set; registration tests refusing a module's seat naming a path at mesh scope, with `${`, or already named by another seat. |
|
||||
|
||||
@@ -47,7 +47,7 @@ A requirement has three parts:
|
||||
|
||||
**A contract is shared, not per module.** A database's contract is the database's, whoever requires
|
||||
it. **The controller holds every contract**, one per provision name, declared where the provision is
|
||||
defined in the catalogue. Today contracts are implicit in each provider's served fields; the first
|
||||
defined in the catalog. Today contracts are implicit in each provider's served fields; the first
|
||||
phase below makes them explicit, because nothing can be checked against a contract that is not
|
||||
written down. A provider is checked against the contract it claims to answer. A module's own
|
||||
specification may narrow a contract (a password of at least this length, a directory owned by this
|
||||
@@ -213,13 +213,13 @@ a placed directory under the node's root. Each is this design's provider in the
|
||||
placeholders have, not yet the one requirement form below; they are phase 1's first cases.
|
||||
|
||||
*Phase 3, in part (2026-09-30):* every definition's **own** data directory is placed; the conversion
|
||||
moved no data, proven by resolving both catalogues with the controller's rule and comparing
|
||||
moved no data, proven by resolving both catalogs with the controller's rule and comparing
|
||||
([issue 119](../../04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md)).
|
||||
What the mesh writes *for* a module was still placed by the definition
|
||||
([issue 174](../../04-ISSUES/174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md)),
|
||||
the gap this design answered on 2026-09-26; built later the same day: a directory saying
|
||||
`place: "mesh"` is `<root>/mesh/<module>`, a directory beneath a placed one states its path as
|
||||
`${dir:<id>}/<rest>` and moves with it, and the same proof — both catalogues resolved and compared —
|
||||
`${dir:<id>}/<rest>` and moves with it, and the same proof — both catalogs resolved and compared —
|
||||
shows forty-eight definitions naming the paths they named before.
|
||||
|
||||
*A preference has a default; the operator's own value has none (2026-10-08,
|
||||
@@ -323,7 +323,7 @@ value in a container's environment is refused when the definition is parsed, wit
|
||||
containers, login, broker account and settings are keyed by it, as today, and a login still fits the
|
||||
backend that keeps it (ADR 0049):
|
||||
the bound is the one the offer of each provision it requires states, a keyless provision states none,
|
||||
and an overflow is refused by the catalogue check before merge and left out of the provider's grants,
|
||||
and an overflow is refused by the catalog check before merge and left out of the provider's grants,
|
||||
reported, at composition — never a refusal of the provider's machine
|
||||
(ADR 0225).
|
||||
|
||||
@@ -371,7 +371,7 @@ not rotated by the vault, which cannot make its replacement: an operator deliver
|
||||
|
||||
**Each requirement says how its recipient takes a new value.** It either *applies* it, through a
|
||||
provisioner (a provider setting a login's password, the broker's provisioner updating an account, a
|
||||
store's provisioner changing its own superuser), or *reads it at start*. Every module in the catalogue
|
||||
store's provisioner changing its own superuser), or *reads it at start*. Every module in the catalog
|
||||
reads its secrets at start, and none watches them. The node-engine already recreates a container when a file
|
||||
it read at creation changes, its env-files and files mounted into it directly
|
||||
([issue 103](../../04-ISSUES/103-a-container-is-not-recreated-when-a-file-it-reads-changes/00-report.md)).
|
||||
@@ -433,7 +433,7 @@ as waiting, and nothing is delivered until the answer arrives.
|
||||
| paths in resources, mounts, bindings, secrets and received files | host directory requirements, placed by the assignment |
|
||||
| literals carried in a definition | operator requirements with defaults |
|
||||
|
||||
Each is retired only once nothing uses it. Until then both are accepted, and a catalogue test lists
|
||||
Each is retired only once nothing uses it. Until then both are accepted, and a catalog test lists
|
||||
the definitions still using the old form. That list shrinks to empty, and then the old form is
|
||||
removed from the parser.
|
||||
|
||||
@@ -453,7 +453,7 @@ Each phase ends at a check that holds, so none of them leaves a mechanism half-r
|
||||
is fixed in the node-engine already. *Ends when* nothing outside the vault generates a shared secret after
|
||||
genesis, a lab consumer of analytics receives its site id, and a database credential rotates over
|
||||
its two credentials, the consumer recreated by derivation, never without a working login, and its data intact.
|
||||
3. **Definitions move, and seats move to assignments.** Every catalogue definition is rewritten, adopted
|
||||
3. **Definitions move, and seats move to assignments.** Every catalog definition is rewritten, adopted
|
||||
and running assignments placed where their data already is, and each claim becomes a seat the
|
||||
module can hold, held by the assignment that holds it today. *Ends when* the list of definitions
|
||||
using an old form is empty, the old forms are removed, and the store module runs on two lab
|
||||
@@ -464,7 +464,7 @@ Each phase ends at a check that holds, so none of them leaves a mechanism half-r
|
||||
| Rule | Checked by |
|
||||
|---|---|
|
||||
| Every requirement has one of the four provider kinds | The parser refuses any other. |
|
||||
| A definition names no host path, node or mesh | The catalogue tests of ADR 0112. |
|
||||
| A definition names no host path, node or mesh | The catalog tests of ADR 0112. |
|
||||
| A module provider is chosen by named seat, pin, co-location, only one, a person's choice | Resolution tests for each step: a requirement naming a seat served by its holder even with another provider on the consumer's node, and refused when the seat is unheld; several candidates and none local, where assignment lists them with the seat's holder first and records the choice as a pin, and refuses without one. |
|
||||
| A provider answers within its contract | A controller test: an answer carrying a field its contract does not name, or missing one it does, is refused and not delivered. |
|
||||
| A module narrows a contract and never widens it | The parser refuses a module specification that loosens a contract's field. |
|
||||
@@ -484,9 +484,9 @@ Each phase ends at a check that holds, so none of them leaves a mechanism half-r
|
||||
| The controller and a node's host take the same path | 0113's tests: the controller's definition declares requirements and no own secret; a node's bus account is made by the vault and delivered sealed to that node. |
|
||||
| Moving the vault or the broker is break-glass | A resolution test: an ordinary assignment moving `mesh-vault` or `mesh-broker` is refused, naming the procedure. |
|
||||
| A secret that cannot be rotated says so | A vault test: rotating a secret marked not rotatable by the mesh is refused, naming why. |
|
||||
| Only the controller reads the seat placeholder | A catalogue test, from phase 3: no definition uses the seat placeholder. |
|
||||
| Only the controller reads the seat placeholder | A catalog test, from phase 3: no definition uses the seat placeholder. |
|
||||
| Refusal names everything at once | A resolution test with three unresolved requirements of different kinds: one refusal naming all three. |
|
||||
| The old forms retire | The catalogue test listing definitions still using one. It must be empty before a form is removed. |
|
||||
| The old forms retire | The catalog test listing definitions still using one. It must be empty before a form is removed. |
|
||||
|
||||
## Not settled here
|
||||
|
||||
|
||||
@@ -169,11 +169,11 @@ paper is wrong until there is a second mesh to find out.
|
||||
|
||||
The derivation refuses two things where they can still be named: two users with one name — the
|
||||
server reads the file as one of them and which one depends on the order — and a module
|
||||
assigned but absent from the catalogue, which would compose a user with no authority and fail
|
||||
assigned but absent from the catalog, which would compose a user with no authority and fail
|
||||
on its first publish with an authorisation error that says nothing about a missing manifest.
|
||||
A user the mesh has minted no password for is *named* rather than dropped or written as a user
|
||||
anybody is: an ordinary situation with an obvious remedy, and the caller decides whether a
|
||||
partial file is worth writing. A seat's protocol is gathered across the whole catalogue, not
|
||||
partial file is worth writing. A seat's protocol is gathered across the whole catalog, not
|
||||
from one manifest, because a seat is declared by one module and held by another.
|
||||
|
||||
**Out, and what each needs.**
|
||||
@@ -447,7 +447,7 @@ pays for itself furthest away.
|
||||
derived to subjects, the three namespaces, permissions computed from a declaration, and a
|
||||
manifest that contains no subject. Done in the controller's composer (permissions, streams,
|
||||
consumers), in the runtime's client (subjects derived from the credential, never named by a
|
||||
module), and as a catalogue test asserting all 72 manifests hold no subject — because the
|
||||
module), and as a catalog test asserting all 72 manifests hold no subject — because the
|
||||
rule held by construction, and a rule held by construction is one a later field breaks
|
||||
quietly.
|
||||
- [x] 3.9 **seats declared by modules** — the manifest now carries `seats` (name, scope,
|
||||
@@ -470,7 +470,7 @@ pays for itself furthest away.
|
||||
all: a holding is derived at resolution, never stored, so nothing recorded points at an old
|
||||
name (recorded as a progressive insight on ADR 0126). A **kept** rename table tells a
|
||||
manifest written against an old name what it became, because a module lives in its own
|
||||
repository and may be registered long after the catalogue stopped using one.
|
||||
repository and may be registered long after the catalog stopped using one.
|
||||
|
||||
**A seat and the interface it delivers are different names.** The `git` seat became
|
||||
`mesh-git` while the `git` *provision* it delivers did not change, and the same for the
|
||||
@@ -522,7 +522,7 @@ it, and the beds that need a mesh living on NATS can finally run.
|
||||
(ADR 0129). Both sides
|
||||
are behind a seam with an implementation per bus, and on the bus being built one publish does
|
||||
what two did: the outcome is the role's own event, so the asker matches it by the id its
|
||||
request carried, the controller records it and the catalogue places it in the graph. A builder needs a reply queue for nothing and a grant over nobody's inbox.
|
||||
request carried, the controller records it and the catalog places it in the graph. A builder needs a reply queue for nothing and a grant over nobody's inbox.
|
||||
|
||||
Checked against a running server: the round trip; a third party on the role's event hearing
|
||||
the same outcome the asker did, which is what the decision rests on; work leaving the queue
|
||||
@@ -532,7 +532,7 @@ it, and the beds that need a mesh living on NATS can finally run.
|
||||
|
||||
The outcome carries the module name, because only the manifest says what was built and one
|
||||
message now has three readers. A failed build names none: it produced no module version, and
|
||||
the catalogue would otherwise place something that was never made.
|
||||
the catalog would otherwise place something that was never made.
|
||||
- [x] 4.3 an installation completes over the bus, with the same outcome as the path it replaces —
|
||||
**the installer can raise it**: a foundation template that stands up the server, writes the
|
||||
server's own settings and the mesh's first user list beside them, and starts a controller
|
||||
@@ -573,7 +573,7 @@ it, and the beds that need a mesh living on NATS can finally run.
|
||||
correct. It uses the client a module's runtime uses, so what a person may do is answered by the
|
||||
same permission list that answers it for a module and an audit has nothing separate to read.
|
||||
|
||||
Three decisions in it worth keeping. It lists what the **catalogue** has rather than what this
|
||||
Three decisions in it worth keeping. It lists what the **catalog** has rather than what this
|
||||
credential may call: somebody seeing only their own tools cannot tell "not installed" from "not
|
||||
yours", and those need different people to fix them. A failed call says which of three things
|
||||
happened — nobody serves it, this credential may not, or the tool was slow — because the
|
||||
@@ -597,10 +597,10 @@ it, and the beds that need a mesh living on NATS can finally run.
|
||||
|
||||
**The catch-up half needed nothing built, and that was the answer.** It existed because a queue on
|
||||
the bus the mesh runs on today receives only what is published after it is bound, so everything
|
||||
built before the catalogue existed was announced to nobody — and on a fresh mesh that is always
|
||||
the foundation, because those are the things the catalogue needed in order to exist
|
||||
built before the catalog existed was announced to nobody — and on a fresh mesh that is always
|
||||
the foundation, because those are the things the catalog needed in order to exist
|
||||
([issue 050](../../04-ISSUES/050-the-catalogue-knows-nothing-built-before-it/00-report.md)). A
|
||||
whole mechanism followed: the catalogue asks, the controller re-publishes.
|
||||
whole mechanism followed: the catalog asks, the controller re-publishes.
|
||||
|
||||
A stream is a log and a consumer is a position in it. A consumer created later starts at the
|
||||
beginning, so the builds are simply there — asked of a running server rather than assumed, since
|
||||
@@ -636,8 +636,8 @@ left to be re-derived under time pressure:
|
||||
| 1 | this one | prose; nothing deploys |
|
||||
| 2 | the sdk | comments only, and no module rebuilds for it |
|
||||
| 3 | the client library | it is what a module calls to emit, and it is where the subject is derived. Until it lands, a locally-named event is published under the local name itself |
|
||||
| 4 | the catalogue | every manifest and every module's code, renamed together. Safe only once the runtime derives |
|
||||
| 5 | the controller | **it refuses an old-style event name outright**, so landing it before the catalogue makes every unconverted module unregisterable |
|
||||
| 4 | the catalog | every manifest and every module's code, renamed together. Safe only once the runtime derives |
|
||||
| 5 | the controller | **it refuses an old-style event name outright**, so landing it before the catalog makes every unconverted module unregisterable |
|
||||
| 6 | the node-engine | last, because nothing else waits on them |
|
||||
|
||||
Two properties make the sequence safe rather than merely ordered, and both are pinned by tests. A
|
||||
@@ -682,11 +682,11 @@ healthy while reacting to nothing.
|
||||
- [x] 5.4 **the old broker and everything that named AMQP leave the mesh** (ADR 0131,
|
||||
superseding ADR 0127): the two modules that
|
||||
required `amqp` are removed, the broker's module is unassigned and removed (**done 2026-09-28**; the predecessor's own tooling, which rode the same adopted broker, went dark with it, as ADR 0130 accepted), registration refuses
|
||||
a manifest that provides or requires `amqp`, and a whole-catalogue check asserts none does. Not
|
||||
a manifest that provides or requires `amqp`, and a whole-catalog check asserts none does. Not
|
||||
a retirement condition — a decision, taken, with the operator's "I don't care if the predecessor
|
||||
breaks" on record (ADR 0130).
|
||||
Retiring with it: the build outcome's second announcement under the module's own name, which
|
||||
existed only so a catalogue deployed before the rename and one after both heard it.
|
||||
existed only so a catalog deployed before the rename and one after both heard it.
|
||||
|
||||
> **The remote tooling goes with it too.** The predecessor's own mesh talks over that broker, so
|
||||
> shutting it down ends the path that reaches this installation's machines from a workstation.
|
||||
|
||||
@@ -174,7 +174,7 @@ the client/identity side and the CA are the open pieces.
|
||||
`ssh-client` template can emit a `Host` block per node with the right login name. Composed
|
||||
end-to-end in the controller's tests.
|
||||
|
||||
**Written but not shipped:** the `ssh-client` catalogue module itself exists on a branch of the
|
||||
**Written but not shipped:** the `ssh-client` catalog module itself exists on a branch of the
|
||||
module repository; its pull request was closed with a hold until this design is deployed, and
|
||||
nothing has deployed it since. The predecessor's generator still writes every workstation's ssh
|
||||
client blocks today — which is where [issue 172](../../04-ISSUES/172-the-ssh-client-block-matches-one-spelling-of-a-machine/00-report.md)
|
||||
|
||||
@@ -30,7 +30,7 @@ webhook".
|
||||
seat or schema:
|
||||
|
||||
1. `module moved <module> <commit>` — tell the mesh its source advanced (the controller repo has no
|
||||
trigger, so this is manual; the catalogue's webhook does it automatically — see below).
|
||||
trigger, so this is manual; the catalog's webhook does it automatically — see below).
|
||||
2. `build --behind` (or `build <repo> [--ref] [--path <subdir>]`) — the builder rebuilds and
|
||||
records the new image.
|
||||
3. The mesh **reconciles on its own**: the module's declaration now names the new image, the next
|
||||
@@ -41,7 +41,7 @@ seat or schema:
|
||||
**A breaking change** — a manifest schema the controller parses differently (a fact's shape, a
|
||||
seat's name), where the new controller cannot read the manifests the old one stored:
|
||||
|
||||
4. Land the code (controller + catalogue together — they are one change).
|
||||
4. Land the code (controller + catalog together — they are one change).
|
||||
5. Rebuild + deploy the new controller (steps 1–3). **The moment it is live it refuses the
|
||||
still-old-shape stored manifests, and composition freezes for every node that runs an affected
|
||||
module.** Running services are untouched; only new declarations stop.
|
||||
@@ -63,7 +63,7 @@ automating this must solve.
|
||||
|
||||
### 1. The trigger today is HAL's, not the mesh's
|
||||
|
||||
Build-on-push works for the catalogue because its repository has a Gitea webhook pointing at
|
||||
Build-on-push works for the catalog because its repository has a Gitea webhook pointing at
|
||||
`http://host.docker.internal:9877/webhook/gitea` — and **that receiver is `hal-gitea-tools.service`**
|
||||
(`~/.hal/modules/hal/gitea/tools/server.js`), a *predecessor* component. The nox builder consumes
|
||||
build work; it does not receive Git events. So the mesh's own build pipeline currently rides on a
|
||||
@@ -78,12 +78,12 @@ HAL service, and:
|
||||
seat — `mesh-forge-trigger` or folded into the git seat's holder) that receives Git events and turns
|
||||
them into build work over the broker, for **every** repository including `mesh-controller`. Replacing
|
||||
`hal-gitea-tools` is the concrete first build. Its logic already exists to copy: match the pushed
|
||||
repository (and changed paths, for a monorepo like the catalogue) against the build-context
|
||||
repository (and changed paths, for a monorepo like the catalog) against the build-context
|
||||
repository of every registered module, and rebuild the matches.
|
||||
|
||||
### 2. The builder validates too — and a breaking change deadlocks it
|
||||
|
||||
The builder embeds the same catalogue package the controller does, so **it validates a
|
||||
The builder embeds the same catalog package the controller does, so **it validates a
|
||||
manifest against its own compiled-in seat/schema set**. A breaking change therefore couples *four*
|
||||
things, not two: the controller, the **builder**, every affected manifest, and every node's host.
|
||||
This session's seat rename rebuilt the controller but not the builder, and the stale builder then
|
||||
@@ -126,7 +126,7 @@ automate the freeze.
|
||||
Revision, ADR 0162. The
|
||||
trigger exists: the forge announces a merge on the bus and the controller acts on it (ADR 0157 made
|
||||
the build narrate; this makes the merge a plan). A module's dependencies are one relation in the
|
||||
catalogue — `depends-on` edges of four kinds: stands-on, packages, built-by, declared. A merge takes
|
||||
catalog — `depends-on` edges of four kinds: stands-on, packages, built-by, declared. A merge takes
|
||||
what changed and everything reachable from it along those edges, sorts the set into tiers, writes the
|
||||
plan to the store, asks the first tier and returns. Each outcome advances the plan; a tier whose
|
||||
rolled-out modules a later tier is built by waits until the machines report them applied; a
|
||||
|
||||
@@ -62,7 +62,7 @@ onto this in step 3 of ADR 0116.
|
||||
This is the load-bearing rule.
|
||||
ADR 0112 says a manifest names no node, mesh or path. A transport address is the same class of thing: if
|
||||
manifests held literal subjects, reorganising the subject space would mean editing every module in
|
||||
the catalogue, and the mesh would have hundreds of copies of a decision it made once.
|
||||
the catalog, and the mesh would have hundreds of copies of a decision it made once.
|
||||
|
||||
| declared | derived |
|
||||
|---|---|
|
||||
@@ -94,7 +94,7 @@ lives under the emitting module's own name and the consumer carries that couplin
|
||||
|
||||
**How the rule is checked, because it was not.** *Added 2026-09-27, same source.* Two checks, because
|
||||
the mistake happens at two scales. Per manifest, at registration: an event is a local name, and the
|
||||
old bus's form is refused with the name to write instead. Across the whole catalogue, as a test:
|
||||
old bus's form is refused with the name to write instead. Across the whole catalog, as a test:
|
||||
where a consumed event's emitter is present, it must emit that event. The second cannot demand a live
|
||||
emitter for everything — a module lives in its own repository and may be installed long before the
|
||||
one whose events it wants — so it says nothing about an absent emitter and everything about a present
|
||||
@@ -117,7 +117,7 @@ caller already handles). The kind token is what makes `mesh.mod.*.event.>` a saf
|
||||
first draft of this table had no token, which reads better and cannot be implemented.
|
||||
|
||||
**The test this must pass: the manifest survives the wire changing.** Reorganise the subject space
|
||||
and every manifest in the catalogue is still correct. That is the property
|
||||
and every manifest in the catalog is still correct. That is the property
|
||||
ADR 0039 gave the sdk, applied to
|
||||
declarations.
|
||||
|
||||
@@ -172,7 +172,7 @@ can be wrong.
|
||||
|
||||
**And one publish reaches three audiences without anybody's inbox being opened.** A build's outcome is
|
||||
the seat's own event: whoever asked matches it by the id their request carried, the controller records
|
||||
it, the catalogue places it in the graph. That is the fan-out a shared exchange gave for free, written
|
||||
it, the catalog places it in the graph. That is the fan-out a shared exchange gave for free, written
|
||||
as a subject the mesh derived instead of a topology somebody configured — and it is why a holder needs
|
||||
no permission to publish into an asker's inbox, which is the one grant design 25 §4 refuses by name.
|
||||
|
||||
@@ -249,7 +249,7 @@ registered for every machine, sent as an event, never reached a machine assigned
|
||||
consumer did not exist yet when the event passed — and a licence binding sent as events replays a
|
||||
week of rotations where only the latest matters. So a module names the state it **owns** with
|
||||
`state`, and another module's it **reads** with `reads: <module>.<name>`. Each is a key-value bucket
|
||||
the controller creates from the catalogue, mesh-wide, existing from registration so a reader can
|
||||
the controller creates from the catalog, mesh-wide, existing from registration so a reader can
|
||||
watch before the owner runs anywhere ([design 25](25-the-bus-on-nats.md) §3). Every instance of the
|
||||
owner writes; a reader reads and watches. A key may name a machine by the module's own convention;
|
||||
the mesh keeps one bucket per name, not one per machine, because "every server, for every machine"
|
||||
@@ -326,7 +326,7 @@ Every shape above appears once, in order, and no step knows where the next one r
|
||||
**A change lands.** The module holding `mesh-git` emits `pushed` — repository, ref, commit. An
|
||||
event, because it is a fact about git and git's identity is the meaning.
|
||||
|
||||
**The change becomes work.** The controller consumes `pushed`, asks the catalogue which modules
|
||||
**The change becomes work.** The controller consumes `pushed`, asks the catalog which modules
|
||||
are built from that repository and path
|
||||
(ADR 0069), and submits one
|
||||
**job** per affected module to the `mesh-build-machine` seat. The builder stays simple: it builds
|
||||
@@ -366,7 +366,7 @@ not to an address it was given at genesis. Held and retried while the store rest
|
||||
chain above went dark at the moment it touched a machine: nothing said which version a machine now runs,
|
||||
or that it refused to. The controller states those as facts under its own seat's namespace, when what
|
||||
a machine runs changes rather than on every convergence pass, and anything that cares subscribes the way
|
||||
the catalogue subscribes to `built` (ADR 0134).
|
||||
the catalog subscribes to `built` (ADR 0134).
|
||||
The facts are second-hand by design — one emitter, one ordering — and a machine that cannot reach the bus
|
||||
produces none, so absence is not health.
|
||||
|
||||
@@ -482,7 +482,7 @@ addresses will believe a class of bug is fixed when it is untouched.
|
||||
controller's address — recorded somewhere, at some moment, and wrong as soon as anything moved.
|
||||
Now it publishes to `mesh.seat.mesh-controller.report` and the bus routes it to whoever holds the
|
||||
seat. Nothing anywhere records where the controller is, so nothing can record it *wrongly*. The
|
||||
same is true of the builder, the catalogue, the telegram sender. This class is not mitigated; it
|
||||
same is true of the builder, the catalog, the telegram sender. This class is not mitigated; it
|
||||
is gone, because the information is no longer stored.
|
||||
|
||||
**Something that is not on the bus: the address stays, exactly as before.** A module that requires
|
||||
@@ -580,11 +580,11 @@ billing existing under that name.
|
||||
|
||||
## 12. How it is checked
|
||||
|
||||
- **A manifest holds no subject.** A catalogue test: no manifest contains a string matching the
|
||||
- **A manifest holds no subject.** A catalog test: no manifest contains a string matching the
|
||||
subject grammar. The rule is worthless if it is followed by convention.
|
||||
- **A preparation is given what the module is given.** A composition test: what the preparation
|
||||
entrypoint receives equals what the module's own code receives, asserted rather than written twice —
|
||||
which is the drift a hand-written step invites, three times over in the catalogue today.
|
||||
which is the drift a hand-written step invites, three times over in the catalog today.
|
||||
- **A convergence that changed nothing says nothing.** Two identical reports, one emitted fact: what is
|
||||
guarded against is a fact per minute per machine, which is a stream nobody reads.
|
||||
- **Permissions are exactly the three namespaces.** A composition test per module: the derived
|
||||
|
||||
@@ -135,7 +135,7 @@ by side until nothing is bound to the old one.
|
||||
*Amended 2026-10-07 (ADR 0246,
|
||||
[issue 298](../../04-ISSUES/298-a-new-seat-verb-deadlocks-across-two-repositories/00-report.md)):*
|
||||
**additive takes two steps when the seat and its holder are in different repositories.** That is the
|
||||
case for every mesh seat whose holder is a catalogue module. §3 makes every promised verb a condition of
|
||||
case for every mesh seat whose holder is a catalog module. §3 makes every promised verb a condition of
|
||||
holding, and a claim may name no verb the seat does not promise. So a verb added to the seat in the
|
||||
controller refuses today's holder, and a holder that serves it is refused by today's controller. Neither
|
||||
change can land first. An added verb therefore lands in three steps, each its own pull request:
|
||||
@@ -163,7 +163,7 @@ still a new version.
|
||||
of the subject table.
|
||||
- **An added verb lands in steps, and each step is checked (§7).** The controller's claim check refuses
|
||||
a claim that serves a verb the seat does not promise, and a holder that misses a required verb. It runs
|
||||
at registration, at handover, and in `module check`, which the catalogue's merge check runs over every
|
||||
at registration, at handover, and in `module check`, which the catalog's merge check runs over every
|
||||
manifest with the controller the mesh runs. So a step out of order fails the pull request that took
|
||||
it. That an optional verb stays optional when read back from the store is held by mesh-controller's
|
||||
`TestAnOptionalVerbStaysOptionalInASetReadFromTheStore` (#114), and that a seat's row gains a verb's
|
||||
|
||||
@@ -28,7 +28,7 @@ it put it there (ADR 0152).
|
||||
|
||||
## 1. What it is
|
||||
|
||||
A module, `mesh-console`, in the catalogue. Its image is the tool runner's own — the client that
|
||||
A module, `mesh-console`, in the catalog. Its image is the tool runner's own — the client that
|
||||
already speaks the bus as a command line and as an MCP server — started in a mode that reads the
|
||||
module's credential and listens on loopback. It has no state, no provision, no seat. What it needs is
|
||||
the bus, which it gets the way every module does: a credential the mesh minted for `<node>.mesh-console`,
|
||||
@@ -42,7 +42,7 @@ consumer is the operator's agent, [36 — The operator's agent on a machine](36-
|
||||
a machine without the mesh MCP server refuses such a module by name. Nothing else above changes: no seat, no
|
||||
state, no tools of its own.
|
||||
|
||||
Its manifest says three things nothing else in the catalogue says together:
|
||||
Its manifest says three things nothing else in the catalog says together:
|
||||
|
||||
- `invokes: ["*"]` — it calls every tool on the mesh, and the bus grants exactly that publish side;
|
||||
- `listens` on a port `from: machine` — the filter opens nothing for it, because loopback is not outside;
|
||||
@@ -74,7 +74,7 @@ its own under that module's name, `mesh.mod.<module>.tool.tools`, answering the
|
||||
descriptions and argument schemas — the definitions from the code that answers them, and from nowhere
|
||||
else. A module may not name a tool of its own `tools`; the runtime refuses the collision at load.
|
||||
|
||||
**The mesh MCP server asks the catalogue which modules the mesh holds, then asks each.** `catalog_modules`
|
||||
**The mesh MCP server asks the catalog which modules the mesh holds, then asks each.** `catalog_modules`
|
||||
answers the roster; one `tools` request per module, in parallel, answers the list. The bus refuses at
|
||||
once a request nothing serves, so a module that is not running costs nothing and is named in the answer
|
||||
as not answering, rather than silently absent — *silence and success must never look alike*. The list is
|
||||
@@ -106,7 +106,7 @@ answers is reached through them by its address: `<seat>.<verb>` for a seat held
|
||||
`<node>/<seat>.<verb>` for one held per machine, `<node>/<module>.<tool>` for an assignment, and
|
||||
`<module>.<tool>` as well for a module whose instances are interchangeable. A module that is not
|
||||
interchangeable is called with its machine or refused with the machines it runs on. Each discovery
|
||||
verb asks the mesh when it is called, so nothing is kept for a session's length; the flat catalogue
|
||||
verb asks the mesh when it is called, so nothing is kept for a session's length; the flat catalog
|
||||
stays reachable through the `mesh` client and a setting, unannounced.
|
||||
|
||||
**What exists is what announced itself** (ADR 0197):
|
||||
@@ -159,7 +159,7 @@ machine with nothing else on it does; the mesh MCP server binds whatever it is g
|
||||
|
||||
Two things shipped bent, both stated in [`00-as-is/13-the-console.md`](../00-as-is/13-the-console.md):
|
||||
the person's client through the mesh MCP server (`--console`) exists and was exercised in the test suite, not
|
||||
on the live mesh; and a module registered from the catalogue by hand recorded its source as a URL rather
|
||||
on the live mesh; and a module registered from the catalog by hand recorded its source as a URL rather
|
||||
than as a path on the git seat, because `--self` takes the forge path form — the rebuild-on-merge still
|
||||
matched it by URL.
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ a hypothesis (ADR 0153);
|
||||
the mesh is asked and changed through the controller seat's verbs; the forge through the forge module's
|
||||
tools; a licence through the `anthropic-licence-manager` seat's verbs, never by editing a file. The hard
|
||||
rules in new words: a file the mesh manages is changed through the verb that owns it or through the
|
||||
catalogue, never on disk; a store's database is never written by hand; main is never pushed; the mesh
|
||||
catalog, never on disk; a store's database is never written by hand; main is never pushed; the mesh
|
||||
creates no symlinks and nobody else does; a package is declared, not installed by hand. The glossary's
|
||||
words, none of the predecessor's.
|
||||
|
||||
@@ -231,7 +231,7 @@ All four nodes carry one since 2026-10-03. **Per node:** the role. **Per mesh or
|
||||
extra tool servers, and the operator's managed-settings keys (§2). The controller's verb replaces a
|
||||
setting layer whole, so a layer set for one of these keeps the others it already held. **Prerequisite:** the manager holds its seat and has adopted the licences.
|
||||
|
||||
**Order:** the manager assigned and a refresh observed; the mesh MCP server's provision in the catalogue; this
|
||||
**Order:** the manager assigned and a refresh observed; the mesh MCP server's provision in the catalog; this
|
||||
module on one workstation; the six predecessor files and the hand-made mesh MCP server entry removed there; a
|
||||
new session read to confirm it sees the mesh's instruction file, the mesh MCP server's five tools under `mesh`, and
|
||||
its licence; then the rest.
|
||||
|
||||
@@ -19,7 +19,7 @@ decisions:
|
||||
|
||||
# 37 — The operator's machine
|
||||
|
||||
**Every configurable thing on a node is a module, the home included, and the same catalogue serves
|
||||
**Every configurable thing on a node is a module, the home included, and the same catalog serves
|
||||
a server and a laptop.** One default configuration per module, varied per node by a setting or a
|
||||
kept region; roles a machine has once as node-scoped seats with tool contracts; one tool runner
|
||||
per node serving every module's tools outside any container
|
||||
@@ -117,7 +117,7 @@ resolver is asked by the second graphical module; the display server itself is g
|
||||
all four today. Nothing home-scoped composes before it.
|
||||
2. **The tool runner** — `mesh-host` supervises it; mesh-tools serves bundles from memberships
|
||||
and reloads; mesh-controller composes the bundle into the declaration and the memberships to one
|
||||
runtime per node; the catalogue renames the mesh MCP server. Proven when the packet-filter verbs answer
|
||||
runtime per node; the catalog renames the mesh MCP server. Proven when the packet-filter verbs answer
|
||||
from it and its container is gone.
|
||||
3. **`zsh`**, the first environment module: seat, `user` shape, home files, `execute`. Proven on a
|
||||
server first, then every node.
|
||||
|
||||
@@ -52,7 +52,7 @@ as design 28's: nothing here is new ground; every package reshapes something sta
|
||||
| the controller's bus principals | Go: one principal per module per node, grants from what it declares | 132 lines | gains one principal per node for the runtime |
|
||||
| the controller's memberships | Go: one per assignment, the subjects a runtime serves | 143 lines | **unchanged** in shape; the runtime reads several |
|
||||
| the controller's declaration composer | Go, one file | 2 053 lines | gains the runtime's process, the bundles' archives, two env words |
|
||||
| the catalogue | 35 manifests build a per-module tool container on the runtime's base image | — | none do; the runtime is a module of its own |
|
||||
| the catalog | 35 manifests build a per-module tool container on the runtime's base image | — | none do; the runtime is a module of its own |
|
||||
|
||||
**Two measurements decide the shape.** The node-engine needs no change: a `process` and an `archive` are
|
||||
what the runtime and a bundle are, and both are applied today. And the runtime already does
|
||||
@@ -140,11 +140,11 @@ with the shortcut off answers the same.
|
||||
a module whose own code is an image artifact is refused, whatever image it is built on.
|
||||
|
||||
*Amended 2026-10-02, at WP3.* The gate refuses the pattern **spreading**, not standing: a module
|
||||
new to the catalogue in that shape, or one that had already moved to a bundle and returns to it,
|
||||
is refused; a module the catalogue already holds in that shape — judged from the manifest it
|
||||
new to the catalog in that shape, or one that had already moved to a bundle and returns to it,
|
||||
is refused; a module the catalog already holds in that shape — judged from the manifest it
|
||||
holds and what that module's newest build stood on — is rebuilt without complaint. The day the
|
||||
runtime arrives some thirty such modules stand, each moves in its own change from WP4 on, and a
|
||||
gate refusing every rebuild in the meantime would stop the catalogue's pipeline to make a point
|
||||
gate refusing every rebuild in the meantime would stop the catalog's pipeline to make a point
|
||||
this record already makes.
|
||||
|
||||
**Proof.** Composition tests: a node with three assigned modules, one holding a seat, yields one
|
||||
@@ -159,7 +159,7 @@ the runtime is registered.
|
||||
**What changes.** mesh-tools gains a `bundle` artifact of itself beside its images, and its manifest
|
||||
becomes the `node-tools` module: a package for the interpreter, the loopback listener the mesh MCP server
|
||||
declared, `invokes: *`, and nothing else — the process is the controller's to compose (WP2). In the
|
||||
catalogue, `mesh-console` is retired as a module and `node-tools` assigned where it was. The
|
||||
catalog, `mesh-console` is retired as a module and `node-tools` assigned where it was. The
|
||||
runtime's `serve` keeps answering MCP on loopback; the person's end of it keeps the name "console"
|
||||
([glossary](../../00-META/glossary.md)).
|
||||
|
||||
@@ -178,7 +178,7 @@ runtime accepts. *Built 2026-10-02* (mesh-tools `c46f950`, mesh-controller `ca7e
|
||||
`729a5f9`). *Proven live 2026-10-02/03, on all four machines*: the mesh MCP server's container is gone,
|
||||
`node-tools` runs as a unit the node-engine wrote, as the operator's account, `tools/list` on each loopback
|
||||
answers with the same 219 tools as before, and the controller's verbs answer through it; `mesh-console`
|
||||
retired from the catalogue. Three things the step found are issues
|
||||
retired from the catalog. Three things the step found are issues
|
||||
[203](../../04-ISSUES/203-a-fresh-assignment-is-pushed-before-its-credential-exists/00-report.md),
|
||||
[204](../../04-ISSUES/204-a-controller-handover-re-sent-every-node-a-stale-declaration/00-report.md) and
|
||||
[205](../../04-ISSUES/205-a-package-resource-fails-against-a-stale-package-database/00-report.md).
|
||||
@@ -224,7 +224,7 @@ the node-engine re-creates the runtime's process on every reconcile (resolved th
|
||||
credential and state directory gone, the client through `sudo` since the daemon's socket is root's;
|
||||
proven on all four machines — `status`, `banned` and the module's own `fail2ban_settings` answer from
|
||||
the runtime, no `mesh-fail2ban` container, the runtime serving both bundles. Two holders moved; of the
|
||||
thirty-three tool containers the catalogue held, thirty-one remain, and all but these two carried their
|
||||
thirty-three tool containers the catalog held, thirty-one remain, and all but these two carried their
|
||||
module's configuration and secrets in the container's environment, which a bundle does not have — the
|
||||
question research research 020 opened
|
||||
and ADR 0192
|
||||
@@ -235,7 +235,7 @@ container's, the runtime hands each bundle its own. That is WP4b below.
|
||||
|
||||
*mesh-controller, mesh-tools, mesh-catalog. One day. The rest of ADR 0175, under ADR 0192.*
|
||||
|
||||
**What changes**, in order: the manifest's tools artifact gains `env` and the catalogue check
|
||||
**What changes**, in order: the manifest's tools artifact gains `env` and the catalog check
|
||||
refuses a secret's content in it; the composer resolves a bundle's `env` per machine and carries it
|
||||
beside the bundle's archive, `restart-on` included; the runtime hands each bundle its own
|
||||
environment — the contributor's argument for an imported bundle, the child's environment for a
|
||||
|
||||
@@ -60,8 +60,8 @@ Measured 2026-10-03 on the four machines and in the repositories.
|
||||
| a bundle's words | paths and constants written with `${dir:…}` and `${port:…}` only; a fact the mesh knows reaches a bundle as a file whose path is a word | the agent module's facts file and settings file |
|
||||
| a bundle calling a tool | `mesh/ask` through the runtime that launched it (ADR 0198); no bundle holds a bus credential | how the manager visits every node |
|
||||
| a module's own long-running code | a bundle the runtime launches and restarts (ADR 0198); the runtime's subscription and grants built, the modules moving in design 38 WP4c's waves | the manager's daemon (WP3, WP4) |
|
||||
| the vendor's refresh, the sealed box, the grant file | `anthropic-manager` in the catalogue, built on the controller placement ADR 0183 moved away from; assigned to nothing | its client ported into the manager; the module retired (WP6) |
|
||||
| the credentials write, the strip, the identity read | `anthropic-consumer` in the catalogue; tested; assigned to nothing | ported into the agent module with its tests; the module retired (WP6) |
|
||||
| the vendor's refresh, the sealed box, the grant file | `anthropic-manager` in the catalog, built on the controller placement ADR 0183 moved away from; assigned to nothing | its client ported into the manager; the module retired (WP6) |
|
||||
| the credentials write, the strip, the identity read | `anthropic-consumer` in the catalog; tested; assigned to nothing | ported into the agent module with its tests; the module retired (WP6) |
|
||||
| the predecessor's manager and consumer | the lease per licence, the expiry floor, the lineage comparison, the identity guard, three touchpoints, cooldowns | ported as logic with its tests |
|
||||
|
||||
## The order the work allows
|
||||
@@ -99,7 +99,7 @@ resolves it. The runtime's port stays what its code fixes; assigning it is
|
||||
second question and not this package's.
|
||||
|
||||
**Proof.** The plan for a workstation carrying a consumer of `mcp-endpoint` shows it bound to the
|
||||
runtime's port; the controller's tests and the catalogue's checks pass.
|
||||
runtime's port; the controller's tests and the catalog's checks pass.
|
||||
|
||||
## WP2 — The agent module
|
||||
|
||||
@@ -130,7 +130,7 @@ runtime's port; the controller's tests and the catalogue's checks pass.
|
||||
**Proof, before anything runs live.** Unit tests: the renderer writes the mesh's keys and nothing else;
|
||||
it writes nothing when nothing changed; the credentials write strips a refresh token and is atomic; the
|
||||
lineage cases from the predecessor; a sealed hand-over opens only with the module's key; no tool's answer
|
||||
contains a token. The catalogue's checks pass.
|
||||
contains a token. The catalog's checks pass.
|
||||
|
||||
**Proof, live, on one workstation, configuration only.** Assign the module; set the node's role; push.
|
||||
The agent's managed directory holds the two files; everything under the person's agent directory is
|
||||
@@ -188,11 +188,11 @@ credentials file is left as it was and the agent authenticates through the key-h
|
||||
|
||||
*The live mesh and mesh-catalog. One day.* The module on every node, the predecessor's files removed on
|
||||
the second workstation; a login under a licence's account collected and adopted, and one under the wrong
|
||||
account refused and notified; `anthropic-manager` and `anthropic-consumer` retired from the catalogue;
|
||||
account refused and notified; `anthropic-manager` and `anthropic-consumer` retired from the catalog;
|
||||
designs 36 and 39 set to `implemented` with the as-is written
|
||||
([playbook 02](../../00-META/process/02-graduation.md)).
|
||||
|
||||
**Proof.** `claude_code_status` answers on every node; the refusal's notification arrived; the catalogue
|
||||
**Proof.** `claude_code_status` answers on every node; the refusal's notification arrived; the catalog
|
||||
has no module built on the old placement.
|
||||
|
||||
## The home, personal rules and lessons — WP7 to WP10
|
||||
@@ -259,7 +259,7 @@ WP10 the rest of the nodes, and the as-is (the live mesh)
|
||||
neither list, the watched entries and the pending requests.
|
||||
|
||||
**Proof, before anything runs live.** The module's tests of design 36 "How it is checked" for ADR 0273 §1,
|
||||
§2, §3 and §4, each failing on the commit before. The catalogue's checks pass.
|
||||
§2, §3 and §4, each failing on the commit before. The catalog's checks pass.
|
||||
|
||||
### WP8 — Personal rules and the operator's items, live
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ against WP2's controller before anything is published.
|
||||
the node. The rendering is ADR 0203's and ADR 0204's: module order, a naming line per contribution,
|
||||
`PATH` entries added only when missing. `${machine:…}` in a contributed value is resolved first.
|
||||
- **Refusals.** A variable set by two modules on one node is refused, naming both. A placeholder in a
|
||||
module that does not claim the matching seat is refused, both at the catalogue check and at
|
||||
module that does not claim the matching seat is refused, both at the catalog check and at
|
||||
composition.
|
||||
|
||||
**Proof.** The controller's tests:
|
||||
@@ -157,7 +157,7 @@ against WP2's controller before anything is published.
|
||||
- each refusal, by name;
|
||||
- the seat table carries both seats and refuses a module declaring either.
|
||||
|
||||
The catalogue check over the whole catalogue passes.
|
||||
The catalog check over the whole catalog passes.
|
||||
|
||||
## WP3 — The modules
|
||||
|
||||
@@ -188,7 +188,7 @@ The catalogue check over the whole catalogue passes.
|
||||
- **`zsh-autosuggestions` and `zsh-syntax-highlighting`, new.** Each declares its package and
|
||||
contributes its loader from the distribution's path, in the `normal` and `last` slots.
|
||||
|
||||
**Proof.** The controller's catalogue check over the whole catalogue passes. The modules' tests pass.
|
||||
**Proof.** The controller's catalog check over the whole catalog passes. The modules' tests pass.
|
||||
A rehearsal composition for a node holding all five shows:
|
||||
|
||||
- the `.zshrc` block with the prompt in `normal` and highlighting in `last`;
|
||||
@@ -227,7 +227,7 @@ The user-scoped units of `mesh-host` #72 stay to-be 38's WP6.
|
||||
|
||||
1. Merge WP1 and roll the node-engine.
|
||||
2. Merge WP2, and push the controller.
|
||||
3. Merge WP3 and WP4, and build the new modules by hand: a new catalogue module's first build is asked
|
||||
3. Merge WP3 and WP4, and build the new modules by hand: a new catalog module's first build is asked
|
||||
for, not automatic.
|
||||
4. On one server, assign `node-env`, `zsh`, `zsh-autosuggestions` and `zsh-syntax-highlighting`, and
|
||||
push. Then check:
|
||||
@@ -328,7 +328,7 @@ control-node withholds it until a call to it needs a person's approval. The othe
|
||||
writes it into, and a word naming that file for its tools bundle. The bundle registers `execute` only
|
||||
when the file says `serve`. `zsh_config` also answers whether `execute` is served on the node, and why.
|
||||
|
||||
**Merge order.** The controller first, then the catalogue. Either order composes. With the catalogue
|
||||
**Merge order.** The controller first, then the catalog. Either order composes. With the catalog
|
||||
alone and the control-node's value set, the self-check would say the holder there is silent.
|
||||
|
||||
**Proof.** The tests ADR 0268 lists under *how it is checked*. Live, after both are merged and rolled
|
||||
@@ -382,7 +382,7 @@ move, write or delete a file, and a cleanup needed a shell on the machine. Most
|
||||
project folders, downloads, logs, scratch.
|
||||
|
||||
- **A node seat, `node-files`, on every node**, held by a module of its own, `mesh-files`, beside `mesh-reads`
|
||||
in the catalogue; the controller holds the seat's definition beside the other node seats. It has a verb for each file action: list,
|
||||
in the catalog; the controller holds the seat's definition beside the other node seats. It has a verb for each file action: list,
|
||||
stat, readlink, read, search, write, copy, move, delete, make a folder, change mode, change owner, account and group
|
||||
lookup, and the trash's list and restore. Each says what it replaces (ADR 0245): `ls`, `stat`, `readlink`, `cat`,
|
||||
`grep`, `cp`, `mv`, `rm`, `mkdir`, `chmod`, `chown`, `getent passwd`, `getent group`, `id`, and `ssh <node> cp` for a
|
||||
|
||||
@@ -50,7 +50,7 @@ not before. Modules that need none go ahead now.
|
||||
|
||||
## How every module moves
|
||||
|
||||
1. Written in the catalogue, with its tools and their tests, and checked by the controller's catalogue
|
||||
1. Written in the catalog, with its tools and their tests, and checked by the controller's catalog
|
||||
check.
|
||||
2. Merged, which builds it.
|
||||
3. Assigned to **the first workstation, the proving machine**, and pushed. Its tools and files are
|
||||
@@ -199,11 +199,11 @@ both places of both bars once. The bar writes no block that depends on the hardw
|
||||
the power module's state `power.draw`, one value per machine, which its bundle puts from its own root
|
||||
sampler. The block `shows: state`; the bar's bundle watches it under a read the controller grants the
|
||||
bar for what the modules on its machine offer, and the bar names no module. The battery block is the
|
||||
bar's own, reading the kernel. The catalogue's `checks/paths` refuses a module that names another
|
||||
bar's own, reading the kernel. The catalog's `checks/paths` refuses a module that names another
|
||||
module's path under `/run` or `/var/lib`.
|
||||
|
||||
The node-engine is delivered first, so the machines report the two capabilities. The controller comes
|
||||
next, because it parses `data`, `if-capability` and `renders`. The catalogue comes last. Left open: the
|
||||
next, because it parses `data`, `if-capability` and `renders`. The catalog comes last. Left open: the
|
||||
draw at the wall (a metered plug or a UPS), a GPU-load block from a generic module, and the headsets.
|
||||
|
||||
## Phase 3 — one machine model
|
||||
@@ -213,6 +213,6 @@ and `memory-pressure` (research 027/03).
|
||||
|
||||
## How it is checked
|
||||
|
||||
Each module's own tests and the catalogue check, at merge. On the proving machine, each tool answered
|
||||
Each module's own tests and the catalog check, at merge. On the proving machine, each tool answered
|
||||
through the mesh and each owned file checked in place, before any other machine is assigned. This
|
||||
document's tables are updated as each module lands.
|
||||
|
||||
@@ -146,8 +146,8 @@ a manifest. Not off-site: a lost machine loses its backups with its data, by the
|
||||
|
||||
## How it is checked
|
||||
|
||||
The catalogue check (`module check`, and the catalogue-wide test beside it) refuses a provider that
|
||||
The catalog check (`module check`, and the catalog-wide test beside it) refuses a provider that
|
||||
grants without saying what it keeps for its consumers, an irreplaceable item with neither a backup nor a
|
||||
redundancy, and any backup line written by hand (ADR 0233). The store rule this design stated before
|
||||
was held only by a controller test over the catalogue checked out beside it, never by `module check`. The weekly
|
||||
was held only by a controller test over the catalog checked out beside it, never by `module check`. The weekly
|
||||
restore test above, and the 48-hour status line, are the running checks.
|
||||
|
||||
@@ -68,7 +68,7 @@ Owning repositories: **mesh-controller** (the condition store, watchdogs, `docto
|
||||
calls, the hand-act log, the facts snapshot), **`mesh-host`** (the node-engine: the apply queue, report
|
||||
order, epoch refusal, the `report` verb, rollback witnessing), **mesh-tools** (the tool runner and the
|
||||
mesh MCP server: their heartbeat, passing every argument, health answers), **mesh-catalog** (the
|
||||
`operator-channel` seat's holder and channels, the watcher, the providers' retirement, the catalogue's
|
||||
`operator-channel` seat's holder and channels, the watcher, the providers' retirement, the catalog's
|
||||
composition check), **mesh-sdk** (the TypeScript providers' loop and its retirement), **mesh-lab** (the
|
||||
replays and the induced-failure scenarios).
|
||||
|
||||
@@ -680,7 +680,7 @@ runs, and a restore that builds a new store beside the live one for a person to
|
||||
**The facts snapshot.** The controller exports daily, and after any change of machines, assignments or
|
||||
seats: every machine (a stable pseudonym of the same length as its name, its role, operating system,
|
||||
C library, architecture, node-engine and tool runner builds), assignments, settings keys and their
|
||||
non-secret values, seats and their holders, the catalogue commit, and the versions the mesh runs of
|
||||
non-secret values, seats and their holders, the catalog commit, and the versions the mesh runs of
|
||||
the bus server, the store and the node-engine. No secret and no address: an address is replaced by one
|
||||
from a documentation range. It is kept in the artifact store as `facts/latest`, where the build seat
|
||||
reads it.
|
||||
@@ -768,7 +768,7 @@ replay covers what must not be done to it on purpose, and every rule keeps a liv
|
||||
- **It runs on the build seat**, asked by the controller when the forge's announcer says a pull request's
|
||||
head moved: the repository's own `merge-check.sh` in the mesh's Go toolchain with no container runtime
|
||||
socket, beside a throwaway store and bus of the versions the snapshot says run, then mesh-lab's replays.
|
||||
The verdict is the head commit's status `mesh/merge-gate`, an error never a pass. A catalogue change is
|
||||
The verdict is the head commit's status `mesh/merge-gate`, an error never a pass. A catalog change is
|
||||
judged by the controller the mesh runs; a node-engine change by it built with the change's validator.
|
||||
- **Replays live where their incident is**: a component's logic in its repository as `TestReplay<issue>`,
|
||||
what the mesh runs (the bus's release, the resolver under musl and glibc) in mesh-lab's `replays/`, all
|
||||
@@ -868,7 +868,7 @@ the `data` verb; `cleanup` extended to a module's own retired data, deleted by t
|
||||
holder after a last restore point; the controller's grant names `node-backup.backed-up`. Built on
|
||||
branches in mesh-controller (migration 0072), `mesh-host` (the installer's user list; the kept-directory
|
||||
report), mesh-catalog (the holder's measuring and deletion, every module's data, the providers' held
|
||||
sizes), the media catalogue and the photo application, not yet merged. **Not yet:** the TypeScript
|
||||
sizes), the media catalog and the photo application, not yet merged. **Not yet:** the TypeScript
|
||||
providers saying their consumers' sizes (until then a consumer at two of them is `data-held-twice`, a
|
||||
warning, not compared), and SMART read under an array.
|
||||
|
||||
@@ -932,7 +932,7 @@ and "the commit is the build at hand"), on branches, not yet merged:
|
||||
| Repository | Delivers |
|
||||
|---|---|
|
||||
| mesh-controller | the planner's one mapping of a changed file onto modules and its one answer of a merge's reach, asked by the merge handler, the what-if, the gate and the check; a file no build reads touches nothing (issue 280's *left open*); every pull request answered — the gate when it reaches a module, the repository's own check for the mesh's repositories, a pass that says so otherwise; the gate run by the build seat with its judge chosen from the graph, composing the plan's definitions only, failing only a manifest problem the change brings; the delivery plan computed and carried with the verdict; a build of a commit off its module's trunk recorded and never registered; its own manifest naming every verb of its seat |
|
||||
| mesh-catalog | the forge's announcer saying, with every pull request, the directories holding a module at its head, the files it deletes and whether it has a `merge-check.sh`; both statuses and the delivery plan on the pull request; `gitea_branch_protection_get` and `gitea_branch_protection_set`; the catalogue's own check |
|
||||
| mesh-catalog | the forge's announcer saying, with every pull request, the directories holding a module at its head, the files it deletes and whether it has a `merge-check.sh`; both statuses and the delivery plan on the pull request; `gitea_branch_protection_get` and `gitea_branch_protection_set`; the catalog's own check |
|
||||
| `mesh-host`, mesh-tools, mesh-sdk, mesh-lab, mesh-media-catalog, and the applications built from their own repositories | each its own `merge-check.sh`; mesh-tools' tests each on a bus of their own |
|
||||
| mesh-tools-go, mesh-tools | a C compiler and Python in the Go toolchain, git in the TypeScript one |
|
||||
| hq | its own `merge-check.sh`, running `records.py`, `index.py` and `cycle.py` |
|
||||
|
||||
@@ -696,7 +696,7 @@ Each phase ends at its own *done when*. Owning repositories from [`repos.md`](..
|
||||
|---|---|---|---|
|
||||
| **1 — Fix the first holder in place** | D1–D4 in the output seat's holder and the watcher, then D5–D10; no change of shape. The operator then makes the two bots and the mesh is given their values through the controller | mesh-catalog | the holder's tests for D1–D4 pass; a live test message reaches the phone from both the holder and the watcher |
|
||||
| **2 — The desk's actions** | `node-notifier.send` gains actions; the dunst holder emits the chosen one; the launcher's prompt returns typed text; the screen-lock holder emits lock and idle changes | mesh-catalog | a notification with two actions returns the chosen token as an event; locking emits an event |
|
||||
| **3 — The seats, and the router** | the kinded bench in the seat set; `kind` and `capabilities` on a claim; `channel-capabilities/1` and its contract tests; publishing on a seat's event subjects and its grant; `channel` and `intake` seats; the output seat's holder becomes the router and holds `channel/desktop` and `intake/desktop`; presence as current state; the operator's identity list and identity check in the controller, the router's `trusted` stamping and untrusted input (§4); the content rule allowing machine names, references and `detail`, refusals naming the offending part (§9) | mesh-controller (seat set, claim fields, registration refusals, grants, the identity list and check); mesh-sdk and mesh-tools (seat-event publishing in the shared library and the tool runner); mesh-catalog (the seats' definitions, the router) | the catalogue refuses an unknown capability and a second holder of one kind; a message is routed to the desk by capability and context; an envelope from an identity not on the list is re-emitted untrusted; a path is refused to its sender by name, and travels as a reference the desk opens |
|
||||
| **3 — The seats, and the router** | the kinded bench in the seat set; `kind` and `capabilities` on a claim; `channel-capabilities/1` and its contract tests; publishing on a seat's event subjects and its grant; `channel` and `intake` seats; the output seat's holder becomes the router and holds `channel/desktop` and `intake/desktop`; presence as current state; the operator's identity list and identity check in the controller, the router's `trusted` stamping and untrusted input (§4); the content rule allowing machine names, references and `detail`, refusals naming the offending part (§9) | mesh-controller (seat set, claim fields, registration refusals, grants, the identity list and check); mesh-sdk and mesh-tools (seat-event publishing in the shared library and the tool runner); mesh-catalog (the seats' definitions, the router) | the catalog refuses an unknown capability and a second holder of one kind; a message is routed to the desk by capability and context; an envelope from an identity not on the list is re-emitted untrusted; a path is refused to its sender by name, and travels as a reference the desk opens |
|
||||
| **4 — Asks, and operator messages** | `ask`, `ask cancel`, `asks`, `answer`; the events; kinds, life, limits, batching, history; escalation; addressing, the register of agents with kept messages, the responder; the untrusted-input rule in every agent module's instructions (§5) | mesh-catalog | the router tests of ADR 0234 pass; live: an agent's question answered at the desk, and with the desk locked, on the phone; `@mesh status` answered; a message to a stopped agent kept and said |
|
||||
| **5a — Asks on the bus** (ADR 0259) | the kinded bench, caller-named verbs, `proof` subjects and the records users read (controller); a bundle's seat traffic, compare-and-set state and its own account (tool runner); the asker's and channel's types (shared library); the router's asks, tokens, identities, judging, warrants, expiry and linking with its hour (messenger); `telegram` and `desk-channel`; the controller asking for its conditions' actions and acting on warrants | mesh-controller, mesh-tools, mesh-sdk, mesh-catalog | a held delivery released from the phone; the hand-act reads "the operator, via telegram, user id verified"; a desk Silence goes through an ask |
|
||||
| **5 — Authorise, with TOTP** | `authorises` in the verb table; the router's checks for the code; direct calls refused, break-glass; `retire approve` takes `expect`; the hand-act fields; the TOTP seed as the router's state, enrolled at a terminal with ten recovery codes; `factor recover`, `factor status`, the local-only break-glass enrolment; no P2 tier before recovery codes exist; optional key enrolment; the self-check probe for the away channel; agents' grants lose the authorising verbs | mesh-controller; mesh-catalog (agents' grants, the desk's code prompt) | one controller test per refusal passes; at the desk, approve needs a code; a recovery drill re-enrols with a recovery code, and is announced |
|
||||
|
||||
@@ -130,10 +130,10 @@ source revision is derived from the keys: every **want** equals its module's **f
|
||||
- **The forge bridge** turns the forge's events into stream messages and is the only part that calls the
|
||||
forge.
|
||||
- **The indexer** computes each module's fingerprint at the merge preview and writes the candidates of each
|
||||
group and the modules of the trunk. At each trunk merge of the catalogue it also reads the catalogue's
|
||||
group and the modules of the trunk. At each trunk merge of the catalog it also reads the catalog's
|
||||
**placement files**, one per node, into its state `placement`: one key `placement.<node>` with that node's
|
||||
modules, their pins, host ports and bindings, and the source revision. It adds the nodes to `modules.<module>`
|
||||
and says `placement-changed` only for a node whose key differs (ADR 0549 §1, §2). While a catalogue pull request
|
||||
and says `placement-changed` only for a node whose key differs (ADR 0549 §1, §2). While a catalog pull request
|
||||
that changes a placement file is open, the forge bridge carries its placement files at the head and the indexer
|
||||
keeps, at each head, each node's file that differs from the trunk's as a **placement candidate** in a state of its
|
||||
own, `placement-candidates`, one key `<group>.<node>`, deleting the group's other keys, and all of them when the
|
||||
@@ -229,7 +229,7 @@ there is three milestones, one issuer of each key at every moment (ADR 0547 §11
|
||||
elsewhere but the bus's user list, which the bus node keeps able to follow until the third milestone so `hand-back` has a list to return to.
|
||||
3. **Nothing is left with the controller** (issue 774; what remains of issue 1126, the homes of the controller's
|
||||
other jobs, carries into it by a note on 1126 and is not one of its issues): the vault mints bus accounts, the grants part
|
||||
writes grants, the try is the checker's own verb, placement is read from the catalogue alone (ADR 0549 §5,
|
||||
writes grants, the try is the checker's own verb, placement is read from the catalog alone (ADR 0549 §5,
|
||||
§6), the vault signs with the mesh's signing key (ADR 0550 §2), and the controller's lease is handed to a holder that is no controller once every other job
|
||||
it keeps has a home by its own decision (research 072).
|
||||
|
||||
@@ -269,7 +269,7 @@ The target (ADR 0547):
|
||||
- **The controller** is no part of build and delivery (ADR 0547 §1). The jobs it still keeps outside them
|
||||
(nodes and adoption, identities, secrets and given values, licences, config moves, conditions and facts, ADR
|
||||
0429 §9) each move by a decision of their own under research 072 before it loses its lease. Two are decided, by
|
||||
the operator's answers of 2026-10-26: placement lives in the catalogue (ADR 0549), and the mesh's signing key in
|
||||
the operator's answers of 2026-10-26: placement lives in the catalog (ADR 0549), and the mesh's signing key in
|
||||
the vault (ADR 0550 §2).
|
||||
- **The vault** keeps every secret the mesh keeps (ADR 0550 §1): the mesh's signing key, licence grants and
|
||||
tokens, bus account passwords and module secrets; a holder gets a value only sealed to its own key, or asks the
|
||||
@@ -411,7 +411,7 @@ default yielded so the node-engine and the controller shared one tier (ADR 0539
|
||||
**The SDK by version.** A Go module names the SDK by its released version and fetches it from the mesh's forge,
|
||||
checked against its recorded checksum; vendored copies go. Inside a group, a member may name the SDK member's
|
||||
head: its merge check resolves that commit from the forge and builds against it, and the head stays fetchable
|
||||
after the group merges because the forge keeps every pull request's head. The catalogue's check accepts an SDK
|
||||
after the group merges because the forge keeps every pull request's head. The catalog's check accepts an SDK
|
||||
commit on the SDK's trunk or the head of an SDK pull request the forge holds, open or merged (ADR 0402 §2 to §4).
|
||||
|
||||
## Checks
|
||||
@@ -514,7 +514,7 @@ read the setting: it reads `turns`, finds none, and claims its checks at once.
|
||||
bucket in the state `running` names, among its trunk heads, a repository the merge goes into; the verdict of
|
||||
the merging delivery's own heads excepted. The gate already watches that bucket, so the verdict leaving
|
||||
`running` (a pass, a failure, its key deleted) gives the waiting merge its turn at once, with no timer and no
|
||||
new event. Siblings a check reads beside are not held: the catalogue is read beside nearly every check. A merge
|
||||
new event. Siblings a check reads beside are not held: the catalog is read beside nearly every check. A merge
|
||||
made at the forge by hand is not held; the check it lands under ends on the commit it read and is then carried
|
||||
(ADR 0548 §2).
|
||||
- **The merge turn.** Before it reads the checker's verdicts for an approved merge, the gate writes one key per
|
||||
@@ -692,7 +692,7 @@ backup holder backs up nothing, and the have says so.
|
||||
- **What a node composes from, and who writes it.** One composer, mesh-sdk's, has three callers: the node agent for
|
||||
its own node, the assigner's composition check for every node, and the grants part. Each fills the composition's
|
||||
input from the bus with its own credential and nothing else (ADR 0547 §2, ADR 0549 §3):
|
||||
- **placement**, the indexer's state `placement`, one key `placement.<node>` read at each catalogue merge from
|
||||
- **placement**, the indexer's state `placement`, one key `placement.<node>` read at each catalog merge from
|
||||
the node's placement file and said by `placement-changed` (ADR 0549 §2);
|
||||
- **the node records**, each in its node's own have key `<node>.node`, written by that node alone and heard as
|
||||
the key's change (ADR 0555 §3);
|
||||
@@ -706,7 +706,7 @@ backup holder backs up nothing, and the have says so.
|
||||
While both sources stand (ADR 0549 §5), mesh-config resolves values for a module either the placement or the
|
||||
controller's assignments name on a node, and the grants part composes both, holding a seat held once for the mesh
|
||||
where the placement says.
|
||||
- **The placement file.** One file per node in the catalogue, changed only by a pull request and checked before
|
||||
- **The placement file.** One file per node in the catalog, changed only by a pull request and checked before
|
||||
it merges by the nodes it places (ADR 0549 §1, ADR 0555, ADR 0559). It holds what a person decides of the node and nothing a machine observes:
|
||||
- the node's decided facts: its endpoint and site, whether it is the private network's hub, its address on the
|
||||
private network (a decision written once at enrolment; nothing allocates one at run time), the domain its
|
||||
@@ -720,7 +720,7 @@ backup holder backs up nothing, and the have says so.
|
||||
since an empty layer still carries the module's config values; and, for a module in the settings form only,
|
||||
the config values a person set in each of those layers (on the day of the seed the media modules of one node);
|
||||
- its pins by provision, and the seats its modules hold.
|
||||
The catalogue's check refuses a placement naming a module it does not hold or a node not enrolled, two modules
|
||||
The catalog's check refuses a placement naming a module it does not hold or a node not enrolled, two modules
|
||||
of one node on one host port, a pin, binding or holding naming a provider not placed where it says, a seat held
|
||||
once for the mesh held twice, two nodes with one address and more than one hub, each named in one answer.
|
||||
- **A node answers a placement change before it merges.** The node agent watches every placement candidate. For
|
||||
@@ -754,7 +754,7 @@ backup holder backs up nothing, and the have says so.
|
||||
holds, without these reads, and nodes keep applying while composing their own fails loudly until the next
|
||||
`take-over`. mesh-sdk's composer test holds the copy, user for user, to every grant of the controller's golden
|
||||
plus exactly these reads and the bucket-creation advisories of issue 1790.
|
||||
- **A node composes its own once the catalogue places it.** Its agent reads its key `placement.<node>` at start:
|
||||
- **A node composes its own once the catalog places it.** Its agent reads its key `placement.<node>` at start:
|
||||
with one it composes its own, without one it applies the controller's compositions as before, so nothing changes
|
||||
on a node before the seeded placement file reaches it; it follows the key, and when it appears the agent begins
|
||||
again composing its own, with no restart. A node never applies a stand-in: a composition that stands
|
||||
@@ -768,7 +768,7 @@ backup holder backs up nothing, and the have says so.
|
||||
`placement-export`, the node-engine (its record and its own composer), the vault's `own`, the grants part's reads,
|
||||
mesh-config's and the assigner's reading of placement, and the indexer writing it. Nothing changes on a node by
|
||||
that delivery alone, since no placement file exists yet: the node records appear, and the grants part's list gives
|
||||
the nodes their reads. Then the seed: one catalogue pull request writes each node's placement file from the
|
||||
the nodes their reads. Then the seed: one catalog pull request writes each node's placement file from the
|
||||
controller's export, and its composition check passes only when every node it places has composed it in shadow
|
||||
and answered that what it would run is the same (ADR 0559 §1); only then is it merged. The indexer writes `placement`, each node begins composing its own, and the assigner
|
||||
writes its wants as the build alone.
|
||||
@@ -797,7 +797,7 @@ backup holder backs up nothing, and the have says so.
|
||||
and nothing is asserted (ADR 0547 §5).
|
||||
- **The want names the build.** A want `want.<node>.<module>` names the fingerprint, the artifact's digest, the
|
||||
source revision and whether the module holds data; the node's own want `want.<node>.node` names the node
|
||||
(ADR 0547 §2). A want to a node the catalogue places says so: it carries `self` and names no composition,
|
||||
(ADR 0547 §2). A want to a node the catalog places says so: it carries `self` and names no composition,
|
||||
since the node composes it; every other want still names the one composition it allows, so nothing written
|
||||
before (the controller's bus upgrade among it) reads a want differently. A person's yes to a move the node
|
||||
judged is written into the want as `yes`, the digest of the composition allowed (ADR 0547 §3).
|
||||
@@ -881,7 +881,7 @@ and `delivered` events.
|
||||
*ADR 0408, ADR 0429 §17, ADR 0539 §4, §12, §13.*
|
||||
|
||||
From release 6, module, source revision, build, want and have are one chain, each link answerable from the next
|
||||
(ADR 0429 §17). Placement is the catalogue's, read onto the bus by the indexer (ADR 0549 §1, §2); the controller's
|
||||
(ADR 0429 §17). Placement is the catalog's, read onto the bus by the indexer (ADR 0549 §1, §2); the controller's
|
||||
assignments bucket is kept equal to it until ADR 0547's third milestone and is a source of nothing from then
|
||||
(ADR 0549 §5, §6). The rollouts bucket is kept read-only with its history: no rollout is written after release
|
||||
5's last plan (ADR 0539 §12).
|
||||
@@ -891,9 +891,9 @@ assignments bucket is kept equal to it until ADR 0547's third milestone and is a
|
||||
| build | its module, repository and commit, its reuse key, what it published: an artifact by digest or a package release by name and version | the controller | `mesh-controller_builds` |
|
||||
| want | the fingerprint a node should run of a module, and the digest of the one composition it allows; the node's own want names its node composition | the assigner | `mesh-delivery-assigner_wants` |
|
||||
| have | what the node runs of a module: the fingerprint, the composition's digest, the phase and the health | the node agent | `node-engine_have` |
|
||||
| placement | each node's modules with their pins, host ports and bindings, from the catalogue's placement file of that node at each trunk merge of the catalogue | the indexer | the indexer's state `placement` |
|
||||
| placement candidate | while a catalogue pull request that changes placement is open: one node's placement file at its head, beside the chain and never composed into what runs (ADR 0559 §3) | the indexer | the indexer's state `placement-candidates` |
|
||||
| assignment | until ADR 0547's third milestone: the module and node, pinned or not, ended or not, written by `assign` and `unassign` after a merged placement change, so that it equals the catalogue (ADR 0549 §5) | the controller | `mesh-controller_assignments` |
|
||||
| placement | each node's modules with their pins, host ports and bindings, from the catalog's placement file of that node at each trunk merge of the catalog | the indexer | the indexer's state `placement` |
|
||||
| placement candidate | while a catalog pull request that changes placement is open: one node's placement file at its head, beside the chain and never composed into what runs (ADR 0559 §3) | the indexer | the indexer's state `placement-candidates` |
|
||||
| assignment | until ADR 0547's third milestone: the module and node, pinned or not, ended or not, written by `assign` and `unassign` after a merged placement change, so that it equals the catalog (ADR 0549 §5) | the controller | `mesh-controller_assignments` |
|
||||
| rollout | read-only from release 6: each plan of releases 1 to 5, its tiers, the builds it carried and the assignments it changed | nobody from release 6 | `mesh-controller_rollouts` |
|
||||
|
||||
Beside the chain, on the bus too: deliveries and groups (the gate's `deliveries` and `groups`, ADR 0239 §37), one
|
||||
@@ -1025,7 +1025,7 @@ way is written down before the switch (ADR 0526, which ADR 0522 §6 asked of R4g
|
||||
|
||||
What the build proves before this design is implemented (research 055's direction):
|
||||
|
||||
1. A feature across the controller and the catalogue with a new seat verb is delivered as one group, with no
|
||||
1. A feature across the controller and the catalog with a new seat verb is delivered as one group, with no
|
||||
`after:` line, one composed check and one Approve: its merges move one source revision, each module it changes
|
||||
is built once, and every want naming one of those fingerprints is written by the assigner's six rules, with
|
||||
no order written by anyone.
|
||||
@@ -1052,7 +1052,7 @@ What the build proves before this design is implemented (research 055's directio
|
||||
newer fingerprint once its rules hold, a fingerprint nobody wants any more is never applied, and nothing is
|
||||
released by a person or held in the gate.
|
||||
12. No table in the controller's store holds builds, deliveries or assignments: builds are the builder's keys,
|
||||
wants the assigner's, haves the node agents', and placement the catalogue's placement files, read onto the bus
|
||||
wants the assigner's, haves the node agents', and placement the catalog's placement files, read onto the bus
|
||||
by the indexer, with the controller's assignments bucket kept equal to them until ADR 0547's third milestone
|
||||
(ADR 0549).
|
||||
|
||||
@@ -1113,7 +1113,7 @@ development, the check's share about a minute):
|
||||
after one failed package runs that package alone; a check whose inputs equal an earlier one's runs nothing
|
||||
and takes every kept verdict. Measured on the status's line: the tests run and kept at each level, and the
|
||||
failed test it names.
|
||||
23. A library or catalogue change's composition check composes only the machines it touches, and clones no
|
||||
23. A library or catalog change's composition check composes only the machines it touches, and clones no
|
||||
repository but at most the change's own. Measured in the check's report: the machines composed, against the
|
||||
machines that run or could run a module the change moves or adds, and the repositories cloned.
|
||||
24. A pull request carries one required status, and a delivery group one verdict on every member's head, its
|
||||
@@ -1188,7 +1188,7 @@ release reports the controller's code lines against research 063's baseline (acc
|
||||
| 1 | A merge during a running delivery is never dropped and needs no person's release; the merge window lives on the bus; a rollout that shares nothing with an open one runs beside it | 8, 9, 11 | mesh-controller | 973 (with the rest of 525) |
|
||||
| 2 | A ready mark is acted on within seconds, and each pull request has at most one waiting check | 5, 6, 7 | mesh-controller, mesh-catalog (mesh-delivery, gitea) | 974, 975, 976 |
|
||||
| 2b | build-agent and route-proxy live whole in mesh-controller (ADR 0420, accepted 2026-10-17): the controller accepts a reviewed move, mesh-delivery announces each merge with its Approve, and the two modules move; built as two releases before the rule that a milestone is one release | — | mesh-controller, mesh-catalog (mesh-delivery, build-agent, route-proxy) | release 1: 1025; release 2: 1019 |
|
||||
| 3 | A feature with a new seat verb across the controller and the catalogue is one group, one batch, one delivery plan and one Approve: no order lines, no ready mark, each module built and sent once | 1, 6 | mesh-controller, mesh-catalog (mesh-delivery, gitea) | 977, 978, 1022, 895 (with 1046 folded into it: unit and integration tests split, each level within its bound); 605 (1021 closed as its duplicate) is resolved by this build (ADR 0419 §7) |
|
||||
| 3 | A feature with a new seat verb across the controller and the catalog is one group, one batch, one delivery plan and one Approve: no order lines, no ready mark, each module built and sent once | 1, 6 | mesh-controller, mesh-catalog (mesh-delivery, gitea) | 977, 978, 1022, 895 (with 1046 folded into it: unit and integration tests split, each level within its bound); 605 (1021 closed as its duplicate) is resolved by this build (ADR 0419 §7) |
|
||||
| 4 | A delivery backs up the data it moves first, and a failed backup holds it and is said | 3 | mesh-controller, mesh-catalog (mesh-delivery, the backup seat's holder) | 980, split into 1065, 1066, 1067, 1068 |
|
||||
| 3 fix | A group approved together merges in one pass and goes out in one delivery plan, also when its members' tests read each other's repositories | 1 | mesh-catalog (mesh-delivery, gitea) | 1082, 1049, 1047, 1083, 1079 (1084 closed as its duplicate) |
|
||||
| 5 | One read of a module on a node names the commit, build and rollout that put it there | 10, 12 | mesh-controller, mesh-catalog (mesh-config, nats) | 981 |
|
||||
@@ -1206,8 +1206,8 @@ release reports the controller's code lines against research 063's baseline (acc
|
||||
| R3c | The forge bridge, the indexer and the teller run beside today's mesh-delivery and write their keys; the view shows modules against the controller's source fingerprints; nothing is merged by them yet (ADR 0437) | 15 in part | mesh-delivery (forge bridge, indexer, teller, view), mesh-catalog (the forge module) | 1235 (the forge bridge writes pull messages on FORGE), 1236 (its merged message, with the trees of the merge commit), 1237 (its messages for the pull requests open at its start and each reconnect), 1238 (the indexer writes the candidate groups), 1239 (the indexer writes the modules' fingerprints at a trunk merge, and 1209: none for an image that compiles a Go program from a context), 1240 (the teller runs and carries nothing yet), 1241 (the view shows the candidate groups and each module's fingerprint against the controller's); in the forge module: 1234 (a merge commit's trees in one call, and the head branch in the open pull requests); one delivery group, mesh-catalog first. Its prepare step found three orders: the forge had no tool for a commit's trees, so the forge module joins the milestone as a member of its group; and mesh-sdk shipped two layouts before it, the controller's build record (1223) and a build's directory and a pull request's module directories (1233). It also found that the bridge's FORGE is a stream the others hear by `consumes`, not a bucket they `read`; that the broker test of the three credentials goes into R3d with the gate's (its issue is R3d's); and that the parts hand-write their bus connection until mesh-sdk gives one (1242): facts for ADR 0437's next insight, with R3d's |
|
||||
| R3d | The gate's keys can carry what the forge needs and the controller answers a what-if as data, so the gate has what it reads before it runs; a failed delivery plan asks the operator Retry on the phone, and a seat whose holder is assigned nowhere passes to its only claimant; the operator sees only the Retry notice (ADR 0450, ADR 0449) | — | mesh-sdk, mesh-controller | in mesh-sdk: 1321 (a merge key carries the Approve's heads), 1322 (an owed act carries a status's target, a commit without a pull request, two comments), 1323 (a name a key cannot hold is escaped); in mesh-controller: 1324 (what-if as data), 1325 (the build seat reads the gate's pull keys), 1326 (the gate's buckets writable by its runner only), 1243 (the three parts' credentials); and, delivered first as feature no-terminal (ADR 0449): 1317 (the seat passes to its only claimant), 1314 (a failed delivery plan raises a condition), 1315 (Retry offered and performed), 1316 (a refused answer is told). Its prepare step found four things the gate reads that ship before it, which cut the former R3d in two (ADR 0450), and two steps on the path that needed the operator's terminal (ADR 0449) |
|
||||
| R3e | A group reaches the mesh as one source revision written by the gate; the merge's Approve is asked by the teller and answered on the phone until issue 953; the view shows the open asks; today's module mesh-delivery is carried over into the gate when it first holds the seat and ends; no step needs the operator's terminal (ADR 0437, ADR 0450) | 15 | mesh-delivery (gate's parts: forge bridge, teller, indexer, view), mesh-catalog (the gate, mesh-delivery removed, mesh-issues), mesh-tools (the tool runner's waits) | opened when it is prepared, from R3d's survey; the controller's deletions it makes possible go to release 4 (their issue is R4g's) |
|
||||
| R4a | The shared layouts of release 4 are on mesh-sdk's main before their users pin them: the catalogue, the validator and the composition over a plain input, the fingerprint's new form, and the key layouts of the builder, the checker, the kept verdicts, the manifests and the candidates; the node-engine reports the user namespaces capability (ADR 0453 clauses 1, 2, 3) | — (2 needs it) | mesh-sdk, the node-engine (`mesh-host`) | in mesh-sdk: 1353, 1354, 1355, 1356, 1357, 1358, 1359, 1360, 1361, 1362, 1363, 1364, 1298 (the composition over a plain input); in the node-engine: 1308 (the user namespaces capability) |
|
||||
| R4b | mesh-controller and the node-engine use the catalogue, the validator and the composition from mesh-sdk and delete their copies; the controller composes every node both ways on the live facts and the two agree before this is delivered; it writes the grants and credential bindings the assigner reads; its unit level meets its bound (ADR 0453 clauses 1, 3) | — | mesh-controller, the node-engine (`mesh-host`) | 1375 (to be split when it is prepared), 1337 (the unit level's bound) |
|
||||
| R4a | The shared layouts of release 4 are on mesh-sdk's main before their users pin them: the catalog, the validator and the composition over a plain input, the fingerprint's new form, and the key layouts of the builder, the checker, the kept verdicts, the manifests and the candidates; the node-engine reports the user namespaces capability (ADR 0453 clauses 1, 2, 3) | — (2 needs it) | mesh-sdk, the node-engine (`mesh-host`) | in mesh-sdk: 1353, 1354, 1355, 1356, 1357, 1358, 1359, 1360, 1361, 1362, 1363, 1364, 1298 (the composition over a plain input); in the node-engine: 1308 (the user namespaces capability) |
|
||||
| R4b | mesh-controller and the node-engine use the catalog, the validator and the composition from mesh-sdk and delete their copies; the controller composes every node both ways on the live facts and the two agree before this is delivered; it writes the grants and credential bindings the assigner reads; its unit level meets its bound (ADR 0453 clauses 1, 3) | — | mesh-controller, the node-engine (`mesh-host`) | 1375 (to be split when it is prepared), 1337 (the unit level's bound) |
|
||||
| R4c | The new parts run dark beside today's: the indexer writes the manifests by digest and the fingerprints in the new form beside today's (its own, first delivery), then the build modules (the Go build module, the Node and TypeScript build module: toolchain, sandbox, fetch, digest, test, build), the builder, the checker writing `mesh/check` as a status the forge does not require, and the assigner's composition check writing into the checker's report; the build seat's accounts are decided by a record before the build modules' pull requests are opened (ADR 0453 clauses 4, 6, 7, 8, 11, 12, 16; ADR 0465 §4); the builder is ADR 0478's. It is delivered in ADR 0465 §5's order: mesh-sdk's layouts; the forge bridge and the indexer with the controller's facts in format 2; the build modules; the builder; the checker; the bus user list's composer moved into mesh-sdk; the assigner's composition check; the fetch and run steps. The build modules each run as an account of their own with a forge key known read-only on one forge user (ADR 0466); each builds with the node's installed toolchain at the agreed version and does not refuse on a node with another (ADR 0477, issue 1572), answers a long step with its id and finishes it on a key, is called on a named machine, and runs on the four nodes that run the build agent today (ADR 0469) | 22, 23 (measured on the dark run, proved in R4f) | mesh-delivery (indexer, builder, checker, assigner's composition check), mesh-catalog (build modules, ssh-client, gitea), mesh-controller (the build agent's whole-check tests, the script's steps as tests), mesh-sdk | 971 (its record first), the assigner's composition check (its issue is R4g's), 1309 (the fetch and run steps), 1287, 1288, 1289, 1290, 1291, 1292 (the checker; the build modules run the tests), 1251; the steps of 1293, and of R4g's issue on the old checks, that are no test become tests in R4c to R4e; the rest opened when it is prepared |
|
||||
| R4d | Done (2026-10-21 to 2026-10-23): three modules stopped being built from a Dockerfile, each its own release step: route-proxy, nats and mail's autoconfiguration module (automx, now a Go program) run a pinned stock image with their Go program mounted as an archive (ADR 0480); the image kind is allowed again, built rootless by the image build module (ADR 0487); route-proxy and nats were one delivery group, nats upgraded once on the bus at the operator's moment after a verified snapshot, automx went first alone (ADR 0497). n8n left R4d by the operator's word and keeps its own Dockerfile until R4g (ADR 0506) | — | mesh-catalog, mesh-controller (route-proxy), the node-engine (`mesh-host`: the container groups field, kept unused) | issues 1632 (automx), 1633 to 1635 (route-proxy), 1636 to 1638 (the container groups field), 1640 to 1642 (nats), all resolved and delivered; 1639 and 1755 (n8n) wontfix |
|
||||
| R4e | The controller asks no build: a build is keyed by its fingerprint, the new form becomes the only one as one group, and a merge builds nothing its check built; builders fetch over SSH, so an SDK change ships with its users; the vendored copies leave mesh-controller and mesh-sdk; delivered only once the builder's artifact digests equal build-agent's (ADR 0453 clauses 2, 4, 5, 6) | 16, 2, 4 in part | mesh-controller (builds are no longer asked), mesh-delivery (builder), mesh-sdk | the installed toolchains (their issue is R4g's), 1304, the controller asking no build (opened when it is prepared) |
|
||||
@@ -1216,7 +1216,7 @@ release reports the controller's code lines against research 063's baseline (acc
|
||||
| R6 | Each node applies from its wants the compositions its own node agent composes (ADR 0547 §2; as released it applied the controller's signed ones), a new version reaches the module's canary first and the rest once it is healthy there, a move a person must allow is asked before its want, the view says what is moving, settled, stuck and delivered, and the plans are deleted from the code; switched over by release 5's last plan (ADR 0539) | 18, 19, 20 | mesh-sdk, the node-engine (`mesh-host`: the node agent), mesh-controller (plans deleted; as released also compositions and credentials, which ADR 0547 moves to the node agents, the grants part and the vault), mesh-delivery (assigner, watchman, view), mesh-catalog (the gate, the messenger, the issue tracker, the backup holder), hq | 1942 (the planner puts the node-engine and the controller in one tier for the switch; delivered first), the bus's user list granting what release 6 uses (delivered first, under L1's issue), 1943 (mesh-sdk: the composition's layout and signature, the want's digest, `mesh-hand-compose`, the verb tables without the plan verbs), 1944 (the node agent), 1946 (the gate keeps the merge's states; the messenger and the issue tracker hear the view), 1906 (the assigner's seventh rule and its asks, the watchman's pulses for wants, the view's wants and events), 1130 (hq) |
|
||||
| L1 | The controller leaves build and delivery, first milestone: the bus node follows the user list alone, the grants part composes in shadow and the vault holds the bus accounts' hashes, so the operator's take-over can make the grants part's list the one loaded (ADR 0547 §8, §11; ADR 0550 §4) | — | mesh-controller, mesh-delivery, mesh-catalog (the vault, the grants part), the node-engine (`mesh-host`), mesh-sdk | 1945 |
|
||||
| L2 | Nodes compose: each node agent composes its own modules, and no node applies a composition made elsewhere but the bus's user list (ADR 0547 §11) | — | the node-engine (`mesh-host`), mesh-sdk, mesh-controller | 1126 |
|
||||
| L3 | Nothing is left with the controller: the vault mints bus accounts and signs, the grants part writes grants, the try is the checker's own verb, placement is read from the catalogue alone, and the lease passes to a holder that is no controller (ADR 0547 §11, ADR 0549 §5, §6, ADR 0550 §2); the rest of the controller's code lines is the issue of L2 | — | mesh-controller, mesh-catalog (the vault, the grants part), mesh-delivery | 774 |
|
||||
| L3 | Nothing is left with the controller: the vault mints bus accounts and signs, the grants part writes grants, the try is the checker's own verb, placement is read from the catalog alone, and the lease passes to a holder that is no controller (ADR 0547 §11, ADR 0549 §5, §6, ADR 0550 §2); the rest of the controller's code lines is the issue of L2 | — | mesh-controller, mesh-catalog (the vault, the grants part), mesh-delivery | 774 |
|
||||
| M1 | A milestone is a record of the issue tracker: each row of a design's milestone table is a milestone record its issues are linked to by `issue-tracker.link`, an issue to at most one, and pull requests and commits to issues the same way; the knowledge base's `progress` reads the milestones from the tracker, not from this table (ADR 0493 §1 to §4) | — (ADR 0493 §13 in part) | hq, mesh-catalog (mesh-issues, mesh-knowledge) | 1781 |
|
||||
| L4 | Health leaves the controller: the condition keeper holds the seat `mesh-conditions` and keeps every open condition from the observers' findings, saying today's condition events; the watchman holds `mesh-watchdogs`, its watchdogs pulses, and keeps the dead letters from the lease write; `mesh-self-check` runs the probes and says `checked`; `mesh-healers` heals on events and acts only once the lease names no controller; `status` is the view's derivation; built dark, compared, then switched by the operator's take-over; the bus module gains a tool that removes one named consumer, which the lease write uses (ADR 0564, ADR 0547 §11) | — | mesh-sdk, mesh-catalog (mesh-conditions, mesh-self-check, mesh-healers, the router, mesh-watcher, nats), mesh-delivery (watchman, view, the grants part) | 1952 |
|
||||
| L5 | Control records leave the controller: mesh-config keeps each module's declared config and the predefined keys in its own buckets and hears a pull request's config declarations from the indexer; each node agent writes its node's declared packages beside its compositions; the upgrade policy and the bus step are answered by their new owner; the hand-act log (`hand-act`, `hand-acts`, `warranted`) is a stream of a seat of its own; `settings`, `move-config` and `move-placement` go (a decision record first, under research 072; ADR 0348 §9, ADR 0502 §2, ADR 0539 §14, §15, ADR 0547 §11) | — | mesh-sdk, mesh-controller, mesh-catalog (mesh-config, pacman), mesh-delivery (indexer, assigner), the node-engine (`mesh-host`) | 1953 |
|
||||
|
||||
@@ -257,7 +257,7 @@ health needs a record of its own.
|
||||
- **`module check`** refuses a `health` field naming an endpoint the module does not declare under
|
||||
`listens`, a port or an address, an interval, timeout, count or grace outside its bounds, a tool the module
|
||||
does not serve, or a `tool` kind with no other kind beside it on the module.
|
||||
- **The bed.** mesh-lab provides a throwaway container runtime that the catalogue's check, run on the build
|
||||
- **The bed.** mesh-lab provides a throwaway container runtime that the catalog's check, run on the build
|
||||
seat (ADR 0237),
|
||||
uses to start every resource whose declaration or image changed, alone, and read its check. It must say
|
||||
healthy within its grace. A check that names a provision in `needs` gets no provider on the bed, so there it
|
||||
@@ -273,7 +273,7 @@ health needs a record of its own.
|
||||
nothing long-lived and need no declaration.
|
||||
|
||||
1. **Liveness first, with no declarations** (Phase A). From then every long-running resource is judged. The
|
||||
catalogue's count of long-running resources without `health` is written down and starts.
|
||||
catalog's count of long-running resources without `health` is written down and starts.
|
||||
2. **The seven modules whose images ship checks** adopt them by name; for the two that were wrong in the
|
||||
mesh's configuration, the corrected check is what the bed proves. 19 containers are covered at once.
|
||||
3. **The other container modules, and the containers without an image check in three of the seven,** declare
|
||||
@@ -384,11 +384,11 @@ health of this design, not yet through the machine's own judgement as §10 does
|
||||
|---|---|---|---|
|
||||
| A — liveness and the statement | `mesh-host` (node-engine) | liveness for every long-running resource; restarts counted and kept; the state per resource in the report; the change event and its repetition; nothing restarted on health | the node-engine tests of ADR 0240 rules 1 and 6 pass; every machine's report carries a state for every long-running resource |
|
||||
| | mesh-controller | the last state per machine; `module.<module>.<machine>.unhealthy` on the second statement, cleared on the first that does not say it; the gate reading the stated health; `node show` and `status` | the controller tests of rule 4 pass; mesh-lab's replay of the crash loop fails its gate within the bound; a module stopped on purpose on the live mesh is raised and cleared |
|
||||
| B — the field | mesh-controller | `health` parsed on every long-running resource; `module check`'s refusals; the catalogue-wide parse | a test per refusal; the whole catalogue passes `module check` |
|
||||
| B — the field | mesh-controller | `health` parsed on every long-running resource; `module check`'s refusals; the catalog-wide parse | a test per refusal; the whole catalog passes `module check` |
|
||||
| | `mesh-host` (node-engine) | the scheduler and the kinds: http, tcp, unit itself; exec and runtime as the container's check; tool through the tool runner | the rule 3 tests pass; the replay of issue 145 raises the consumer within two looks |
|
||||
| C — the provider hold | mesh-controller | `needs` read against the provider composed for the consumer; the consumer's finding held under the provider's condition; the consumer's gate waiting | the rule 5 test (one provider, three consumers, one condition) passes |
|
||||
| D — the proof | mesh-lab, mesh-catalog | the bed; the catalogue's check starting every changed resource on it; adopted image checks proved | the replay of the studio's false *unhealthy* fails the bed, not a machine |
|
||||
| E — the migration | mesh-catalog, mesh-controller | the declarations of §9 steps 2–5; the count kept in the catalogue and its test; `module check` refusing after the date | the count is zero, or the date has passed and `module check` refuses |
|
||||
| D — the proof | mesh-lab, mesh-catalog | the bed; the catalog's check starting every changed resource on it; adopted image checks proved | the replay of the studio's false *unhealthy* fails the bed, not a machine |
|
||||
| E — the migration | mesh-catalog, mesh-controller | the declarations of §9 steps 2–5; the count kept in the catalog and its test; `module check` refusing after the date | the count is zero, or the date has passed and `module check` refuses |
|
||||
| F — the machine's network | `mesh-host` (node-engine), `mesh-controller`, `mesh-catalog` | §10: the five parts judged on two looks and stated; the three conditions said once; the gate waiting on another's; `node show` | the tests of ADR 0241 pass; the drill's replay raises and clears; a VPN connect on the laptop is raised naming its writer and cleared by the reconcile |
|
||||
| G — the machine's hardware | `mesh-catalog` | §11: the `sensors` module, its tools, the drive reader and the fan watch, both checks declared | its tests pass; `module check` accepts its health; on the first machine both checks are stated healthy |
|
||||
|
||||
@@ -448,7 +448,7 @@ open:
|
||||
composes the field — the endpoint as the port the machine published it on, the defaults written out — only
|
||||
for a machine whose newest statement says so, and strips it for an older engine, which parses strictly
|
||||
and would refuse the whole declaration. So the engine rolls out first, then the controller, and the
|
||||
catalogue declares only once that controller runs: until then the composition check refuses a declaration, which
|
||||
catalog declares only once that controller runs: until then the composition check refuses a declaration, which
|
||||
is the protection working.
|
||||
- **The engine's looks.** http and tcp from the machine to the published port; a redirect is an answer and
|
||||
is not followed; a certificate is not judged. A unit's readiness is read from the show liveness already
|
||||
@@ -481,8 +481,8 @@ machines: one condition, urgent, listing all three; their gates wait; after reco
|
||||
failing is raised), which fails with four conditions when the hold is taken out.
|
||||
|
||||
**Phase D — the bed.** In mesh-lab's replays, which every merge check of a module of the graph already runs
|
||||
from the lab's trunk with the runtime and the change's catalogue — so nothing else had to change for the
|
||||
catalogue's check to start it. It proves the resources whose declared check or image the change touches
|
||||
from the lab's trunk with the runtime and the change's catalog — so nothing else had to change for the
|
||||
catalog's check to start it. It proves the resources whose declared check or image the change touches
|
||||
(every declared one when it cannot tell), each alone: its literal environment, the files its module writes
|
||||
in full, an empty place for every other mount, no secret and no provider. A program that stays up while its
|
||||
check does not see it fails the change; one that does not stay up alone is said and left to the first
|
||||
@@ -490,15 +490,15 @@ machine's gate; a mesh-built image and a tool or unit check are said. The replay
|
||||
bound to the address its hostname names, and an image check asking the loopback — fails the bed, and passes
|
||||
once the hostname is set to every address.
|
||||
|
||||
**Phase E — the migration.** The catalogue keeps the count in a file of its own, and its merge check fails
|
||||
**Phase E — the migration.** The catalog keeps the count in a file of its own, and its merge check fails
|
||||
a change that raises it and one that lowers it without writing the new number. It starts at 93 long-running
|
||||
resources. The first batch takes it to 70: the image checks of the seven modules that ship them, adopted where
|
||||
they read healthy on the live mesh — except the mail antivirus, whose six-minute start is past the bound,
|
||||
and the mail cache, whose image ships none — and tcp or http on endpoints four services already declare.
|
||||
|
||||
Still to do for these phases' "done when": each read on the live mesh once rolled out in order — the
|
||||
node-engine, then the controller, then the catalogue's declarations; the count's first live reading; and
|
||||
the bed's first run on a catalogue change at the build seat.
|
||||
node-engine, then the controller, then the catalog's declarations; the count's first live reading; and
|
||||
the bed's first run on a catalog change at the build seat.
|
||||
|
||||
## As built — Phase F
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ shape — what each is for, where its records live, and how they depend on one a
|
||||
|
||||
| Domain | Purpose | Its records live in |
|
||||
|---|---|---|
|
||||
| **Module** | to say what one module is, in the manifest every other domain reads | the catalogue, one manifest per module |
|
||||
| **Module** | to say what one module is, in the manifest every other domain reads | the catalog, one manifest per module |
|
||||
| **Core** | to keep the controller, the bus, the store and every node's engine running and agreed | the controller and the store |
|
||||
| **Placement** | to decide, send and apply what each node runs, and say why | the controller's `inventory` database |
|
||||
| **Provisioning** | to resolve which module serves a provision for which consumer, and wire the two | the controller |
|
||||
@@ -134,7 +134,7 @@ rule stands with the new word: where a domain's records live in the controller,
|
||||
on a head word defined twice or also retired. Decision records are never checked. A document that cannot
|
||||
be reworded at once is named with a date in the check's allowance list; a graduated research effort may
|
||||
be kept there, as a record of what was said.
|
||||
- **In the catalogue**, its own repository check reads a copy of the words retired for the tools and fails
|
||||
- **In the catalog**, its own repository check reads a copy of the words retired for the tools and fails
|
||||
when what a module's tools show an agent uses one. The copy and the glossary are compared whenever both
|
||||
are at hand; a change retiring a tools word changes both repositories.
|
||||
- **Homonyms** are checked by review: the reviewer looks for the bare word in the diff.
|
||||
@@ -145,8 +145,8 @@ rule stands with the new word: where a domain's records live in the controller,
|
||||
`node-build-agent`, the licence manager's `release`, the controller's `plan`, `doctor` and queue verbs,
|
||||
the packet filter module's tool — each in its own repository's change.
|
||||
- **The descriptions served by the core's own repositories** (the controller's, the node-engine's and the
|
||||
tool runner's verbs) are outside the catalogue and so outside its check.
|
||||
- **A probe of the served descriptions** in the self-check, once the catalogue's check has run a while.
|
||||
tool runner's verbs) are outside the catalog and so outside its check.
|
||||
- **A probe of the served descriptions** in the self-check, once the catalog's check has run a while.
|
||||
- **A domain on each decision record**, which would let the reading order be printed per domain; a change
|
||||
to the record schema, not made here.
|
||||
- **The reverse rule** — a design defining a word must find it in the glossary — proposed by research 034
|
||||
|
||||
@@ -48,7 +48,7 @@ another. A *routing domain* is a domain whose names a resolver sends to one link
|
||||
|---|---|---|
|
||||
| the seat `node-resolver` | the controller's set | a node seat, its verbs `routes`, `route`, `unroute`, required of every holder |
|
||||
| the stepping back | the controller's composition | where a module holds `node-resolver` and renders `/etc/resolv.conf`, the uplink holder's rendering of it is left out |
|
||||
| `systemd-resolved` | the catalogue | holds the seat, provides `split-dns` at the machine's reach, writes the file and resolved's drop-in, runs the guard |
|
||||
| `systemd-resolved` | the catalog | holds the seat, provides `split-dns` at the machine's reach, writes the file and resolved's drop-in, runs the guard |
|
||||
| the guard | `systemd-resolved`'s process, as root | keeps the file the module's, keeps an outside write, serves the verbs on the machine's socket |
|
||||
| the adapter | `forticlient`'s process, as root | reads the client's write, routes its domains over its tunnel, takes the write, takes the route away when the tunnel goes |
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ The glossary holds the three words.
|
||||
| part | where | does |
|
||||
|---|---|---|
|
||||
| seats `node-nfs-server` and `node-mounts` | the controller's closed set | node seats, capacity 1, with their verbs |
|
||||
| `nfs-server` | the catalogue | holds `node-nfs-server`, provides the provision, writes its export file, runs the service, opens 2049 to the private network |
|
||||
| `mounts` | the catalogue | holds `node-mounts`, writes a unit pair per share, keeps each share's state, serves the tools |
|
||||
| `nfs-server` | the catalog | holds `node-nfs-server`, provides the provision, writes its export file, runs the service, opens 2049 to the private network |
|
||||
| `mounts` | the catalog | holds `node-mounts`, writes a unit pair per share, keeps each share's state, serves the tools |
|
||||
| the binding | the controller | gives a consumer the server's name; the consumer asks the server's seat for each share's path and access over the bus |
|
||||
| the credential | the vault | per SMB source, the `mounts` module's own secret `smb-password-<source>` (a member of the family `smb-password-*`), issued outside the mesh and given with `secret accept` or typed at the desk prompt, unsealed on the node as a file; the username is the user part of the source's entry in the setting `sources` |
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ the issue's worker, who works on it, as the worker states it
|
||||
kind `depends-on` (§ 10).
|
||||
|
||||
**The registered list of owners** is a setting of the module, changed through the controller's settings verb like any
|
||||
other module setting. Slice 1 starts it with the repositories in hq's `00-META/repos.md` and the modules the catalogue
|
||||
other module setting. Slice 1 starts it with the repositories in hq's `00-META/repos.md` and the modules the catalog
|
||||
registers.
|
||||
|
||||
**An issue that has a folder in hq** kept its status in that folder's frontmatter until the import of slice 3 ran;
|
||||
@@ -171,7 +171,7 @@ event carries a secret, and an issue holds none.
|
||||
| any other status | no `by` and no `take-over`; the move ends the work |
|
||||
| `located` | `located-in` names at least one registered owner |
|
||||
| `resolved` | the issue is at `fixing` (or already `resolved`, to correct what it holds); `located-in`, and `fixed-by` saying what fixed it |
|
||||
| `resolved`, for a core issue (its `located-in` names the controller, the node-engine, the tool runner, the SDK, or the catalogue's bus, forge or build agent) | `replay` (an id from mesh-lab's replays register) or `replay-none` saying why none is possible (ADR 0237) |
|
||||
| `resolved`, for a core issue (its `located-in` names the controller, the node-engine, the tool runner, the SDK, or the catalog's bus, forge or build agent) | `replay` (an id from mesh-lab's replays register) or `replay-none` saying why none is possible (ADR 0237) |
|
||||
| `wontfix` | a reason |
|
||||
|
||||
A refused move changes nothing and says which rule refused it. The rows for `diagnosing`, `fixing` and `resolved` are
|
||||
|
||||
@@ -228,7 +228,7 @@ the page as the authority on words (ADR 0423, changing ADR 0244).
|
||||
|---|---|
|
||||
| the knowledge base's own rule on a retired word (§ 4) | the *Not:* and identifier lines of the entries, no longer the page through the records module; it judges a design when it is proposed, as it judges a decision |
|
||||
| hq's word check | the entries' *Not:* and identifier lines, and its uniqueness check (a head word heading two entries, or also retired) over the entries; it covers what stays in this repository, and designs leave its coverage |
|
||||
| the catalogue's repository check | the retired words of no scope or *(tools)* from the entries; its copied list is retired, so hq has no copy to compare |
|
||||
| the catalog's repository check | the retired words of no scope or *(tools)* from the entries; its copied list is retired, so hq has no copy to compare |
|
||||
| hq's cycle check | the design records, for every design naming its decisions, not files under `03-DESIGN/` |
|
||||
|
||||
Each of the four fails, saying so, when the knowledge base does not answer, and never passes on nothing.
|
||||
@@ -318,7 +318,7 @@ is done:
|
||||
8. Every entry of the glossary's Config section is filed under the Config domain, and the domains the entries name
|
||||
are the twelve of ADR 0354.
|
||||
9. Tests 1 to 8 pass together after the one delivery, run against main and the live knowledge base, with nothing
|
||||
in this repository, the catalogue or the instruction text pointing at a removed path.
|
||||
in this repository, the catalog or the instruction text pointing at a removed path.
|
||||
|
||||
## What it leaves out
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ and may not be copied to anyone else. The vendor serves only its newest version,
|
||||
file (a `file://` URL), with `updates_disabled`. Betterbird installs it into every profile, and the person
|
||||
cannot remove it.
|
||||
|
||||
Owl declares the range of Betterbird versions it supports. The catalogue's check refuses a manifest whose
|
||||
Owl declares the range of Betterbird versions it supports. The catalog's check refuses a manifest whose
|
||||
Betterbird pin lies outside the range of its Owl pin, so a Betterbird upgrade that Owl does not support yet
|
||||
waits for Owl.
|
||||
|
||||
@@ -205,7 +205,7 @@ module needs.
|
||||
|
||||
**The preferences are one entry each.** The setting `prefs` maps a preference's name to its value, so a node's
|
||||
layer overrides one preference, or takes one out with null, and the operator's setup lives in the mesh-wide
|
||||
layer, not in the catalogue. The module ships only its policy's locks and the names it keeps at Betterbird's own
|
||||
layer, not in the catalog. The module ships only its policy's locks and the names it keeps at Betterbird's own
|
||||
default (issue 586). `prefs` refuses a credential's preference, an account's (§4), and the four prefixes the
|
||||
settings below own: `calendar.registry.`, `calendar.list.`, `ldap_2.servers.` and `mailnews.tags.`, so each value
|
||||
has one owner.
|
||||
@@ -254,11 +254,11 @@ own layers. With `propose` it proposes the next layer for the operator's Approve
|
||||
each node's that needs one, each only after the one before it was set or declined, against the layer as it then
|
||||
stands and carrying that layer whole (ADR 0277 keeps one open ask per module and machine). It proposes nothing
|
||||
while a desktop node does not answer, because a layer derived without it would put on every node what only some
|
||||
hold. The first mesh-wide proposal also carries the operator's setup of `prefs`, which the catalogue held before.
|
||||
hold. The first mesh-wide proposal also carries the operator's setup of `prefs`, which the catalog held before.
|
||||
|
||||
## How it is checked
|
||||
|
||||
- **The catalogue's tests:** both pins carry a URL, a version and a sha256; a Betterbird pin outside Owl's range
|
||||
- **The catalog's tests:** both pins carry a URL, a version and a sha256; a Betterbird pin outside Owl's range
|
||||
is refused; the generated policy file names Owl as `force_installed` from the placed file with
|
||||
`updates_disabled`, and sets `DisableAppUpdate`; `prefs` refuses the account prefixes and the four of §8;
|
||||
`accounts` refuses a password-like field; a declared account's user.js holds only the module's own keys; every
|
||||
|
||||
@@ -38,7 +38,7 @@ request, and the five, sharing their branch's name, are one delivery group (ADR
|
||||
future state. The five are merged within one merge window, so they are one batch and one rollout (ADR 0276
|
||||
§1, §2), in an order declared with `after:` lines in each pull request (ADR 0239 §11): mesh-sdk, then
|
||||
`mesh-host`, then mesh-controller, then mesh-catalog, then hq. The release holds every package but T6, R1 and R2:
|
||||
the media catalogue and the applications live in repositories of their own and move after it, and only
|
||||
the media catalog and the applications live in repositories of their own and move after it, and only
|
||||
then can the old forms be refused (R1) and the record say what runs (R2).
|
||||
|
||||
**The moves of data run after the delivery, in this order, each a verb that says what it did:**
|
||||
@@ -58,7 +58,7 @@ the backlog rollout once it is heard from (ADR 0360 §10).
|
||||
|
||||
**Nothing breaks on the way.** ADR 0349 clause 17: each file is filled the old way or the new way, never
|
||||
both, by the form of its manifest, and the old forms stay accepted until the last module has moved.
|
||||
Inside the release this still holds: the media catalogue and the applications kept in their own
|
||||
Inside the release this still holds: the media catalog and the applications kept in their own
|
||||
repositories move after it, so the old forms stay accepted until they have (R1).
|
||||
|
||||
**Where it stands, 2026-10-17** (the Milestones section below). The packages' branches were merged into
|
||||
@@ -169,9 +169,9 @@ as T2.
|
||||
|
||||
**T5 (issue 658) — The ten modules without a build.** *mesh-catalog. A day.* ca-trust, de-spiegel, invoicing,
|
||||
local-model-consumer, matrix, node-env, only-office, sshd, step-ca and supabase gain an archive build for
|
||||
their templates. **Proof:** as T2; afterwards no manifest in the catalogue carries file text.
|
||||
their templates. **Proof:** as T2; afterwards no manifest in the catalog carries file text.
|
||||
|
||||
**T6 (issue 659) — The media catalogue and the applications.** *mesh-media-catalog and each application's
|
||||
**T6 (issue 659) — The media catalog and the applications.** *mesh-media-catalog and each application's
|
||||
repository. Two days.* **Proof:** as T2, per repository.
|
||||
|
||||
## 2 — Config (ADR 0348)
|
||||
@@ -185,10 +185,10 @@ object or list not trusted; a predefined key not on the list or of another type
|
||||
value falling back to a trusted key; a value neither in a template nor read at runtime; a placeholder
|
||||
naming an undeclared value; a `reload-on` or `restart-on` naming an undeclared file; a moved module's
|
||||
mergeable file not naming its keys; a mesh word or `puid`/`pgid` in `takes-config`. **Proof:** the registration refusal tests, one per
|
||||
rule; every manifest in the catalogue still registers.
|
||||
rule; every manifest in the catalog still registers.
|
||||
|
||||
**K2 (issue 661) — The list of predefined keys.** *mesh-controller and mesh-catalog. A day.* `predefined.json` at the
|
||||
catalogue's root, with `font-size` and `agent-managed-settings` first; the build validates it and every
|
||||
catalog's root, with `font-size` and `agent-managed-settings` first; the build validates it and every
|
||||
manifest naming a key; the merge check refuses removing a key still named or holding a value; the newest
|
||||
trunk build's list is published. **Proof:** the merge check refuses a planted removal; an older build's
|
||||
list does not replace a newer one.
|
||||
@@ -214,9 +214,9 @@ the controller reads `mesh-config_resolved` where it read its layers (ADR 0349 c
|
||||
an exception to ADR 0348 §9 that R1 removes), and `settings` answers by forwarding to `config`. **Proof:** composition of
|
||||
every node before and after is byte-identical; `settings` and `config` list the same values.
|
||||
|
||||
**K6 (issue 665) — The catalogue says config.** *mesh-catalog. A day.* The 15 modules using `${setting:}`, the 9
|
||||
**K6 (issue 665) — The catalog says config.** *mesh-catalog. A day.* The 15 modules using `${setting:}`, the 9
|
||||
declaring settings and the 4 with `takes-settings` move to the new names and the full declaration.
|
||||
**Proof:** the catalogue's check finds no old name in a moved module.
|
||||
**Proof:** the catalog's check finds no old name in a moved module.
|
||||
|
||||
**K7 (issue 666) — The operator's acceptance on dunst.** *live. An hour.* With every level cleared the first
|
||||
workstation's dunstrc says 10; a mesh font size, then that node's font size, then dunst's own value on
|
||||
@@ -326,7 +326,7 @@ ADR 0349; claude-code reads its managed settings through it.
|
||||
## 8 — The old forms go (ADR 0349 clause 17)
|
||||
|
||||
**R1 (issue 682) — Refused and removed.** *mesh-controller, `mesh-host`, mesh-catalog. Three days.* Once every module of
|
||||
every catalogue has moved: the catalogue's check refuses inline text, `settings`, `${setting:}` and the
|
||||
every catalog has moved: the catalog's check refuses inline text, `settings`, `${setting:}` and the
|
||||
old contribution forms; the controller's filling passes but placement, its settings, secret and binding
|
||||
code and tables (a new migration) go; the node-engine keeps opening sealed values for the core's files
|
||||
only; the controller stops reading `mesh-config_resolved`. **Proof:** the controller's test that its
|
||||
@@ -345,7 +345,7 @@ each makes true. Whether a milestone's issues are delivered is read from the iss
|
||||
| # | Outcome | Packages | Issues |
|
||||
|---|---|---|---|
|
||||
| 1 | Config, provisioning, secrets, bindings, collections and runtime asks run the new way; the old forms are still accepted | every package but T6, B3, R1, R2 | 647, 650 to 679 but 659, 681, 684, 685 |
|
||||
| 2 | The media catalogue and the applications carry their file text as templates | T6 | 659 |
|
||||
| 2 | The media catalog and the applications carry their file text as templates | T6 | 659 |
|
||||
| 3 | The other providers answer their consumers; the old forms are refused; the record says what runs | B3, R1, R2 | 680, 682, 683 |
|
||||
|
||||
## What is deliberately not here
|
||||
|
||||
@@ -128,7 +128,7 @@ identity present on a machine into that machine's declaration, and the node-engi
|
||||
resource. A login that exists is adopted under its name, uid and home; the mesh never renames or deletes one. An
|
||||
agent's machine account has its own home and group, no password, no root group and no sudo rule, and the node-engine
|
||||
judges on every look that it cannot become root without a person, by the ways ADR 0266 §12 to §17 list. No module declares a login for a
|
||||
person or an agent: claude-code's `agent-account` resource goes, and the catalogue's check refuses a manifest that
|
||||
person or an agent: claude-code's `agent-account` resource goes, and the catalog's check refuses a manifest that
|
||||
declares one. The node record's operator account and agent account are read from the identities, no longer stated
|
||||
at the controller's terminal or in the mesh-config snapshot; the machine facts `account`, `agent-account` and their
|
||||
homes keep their names. A machine names an agent account only where the identity seat marks an agent as its agent
|
||||
@@ -204,8 +204,8 @@ Checked on 2026-10-17 (playbook 02, step 1); no conflict was found.
|
||||
## 6. Effects, asks and break-glass (ADR 0387)
|
||||
|
||||
Every seat verb and module tool is one permission and carries one **effect**, set in its module's manifest:
|
||||
**reads**, **acts** or **dangerous**. The catalogue's check refuses an unmarked tool, once every module already in
|
||||
the catalogue is marked. A dangerous tool is granted only by name, and naming one in a role goes to the operator as
|
||||
**reads**, **acts** or **dangerous**. The catalog's check refuses an unmarked tool, once every module already in
|
||||
the catalog is marked. A dangerous tool is granted only by name, and naming one in a role goes to the operator as
|
||||
an ask first. Every other dangerous act is an ask (ADR 0259), bound to that act and carried out on the warrant.
|
||||
|
||||
The **break-glass** agent holds every tool and never asks. It is sealed until the operator opens it, or a condition
|
||||
@@ -246,7 +246,7 @@ carry them:
|
||||
|
||||
1. A licence token replaced under a running session keeps that session working.
|
||||
2. The sandbox module loads KVM on the home server, and a sandbox runs there.
|
||||
3. Every existing module's tools carry an effect, and the catalogue's check refuses an unmarked one.
|
||||
3. Every existing module's tools carry an effect, and the catalog's check refuses an unmarked one.
|
||||
4. The switch, run on the mesh's own modules in the lab from the bed test kept on the lab's branch
|
||||
`research/053-signed-bus-logins`, loses nothing and leaves every identity on the new bus.
|
||||
5. The acceptance sentence at the top, end to end.
|
||||
@@ -260,7 +260,7 @@ From research 057 (ADR 0411 to ADR 0414):
|
||||
unaltered and the self-check raises `agent-can-become-root` for it, on a machine that marks no agent account too;
|
||||
the operator's existing logins are
|
||||
unchanged.
|
||||
8. No manifest in the catalogue declares a login for a person or an agent; the catalogue's check refuses one.
|
||||
8. No manifest in the catalog declares a login for a person or an agent; the catalog's check refuses one.
|
||||
9. For every machine, the declaration before and after differs, for the modules not set to run for an identity, only
|
||||
in the machine accounts' `user` resources (now the controller's, and `nox` added on the other machines); claude-code on the control node, run for `nox`, places the same
|
||||
files in the same home; on the other machines the guard stays off and the operator's sessions keep the mesh's tools.
|
||||
@@ -292,7 +292,7 @@ and the issue tracker takes only registered owners. The building waits until the
|
||||
|
||||
| # | Outcome, in one sentence | Proves | Repositories and their parts | Depends on |
|
||||
|---|---|---|---|---|
|
||||
| 1 | Every tool on the mesh says whether it reads, acts or is dangerous, a tool's description shows it, and the catalogue refuses a module that leaves one unmarked | test 3 | mesh-sdk: the effect in a tool's declaration, an unmarked tool served as dangerous; mesh-controller: the manifest field; mesh-catalog and mesh-media-catalog: every module marked, the check | — |
|
||||
| 1 | Every tool on the mesh says whether it reads, acts or is dangerous, a tool's description shows it, and the catalog refuses a module that leaves one unmarked | test 3 | mesh-sdk: the effect in a tool's declaration, an unmarked tool served as dangerous; mesh-controller: the manifest field; mesh-catalog and mesh-media-catalog: every module marked, the check | — |
|
||||
| 2 | A session works on a repository's workspace through the mesh's tools alone: read, glob, search, edit, write, git, its tests and its merge check, with no command runner | — | mesh-catalog: the module `nox-workspace` on the node seat `workspace`, on the always-on nodes; mesh-controller: the seat | 1 |
|
||||
| 3 | A thread created through the thread seat runs headless on an always-on node, its conversation readable through the seat, its usage counted per thread, and a move to another node loses at most the turn in progress | — | mesh-catalog: `nox-threads` (seat `thread`, restricted streams, leases), `nox-thread-handler` (seat `thread-handler`), claude-code (mirror and rebuild), claude-licence-manager (usage per thread) | 2 |
|
||||
| 4 | The operator starts, follows, answers and stops a thread from a terminal client, and answers its questions on Telegram | — | mesh-catalog: the terminal client, a Go program of `nox-threads`; claude-code: questions and permission requests through the asks | 3 |
|
||||
@@ -313,7 +313,7 @@ waits for nothing; 12 waits for milestone 6.
|
||||
| # | Outcome, in one sentence | Proves | Repositories and their parts (issues) | Depends on |
|
||||
|---|---|---|---|---|
|
||||
| 11 | The agent's forge account is `nox`, with its keys and its place in the agents' write team kept | test 12 | mesh-catalog gitea: the rename, keys registered on `nox`, every `forge-account` entry (993) | — |
|
||||
| 12 | The mesh makes the operator's logins and nox's on every machine from the identities, and judges nox unable to become root everywhere | tests 6 to 9 | identity module: logins, machines, kept logins, the agent account mark, the operator's verb (994); the node-engine (`mesh-host`): existing logins adopted, every agent login judged (995); mesh-controller: machine accounts composed, node accounts read from the identities, the terminal lines retired (996); mesh-catalog: claude-code declares no login, the catalogue's check refuses one (997) | 6 |
|
||||
| 12 | The mesh makes the operator's logins and nox's on every machine from the identities, and judges nox unable to become root everywhere | tests 6 to 9 | identity module: logins, machines, kept logins, the agent account mark, the operator's verb (994); the node-engine (`mesh-host`): existing logins adopted, every agent login judged (995); mesh-controller: machine accounts composed, node accounts read from the identities, the terminal lines retired (996); mesh-catalog: claude-code declares no login, the catalog's check refuses one (997) | 6 |
|
||||
| 13 | The operator and nox each reach the bus with their own password, held to their role; the operator's only where no agent runs as the operator | test 11 | mesh-controller: the operator's and nox's bus accounts, secrets at 0600, the fifteen-minute verdict (999); identity module: nox's role refuses approvals and dangerous tools (998); until milestone 7 builds the asks, a dangerous act of nox's is refused, not asked | 12 |
|
||||
| 14 | The operator starts a claude-code session for nox or for themself, and nox pushes over SSH with its own key | test 10 | mesh-controller: run for, assignments keyed by identity, the refusals (1001); mesh-sdk: calls as the identity, a start goes down only (1002); claude-code: started for nox or for the operator (1003); ssh-client and gitea: nox's own key, closing issue 989 (1004) | 11, 13 |
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ before `mesh-mail-notifier` is unassigned; test 12 with milestone 4.
|
||||
|---|---|---|---|---|
|
||||
| 1 | In a browser on the laptop and the workstation, the operator signs in and browses, searches and reads every account's mail with its attachments, and writes a mail that goes out on their Approve | tests 1 to 9 | mesh-catalog: `mesh-mail`'s account bucket (issue 1255, ADR 0439 §2); the module `mesh-mail-web` (manifest, sign-in, the pages, the send, the seat's verbs, the integration test) | — |
|
||||
| 2 | Each new mail is read into a mail group on the mail page; the mail that matters reaches the operator's phone as one mail signal, a scam warning shows on the page only, the operator's corrections become reading lessons, and the pop-up for every new mail is gone | tests 10, 11, 13 to 17 | mesh-catalog: the seat `mail-reading` declared in `mesh-mail`'s manifest; the module `mesh-mail-reader` (batches, mail entries, reading lessons, mail signals, health, the integration test); `mesh-mail-web`'s mail groups, mail signals, scam warnings and reading lessons; `mesh-mail-notifier` unassigned (issue 1418) | 1 |
|
||||
| 3 | Betterbird is gone from the laptop and the workstation, its profile and mail kept in the home | ADR 0379 §2 | mesh-catalog: Betterbird unassigned on the operator's Approve, then the `betterbird` module removed from the catalogue (issue 806) | 2 |
|
||||
| 3 | Betterbird is gone from the laptop and the workstation, its profile and mail kept in the home | ADR 0379 §2 | mesh-catalog: Betterbird unassigned on the operator's Approve, then the `betterbird` module removed from the catalog (issue 806) | 2 |
|
||||
| 4 | **Replaced by design 63** (2026-10-22): on the mail page the operator asks questions about their mail and gets the mails that answer them, in a mail conversation kept as a thread | test 12 | mesh-catalog: `mesh-mail-web`'s mail conversation as a client of the thread seat; the thread's mail tools under a caller of their own (issue 1419); `mesh-mail`'s folder case and `-any` switches | 2 (the reading lessons); design 58's milestone 3 (issue 880) |
|
||||
|
||||
**Milestone 1, confirmed by the operator on 2026-10-19, is delivered;** its live proof on the laptop and the
|
||||
|
||||
@@ -199,10 +199,10 @@ missing page and expects the error.
|
||||
|
||||
## Milestones
|
||||
|
||||
The milestones build ADR 0468, feature `files-through-the-store`. They are cut by release order: the catalogue's
|
||||
The milestones build ADR 0468, feature `files-through-the-store`. They are cut by release order: the catalog's
|
||||
merge check reads manifests with the controller the mesh runs, and the controller vendors the SDK, so a new
|
||||
manifest field ships SDK first, then the controller and every other strict reader of manifests (the build modules,
|
||||
the delivery's indexer), then the catalogue (playbook 07, *a new manifest word*).
|
||||
the delivery's indexer), then the catalog (playbook 07, *a new manifest word*).
|
||||
|
||||
| # | Outcome | Proves | Repository | Issues |
|
||||
|---|---|---|---|---|
|
||||
|
||||
@@ -48,7 +48,7 @@ document is written and this one's status becomes `implemented`.
|
||||
| [`45-a-core-that-cannot-fail-silently.md`](45-a-core-that-cannot-fail-silently.md) | **Designed.** The core says when it is wrong, refuses what is stale or unreadable, heals what it knows, upgrades one machine at a time with a witness that rolls it back, and is checked against the real mesh before merge: the writers and signals tables, the condition store, `doctor`, the minimal output channel, healers, the lease and report order, staged upgrades, the facts snapshot and replays, in six phases | ADR 0227, ADR 0224, ADR 0218 |
|
||||
| [`46-the-conversation-with-the-operator.md`](46-the-conversation-with-the-operator.md) | **Designed.** The mesh tells and asks its operator over channels that are holders of two kinded benches, `channel` and `intake`, declaring capabilities from a fixed vocabulary; the router orders them by work context and never lowers the bar; an answer that performs an action is checked and performed by the controller, on a TOTP code or a verified Telegram sender, never on a desk click alone; operator messages addressed or answered by the mesh's responder, untrusted input kept as data, references for what words may not carry, and a recoverable factor; Telegram first, in six phases | ADR 0234, ADR 0227 |
|
||||
| [`47-delivery-from-commit-to-delivered.md`](47-delivery-from-commit-to-delivered.md) | **Designed.** A delivery is one commit in one repository, from its pull request's head to every machine; a delivery group is deliveries sharing a branch name, ordered and checked as one future state; both owned by the `mesh-delivery` module with one state table, its state on the bus, every transition said, noted on the commit and shown on the pull request; the controller keeps the planner, the gate, sending and the rollout, and the core's own updates never wait for the module | ADR 0239, ADR 0238, ADR 0236 |
|
||||
| [`49-the-mesh-in-domains.md`](49-the-mesh-in-domains.md) | **In progress.** Every concept belongs to one of eleven domains, which owns its one word; the glossary is organised by them and is the authority, a retired word is named on its replacement's line with its scope, and two checks hold this repository's documents and the catalogue's tool descriptions to it | ADR 0244, ADR 0006, ADR 0008, ADR 0302 |
|
||||
| [`49-the-mesh-in-domains.md`](49-the-mesh-in-domains.md) | **In progress.** Every concept belongs to one of eleven domains, which owns its one word; the glossary is organised by them and is the authority, a retired word is named on its replacement's line with its scope, and two checks hold this repository's documents and the catalog's tool descriptions to it | ADR 0244, ADR 0006, ADR 0008, ADR 0302 |
|
||||
| [`50-split-dns-on-a-machine-with-a-vpn-client.md`](50-split-dns-on-a-machine-with-a-vpn-client.md) | **In progress.** A machine whose VPN client pushes servers of its own runs a resolver of its own, on a node seat, that routes the VPN's domains over its link and every other name to the mesh's resolvers; the VPN client's module carries the adapter | ADR 0247, ADR 0223 |
|
||||
| [`51-the-registries-keep-what-is-named.md`](51-the-registries-keep-what-is-named.md) | **In progress.** The artifact store, the package registry and each machine's container runtime get tools that say what they hold, what names each thing and what could go; every removal is a dry run unless a person asks and says why, and what decides is always a record | ADR 0251, ADR 0189 |
|
||||
| [`52-shares-and-mounts.md`](52-shares-and-mounts.md) | **In progress.** A node shares one folder at a time through `nfs-server`, which provides it as a provision to the private network only, mapping every client to the folder's owner; a node that asks mounts it through `mounts`, a mount unit and an automount per share, so nothing mounts or fails at boot; a device that comes and goes is an occasional source whose absence is a state | ADR 0263, ADR 0051, ADR 0092 |
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Diagnosis — 2026-09-21
|
||||
|
||||
1. The report's own remedy was taken: the same order against a second ACME implementation. The
|
||||
catalogue's own certificate authority, step-ca, was raised in the bed beside Pebble, pinned as
|
||||
the catalogue pins it, and the same proxy pointed at it over the same challenge path. It issued
|
||||
catalog's own certificate authority, step-ca, was raised in the bed beside Pebble, pinned as
|
||||
the catalog pins it, and the same proxy pointed at it over the same challenge path. It issued
|
||||
within a second of the order.
|
||||
2. The name was still not served, and the reason was neither authority's: the bed's stand-in
|
||||
backend, a netcat loop behind the route, used flags this machine's netcat has not got and never
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Diagnosis — 2026-09-21
|
||||
|
||||
1. The catalogue was read again for mounts no resource declares: twenty-four remained, in two
|
||||
1. The catalog was read again for mounts no resource declares: twenty-four remained, in two
|
||||
kinds only. Nine media modules mount the operator's library, and every one of those paths is
|
||||
already in the module's `accesses` — the vocabulary ADR 0051
|
||||
gave exactly this. Four modules mount the container runtime's socket, and every one of them
|
||||
@@ -9,6 +9,6 @@
|
||||
check that was withdrawn needed only to read both: an access is a declared path, and a
|
||||
capability declares the facility it grants.
|
||||
|
||||
**Located in:** the catalogue's parser. The check is back, refuses an undeclared mount naming the
|
||||
path and the three remedies, and a test parses the whole catalogue. Decided in
|
||||
**Located in:** the catalog's parser. The check is back, refuses an undeclared mount naming the
|
||||
path and the three remedies, and a test parses the whole catalog. Decided in
|
||||
ADR 0091.
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ Not "a registry cannot be installed" — **a mesh cannot hold its own modules.**
|
||||
|
||||
The artifact store is one store for everything a module ships: images, and archives, which are
|
||||
directories from a module's repository packed and pushed as content-addressed blobs. So it is the
|
||||
mesh's module catalogue in artefact form, and the same role a separate object store plays in the
|
||||
mesh's module catalog in artefact form, and the same role a separate object store plays in the
|
||||
arrangement being replaced.
|
||||
|
||||
Until it exists, a module can be described and resolved but nothing it carries can be kept
|
||||
|
||||
@@ -6,7 +6,7 @@ opened: 2026-09-04
|
||||
|
||||
## What was observed
|
||||
|
||||
Rolling the module runtime out to the catalogue (the runtime that serves a module's tools and
|
||||
Rolling the module runtime out to the catalog (the runtime that serves a module's tools and
|
||||
runs its events under the module's own account), each tools+events module receives its
|
||||
configuration the way the design intends: a mergeable config file the module declares, into
|
||||
which the assignment's settings are merged. The runtime container mounts that file and reads
|
||||
@@ -38,7 +38,7 @@ failing to load because the value that would fix them is present but unread.
|
||||
## Why it matters beyond this instance
|
||||
|
||||
Every tools+events module converted to the runtime model now takes its URL and credentials
|
||||
this way, so this is not one module's quirk — it is the config path for the whole catalogue.
|
||||
this way, so this is not one module's quirk — it is the config path for the whole catalog.
|
||||
The gap turns the headline promise of the settings design ("change it without editing it, on
|
||||
the fly") into "change it, then recreate the container by hand," which is the manual step the
|
||||
design existed to remove. And because the file is genuinely updated, nothing surfaces the
|
||||
|
||||
@@ -6,7 +6,7 @@ opened: 2026-09-02
|
||||
|
||||
## The symptom, as observed
|
||||
|
||||
Found by review of the catalogue examples (2026-09-02), not by an outage — the outage is the
|
||||
Found by review of the catalog examples (2026-09-02), not by an outage — the outage is the
|
||||
part the design permits to be silent.
|
||||
|
||||
The cache module declares its access-control file as an ordinary file resource with fixed,
|
||||
@@ -40,5 +40,5 @@ even when what grew inside since is somebody else's work the mesh asked for.
|
||||
some other way?
|
||||
- ADR 0010 treats every added resource type as a security artefact. Does a create-once
|
||||
semantic widen what a compromised control plane can express, or narrow it?
|
||||
- Are there other seeded-then-mutated files already in the catalogue that this failure is
|
||||
- Are there other seeded-then-mutated files already in the catalog that this failure is
|
||||
waiting inside?
|
||||
|
||||
@@ -6,7 +6,7 @@ opened: 2026-09-02
|
||||
|
||||
## The symptom, as observed
|
||||
|
||||
Found by review of the catalogue examples (2026-09-02). The media stack is several modules —
|
||||
Found by review of the catalog examples (2026-09-02). The media stack is several modules —
|
||||
a library server, the acquisition managers, a download client and their satellites — and each
|
||||
of them declares the same library and download directories as its own resources.
|
||||
|
||||
@@ -28,7 +28,7 @@ shared workspace of several modules was written six times as six private ones. T
|
||||
a media-stack peculiarity: any pipeline of modules handing files to each other on one machine
|
||||
(an ingest directory, a spool, a drop folder) hits the same wall.
|
||||
|
||||
It is also a fork in the design the catalogue has otherwise avoided: the fix could be a new
|
||||
It is also a fork in the design the catalog has otherwise avoided: the fix could be a new
|
||||
owning module the others depend on, a shared-resource concept in the manifest, or a statement
|
||||
that co-located file handoff is not a thing the mesh supports and these modules are one
|
||||
module. Each answer changes what a module *is*, which is why this is an issue and not a patch.
|
||||
|
||||
@@ -6,7 +6,7 @@ opened: 2026-09-05
|
||||
|
||||
## The symptom, as observed
|
||||
|
||||
Found while converting the catalogue (2026-09-05), across several modules at once. A module can
|
||||
Found while converting the catalog (2026-09-05), across several modules at once. A module can
|
||||
declare *things that exist* — a directory, a file with fixed content, a network, a container — but
|
||||
it cannot declare *a step that runs* at a defined point in its own lifecycle. Three converted
|
||||
modules need exactly that and have nowhere to put it:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
opened: 2026-09-10
|
||||
---
|
||||
|
||||
# 039 — The lab's registry was pinning what the catalogue left unpinned
|
||||
# 039 — The lab's registry was pinning what the catalog left unpinned
|
||||
|
||||
## Symptom
|
||||
|
||||
@@ -43,7 +43,7 @@ quietly supplying a property of the system under test, and nothing said so.
|
||||
## Fixed, and what the fix does not settle
|
||||
|
||||
Each of the nine references now names the digest its tag resolved to, read from the registry that
|
||||
serves them. Every manifest in the catalogue is pinned; the host's refusal has nothing left to
|
||||
serves them. Every manifest in the catalog is pinned; the host's refusal has nothing left to
|
||||
catch, and the check that would have caught this — *no manifest names a tag* — now passes on
|
||||
content rather than on a harness's rewriting.
|
||||
|
||||
|
||||
@@ -57,14 +57,14 @@ either way.
|
||||
|
||||
Still open, and worse than reported: the controller's own manifest now delivers its store
|
||||
connections **and** its broker credentials through an env-file, so both halves reach the process
|
||||
environment; 28 catalogue manifests use env-file for a secret, and nothing in the catalogue
|
||||
environment; 28 catalog manifests use env-file for a secret, and nothing in the catalog
|
||||
engine refuses a `${secret:…}` placeholder in one. The vault work of
|
||||
ADR 0085 rests on the seal this weakens.
|
||||
|
||||
## Resolved 2026-09-21
|
||||
|
||||
By ADR 0086: a secret reaches
|
||||
a process as a file, an environment exception is declared with a reason, and the catalogue engine
|
||||
a process as a file, an environment exception is declared with a reason, and the catalog engine
|
||||
refuses the undeclared shape. The controller, the instance this was opened on, reads all six of
|
||||
its credentials from files. The 35 other containers carry a declared reason; converting each where
|
||||
its software accepts a path remains per-module work and is not this issue's.
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ recipe — so the mesh could *say* which modules a base change had invalidated,
|
||||
produced the old base anyway until somebody edited that line by hand.
|
||||
|
||||
Worse than inconvenient, as it turned out. **A fingerprint names one particular copy of an image,
|
||||
and a copy exists on one mesh.** The three modules in the catalogue named a copy produced inside a
|
||||
and a copy exists on one mesh.** The three modules in the catalog named a copy produced inside a
|
||||
throwaway lab, so none of them could be built anywhere else at all; and the line each of them
|
||||
replaced named a copy from an even earlier throwaway lab. Nobody noticed because the only place they
|
||||
had ever been built was the place the copy existed.
|
||||
|
||||
@@ -56,7 +56,7 @@ The rules are computed from what modules declare they listen on. During a migrat
|
||||
about almost nothing — the services are still running under the system being replaced — so it opens
|
||||
almost nothing. Anything listening **on the host** rather than in a container is then dropped.
|
||||
|
||||
**Including ssh.** No module in the catalogue declares an ssh port, and the generator has no
|
||||
**Including ssh.** No module in the catalog declares an ssh port, and the generator has no
|
||||
built-in allowance for one. So the ruleset loaded on a machine that has not been told otherwise
|
||||
accepts established connections, loopback and ping, and refuses every new ssh connection.
|
||||
|
||||
|
||||
@@ -23,16 +23,16 @@ So the account is right and the request is reasonable, and there is no account t
|
||||
|
||||
## Why this matters
|
||||
|
||||
**A module's tools are its operator-facing surface, and nothing can reach it.** The catalogue serves
|
||||
**A module's tools are its operator-facing surface, and nothing can reach it.** The catalog serves
|
||||
five: what this mesh holds, what a module is made of, what provides a given provision, what depends
|
||||
on a module, and what is stale. Those are the questions the catalogue exists to answer, and today
|
||||
on a module, and what is stale. Those are the questions the catalog exists to answer, and today
|
||||
the answer to "how does anyone ask one" is that they run a container joined to the broker's network
|
||||
namespace and authenticate as the substrate's bootstrap admin.
|
||||
|
||||
**It is why a running module gets mistaken for a working one.** A test that cannot ask a module
|
||||
anything checks that its container is up, and a container being up is not a claim about function.
|
||||
That substitution has now hidden two separate faults in one week — a crash-looping runtime beside a
|
||||
healthy container of a similar name, and a catalogue that was never installed at all.
|
||||
healthy container of a similar name, and a catalog that was never installed at all.
|
||||
|
||||
**The mesh already has the shape for this.** An account scoped to a purpose, minted by the mesh and
|
||||
sealed to a holder, is exactly what provisioning does. What is missing is the recognition that
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
opened: 2026-09-14
|
||||
---
|
||||
|
||||
# 050 — The catalogue knows nothing that was built before it
|
||||
# 050 — The catalog knows nothing that was built before it
|
||||
|
||||
## Symptom
|
||||
|
||||
A mesh raised from bare metal built six modules in order: the shared base, a store, the catalogue,
|
||||
the control plane, a module, and a broker. Asked afterwards what it holds, the catalogue answered
|
||||
A mesh raised from bare metal built six modules in order: the shared base, a store, the catalog,
|
||||
the control plane, a module, and a broker. Asked afterwards what it holds, the catalog answered
|
||||
with three — exactly the three built *after* it started running:
|
||||
|
||||
```
|
||||
@@ -16,47 +16,47 @@ lavinmq built from <the catalogue repository>
|
||||
mesh-control built from <the control plane repository>
|
||||
```
|
||||
|
||||
Missing: the shared base, the store, and the catalogue itself.
|
||||
Missing: the shared base, the store, and the catalog itself.
|
||||
|
||||
The catalogue learns what exists by consuming the builder's announcement over the broker. Anything
|
||||
The catalog learns what exists by consuming the builder's announcement over the broker. Anything
|
||||
built before it was running was announced to nobody, and nothing goes back for it.
|
||||
|
||||
## Why this matters
|
||||
|
||||
**The modules it cannot see are the ones a mesh is made of.** On a fresh mesh the things built
|
||||
before the catalogue are, necessarily, the things the catalogue needed in order to exist: the base
|
||||
before the catalog are, necessarily, the things the catalog needed in order to exist: the base
|
||||
it was compiled against and the store it keeps its records in. So the hole is not random — it is
|
||||
always the foundation, on every mesh, at exactly the moment the graph is first populated.
|
||||
|
||||
**It breaks the question the catalogue exists to answer.** Build edges are derived facts between
|
||||
versions, and the base is the node nearly every other module hangs off. A catalogue with no record
|
||||
**It breaks the question the catalog exists to answer.** Build edges are derived facts between
|
||||
versions, and the base is the node nearly every other module hangs off. A catalog with no record
|
||||
of the base cannot know that moving it makes everything standing on it stale, so `catalog_stale`
|
||||
answers confidently and wrongly — the worst shape for a question about what to rebuild.
|
||||
|
||||
**Nothing reports the gap.** The catalogue does not know what it was not told, so it does not say
|
||||
**Nothing reports the gap.** The catalog does not know what it was not told, so it does not say
|
||||
"three of six" — it says three, and a reader with no independent count believes it. This was found
|
||||
by asking it a question and comparing the answer against what the mesh had just been watched doing.
|
||||
|
||||
**The record is not lost, only the catalogue's copy.** The control plane holds every build it
|
||||
**The record is not lost, only the catalog's copy.** The control plane holds every build it
|
||||
ordered, with commit, repository, path and resolved artifacts. So this is a gap between two stores
|
||||
that should agree, rather than information nobody has.
|
||||
|
||||
## Open questions
|
||||
|
||||
- **Should the catalogue backfill on start** — ask the control plane for the builds it already
|
||||
recorded and take them in? That fixes the fresh-mesh case and every case where the catalogue was
|
||||
- **Should the catalog backfill on start** — ask the control plane for the builds it already
|
||||
recorded and take them in? That fixes the fresh-mesh case and every case where the catalog was
|
||||
down while something was built, which is the same fault with a different cause.
|
||||
- **Or should announcements be durable**, so a build announced while nothing was listening is
|
||||
delivered when something is? That treats the catalogue as one consumer among several, which it
|
||||
delivered when something is? That treats the catalog as one consumer among several, which it
|
||||
already is.
|
||||
- **Or should the control plane be the record and the catalogue a projection of it?** Two stores
|
||||
- **Or should the control plane be the record and the catalog a projection of it?** Two stores
|
||||
that must agree, kept in step by events, is the arrangement that produced this.
|
||||
- **How would anyone notice next time?** The catalogue cannot compare itself to a source of truth
|
||||
- **How would anyone notice next time?** The catalog cannot compare itself to a source of truth
|
||||
it does not have. Whatever is chosen, something has to be able to say "these disagree".
|
||||
|
||||
## How this would be checked
|
||||
|
||||
| Rule | Checked by |
|
||||
|---|---|
|
||||
| The catalogue holds every module the mesh built | A mesh is raised from bare metal and the catalogue is asked; its list is compared against the control plane's build records, and a module in one and not the other is a failure. |
|
||||
| A change to the base makes what stands on it stale | The base is rebuilt on a fresh mesh and the catalogue names the modules that must follow — which requires it to know the base exists. |
|
||||
| The catalog holds every module the mesh built | A mesh is raised from bare metal and the catalog is asked; its list is compared against the control plane's build records, and a module in one and not the other is a failure. |
|
||||
| A change to the base makes what stands on it stale | The base is rebuilt on a fresh mesh and the catalog names the modules that must follow — which requires it to know the base exists. |
|
||||
|
||||
@@ -53,11 +53,11 @@ machine.
|
||||
|
||||
## Open questions
|
||||
|
||||
- **Which private registry, and does the bootstrap have one?** The catalogue holds `verdaccio`;
|
||||
- **Which private registry, and does the bootstrap have one?** The catalog holds `verdaccio`;
|
||||
a git host typically serves package registries too. ADR 0014 says *the* private registry as
|
||||
though there is one, and today a fresh mesh has neither until something installs it — so the
|
||||
first SDK build happens before the registry the record assumes.
|
||||
- **Or should the SDK be a published package?** The catalogue holds a private registry module, and a
|
||||
- **Or should the SDK be a published package?** The catalog holds a private registry module, and a
|
||||
published package is how the rest of the world does this — at the cost of a mesh needing that
|
||||
registry up before it can build anything, which is a bootstrap problem where there is currently
|
||||
none.
|
||||
|
||||
@@ -9,7 +9,7 @@ opened: 2026-09-16
|
||||
Adopting the store and broker as ordinary modules (issue 051) needs them reachable by
|
||||
consumers across the mesh, so genesis now raises `mesh-store` and `mesh-broker` bound to
|
||||
`0.0.0.0` rather than to loopback as the foundation used to. The packet filter is a module,
|
||||
installed several steps after the store, the broker and the catalogue are already running.
|
||||
installed several steps after the store, the broker and the catalog are already running.
|
||||
|
||||
Between each server coming up and the filter being applied, both listen on every interface
|
||||
of the machine with only the genesis bootstrap credentials, and nothing drops traffic to
|
||||
@@ -35,7 +35,7 @@ container in place must declare the same bind, and the module has to serve consu
|
||||
- Can a default-deny base filter (drop everything but loopback, established, and ssh) be
|
||||
applied at genesis, before the store and broker come up, and the mesh-scoped rules refined
|
||||
once the private network has addresses to name? The consumers that must reach the store
|
||||
(the catalogue) come up before the network step, so the `from: mesh` rule would have to be
|
||||
(the catalog) come up before the network step, so the `from: mesh` rule would have to be
|
||||
in place by then.
|
||||
- Or should the servers bind narrowly at genesis (loopback plus the container bridge) and
|
||||
widen only once the filter that protects them exists — accepting that a bind change is a
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
opened: 2026-09-17
|
||||
---
|
||||
|
||||
# 060 — The mesh cannot build most of its own catalogue
|
||||
# 060 — The mesh cannot build most of its own catalog
|
||||
|
||||
## Symptom
|
||||
|
||||
Only 8 of the catalogue's 70 modules carry a `build` section; 62 do not. A module without one
|
||||
Only 8 of the catalog's 70 modules carry a `build` section; 62 do not. A module without one
|
||||
cannot be built by the mesh's own builder: its runtime container pins a placeholder digest
|
||||
(`mesh-runtime-<m>@sha256:0000…`) that only an out-of-band script ever resolves — the lab builds
|
||||
the image on a workstation and rewrites the manifest before registering it. Asked to `build` such
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Diagnosis — 2026-09-18
|
||||
|
||||
The gap was mechanical for most of the catalogue and structural for a few.
|
||||
The gap was mechanical for most of the catalog and structural for a few.
|
||||
|
||||
**The mechanical part (fixed).** 44 of 70 modules now carry a `build` section and a Dockerfile
|
||||
mirroring the eight that already had one: two named bases (build and runtime), the module's own
|
||||
|
||||
+1
-1
@@ -41,4 +41,4 @@ in one missing manifest field.
|
||||
provably runs no provisioner.
|
||||
- Can validation know an image's entrypoints, so "compiled but never named by any container" is
|
||||
refusable at build time rather than discoverable only by an end-to-end bed?
|
||||
- Does any other converted module in the catalogue have the same shape today?
|
||||
- Does any other converted module in the catalog have the same shape today?
|
||||
|
||||
+1
-1
@@ -25,5 +25,5 @@ same bed: with the command named, the vhost and user are minted and the consumer
|
||||
connection over the overlay (bed run of 2026-09-18).
|
||||
|
||||
The open questions in the report — refusing a provider whose containers all default their
|
||||
command, and auditing the rest of the catalogue for the same shape — remain open; this record
|
||||
command, and auditing the rest of the catalog for the same shape — remain open; this record
|
||||
covers the one module the bed proved.
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
went to the composing code with two candidates: the declaration never named the trust, or it
|
||||
named it and was never applied.
|
||||
3. The composer's trust step asks which machine on the network is assigned a module serving the
|
||||
artifact-store provision. Two silent degradations sat in that path: a failed catalogue read
|
||||
artifact-store provision. Two silent degradations sat in that path: a failed catalog read
|
||||
returned "not found", and a failed per-machine assignment read *skipped that machine* and kept
|
||||
scanning. Either converts a transient inventory error into a declaration without the trust,
|
||||
under a push that reports success.
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ from a public registry the build environment does not reach:
|
||||
|
||||
apt-based installs in the same Dockerfiles succeed, so the build has ordinary internet: it is npm
|
||||
and image pulls specifically that are redirected to the mesh's registry. Observed while giving the
|
||||
catalogue its build sections (hq issue 060): 44 of 70 modules build from their own source with no
|
||||
catalog its build sections (hq issue 060): 44 of 70 modules build from their own source with no
|
||||
external fetch; these three need one and stop.
|
||||
|
||||
## Why it matters beyond the instance
|
||||
|
||||
+3
-3
@@ -15,15 +15,15 @@
|
||||
3. Ruled out: that the build environment has no internet. Package installs from the system's
|
||||
repositories succeed in the same Dockerfiles.
|
||||
|
||||
**Located in:** the builder (what it tells npm and the runtime) and the catalogue (three modules
|
||||
**Located in:** the builder (what it tells npm and the runtime) and the catalog (three modules
|
||||
whose Dockerfiles fetch what no manifest names). The image half is decided and built:
|
||||
ADR 0097 — a vendor image
|
||||
is declared under `build.on`, copied in, and a recipe fetching what is undeclared is refused. Still
|
||||
open: the package half — a build must be re-run against the mesh's proxying registry to place the
|
||||
404 — and the three recipes themselves, which now declare their images or are refused.
|
||||
|
||||
*2026-09-21, later.* The package half was placed by a run rather than a reproduction: the catalogue
|
||||
was read again, and exactly one recipe installs a public package — the catalogue module's own,
|
||||
*2026-09-21, later.* The package half was placed by a run rather than a reproduction: the catalog
|
||||
was read again, and exactly one recipe installs a public package — the catalog module's own,
|
||||
which installs a postgres driver into an empty directory. That module was built through the mesh's
|
||||
builder and installed in the genesis bed run that day, against the mesh's package registry as it
|
||||
now proxies the public one. The 404 the report saw is not reproducible on current code. No recipe
|
||||
|
||||
@@ -13,7 +13,7 @@ minted for that consumer↔vault pair. A pair has **exactly one** credential —
|
||||
rotation touches one holder and nothing else.
|
||||
|
||||
A module may only require a provision name once, so a module that requires `secret` receives
|
||||
**one value**. Counted across the catalogue at the time of writing, a module's own secrets other
|
||||
**one value**. Counted across the catalog at the time of writing, a module's own secrets other
|
||||
than its broker account number as follows:
|
||||
|
||||
| own secrets | modules |
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
a pair credential is keyed on (name, consumer node, consumer module, provider), and the login
|
||||
the vault derives is keyed on the (node, module) pair. Two secrets for one module and one vault
|
||||
collide on every key.
|
||||
2. The third question was answered by reading the catalogue as it is today: nine modules hold two
|
||||
2. The third question was answered by reading the catalog as it is today: nine modules hold two
|
||||
or more own secrets besides their broker account. Two hold a relay user beside a relay
|
||||
password — the user is a name, not a secret, and could travel as configuration. The other
|
||||
seven hold genuinely independent values with independent lifetimes: an internal token beside
|
||||
@@ -18,7 +18,7 @@
|
||||
is a manifest-vocabulary decision, and it moves the pair key: the credential must be keyed on
|
||||
the local name, not the provision name, or the second pair overwrites the first.
|
||||
|
||||
**Located in:** the manifest's `secrets` vocabulary (the catalogue parser) and the pair
|
||||
**Located in:** the manifest's `secrets` vocabulary (the catalog parser) and the pair
|
||||
credential's key (the controller's secret store). Fixed as
|
||||
ADR 0094: the
|
||||
key gains the local name, empty for every existing pair, so nothing that exists changed.
|
||||
|
||||
@@ -7,12 +7,12 @@ opened: 2026-09-21
|
||||
## Symptom, as observed
|
||||
|
||||
The control plane's manifest lives at the root of its own repository, where the mesh reads it
|
||||
whenever it builds the controller from source, and again in the catalogue under
|
||||
whenever it builds the controller from source, and again in the catalog under
|
||||
`modules/mesh-controller`, where the installer reads it at genesis. The two differ only in how the
|
||||
image is named — a build section in one, a placeholder digest in the other — and are otherwise
|
||||
meant to be the same document.
|
||||
|
||||
Changing the catalogue copy alone (to make the controller read its credentials from files,
|
||||
Changing the catalog copy alone (to make the controller read its credentials from files,
|
||||
ADR 0086) produced a mesh that
|
||||
installed correctly and then could not be pushed to: the first rebuild of the controller from
|
||||
source replaced the mesh's record with the repository's copy, which still carried the old shape,
|
||||
@@ -30,6 +30,6 @@ machine as doing what it was told throughout, because nothing had been sent.
|
||||
|
||||
## What would close it
|
||||
|
||||
Either the catalogue copy goes and genesis reads the controller's manifest from the controller's
|
||||
Either the catalog copy goes and genesis reads the controller's manifest from the controller's
|
||||
repository (the installer already has the checkout, since it builds from it), or a check refuses
|
||||
two copies that differ outside the image lines. The first is the honest one.
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
1. The two documents were compared. They differ in exactly one place: the repository's names its
|
||||
server container by a build artifact and carries the build section that produces it; the
|
||||
catalogue's names a placeholder image digest and carries no build section. Everything else was
|
||||
catalog's names a placeholder image digest and carries no build section. Everything else was
|
||||
equal on the day of reading, which is the only day that can be said of.
|
||||
2. Who reads which. The mesh's own builder reads the repository's, whenever the control plane is
|
||||
rebuilt from source, and registers the manifest it built with the artifact resolved to the
|
||||
image — that is what replaced the mesh's record. The installer's step 9 read the catalogue's,
|
||||
image — that is what replaced the mesh's record. The installer's step 9 read the catalog's,
|
||||
pinned its placeholder to the image the registry assigned, and registered that. Nothing else
|
||||
read the catalogue's copy.
|
||||
read the catalog's copy.
|
||||
3. The decision was already taken. ADR 0069
|
||||
says the control plane's manifest belongs in its own repository, not the catalogue, and the
|
||||
raising design names the catalogue copy as "a thing that can be removed rather than a thing
|
||||
says the control plane's manifest belongs in its own repository, not the catalog, and the
|
||||
raising design names the catalog copy as "a thing that can be removed rather than a thing
|
||||
that must be designed". It was not removed because step 9 had no other source — at genesis the
|
||||
installer carries the builder, not the control plane, and reads manifests off a checkout put on
|
||||
the machine by hand.
|
||||
@@ -20,10 +20,10 @@
|
||||
artifact resolved to the image the machine now holds. Step 9 was reading a second copy of a
|
||||
document it had been handed six steps earlier.
|
||||
|
||||
**Located in:** mesh-host `internal/bootstrap` (steps 3 and 9), and the catalogue's copy. The fix
|
||||
**Located in:** mesh-host `internal/bootstrap` (steps 3 and 9), and the catalog's copy. The fix
|
||||
keeps the built manifest from step 3, registers it at step 9 with the built image's bare id
|
||||
re-pinned to the registry's reference, and deletes the catalogue's copy. The builder module's
|
||||
manifest is still the catalogue's and still pinned from a placeholder — ADR 0069 puts it in the
|
||||
re-pinned to the registry's reference, and deletes the catalog's copy. The builder module's
|
||||
manifest is still the catalog's and still pinned from a placeholder — ADR 0069 puts it in the
|
||||
control plane's repository too, and it is not there yet; that is a smaller instance of the same
|
||||
gap, left open here and named in the lab's genesis check. Ruled out: teaching the two copies to
|
||||
stay equal (a check across two repositories that only fires after somebody edits one), and reading
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
opened: 2026-09-21
|
||||
---
|
||||
|
||||
# Beds carry copies of catalogue manifests, so a catalogue change is proven nowhere
|
||||
# Beds carry copies of catalog manifests, so a catalog change is proven nowhere
|
||||
|
||||
## Symptom, as observed
|
||||
|
||||
The per-module beds (`assigned-catalogue-small`, `assigned-catalogue-apps`, `assigned-grafana`,
|
||||
`assigned-model-usage`, `assigned-redis`, and others) build the manifests they install inline, as
|
||||
JSON in the test, rather than reading `modules/<name>/module.json` from the catalogue checkout the
|
||||
JSON in the test, rather than reading `modules/<name>/module.json` from the catalog checkout the
|
||||
lab is pointed at. The copies were taken when each bed was written and have not moved since.
|
||||
|
||||
Converting six modules to file-delivered secrets (ADR 0086)
|
||||
therefore changed nothing any of those beds run. Worse: the copies still deliver secrets through
|
||||
an env-file without the declared exception, which the catalogue engine now refuses, so the beds
|
||||
an env-file without the declared exception, which the catalog engine now refuses, so the beds
|
||||
would fail against a current controller for a reason that has nothing to do with what they test.
|
||||
|
||||
## Why it matters beyond this instance
|
||||
@@ -22,13 +22,13 @@ would fail against a current controller for a reason that has nothing to do with
|
||||
bed: a manifest with two sources of truth, and the one the proof runs against is the stale one.
|
||||
- "Proven in the lab" is the standard playbook 06 sets for a module. A bed that installs a copy
|
||||
proves the copy.
|
||||
- The genesis bed and the vault bed already read the catalogue (`--catalog` and a
|
||||
- The genesis bed and the vault bed already read the catalog (`--catalog` and a
|
||||
`catalogueManifest()` helper that swaps the build artifact for the image the lab built); the
|
||||
pattern exists and is one helper away for the rest.
|
||||
|
||||
## What would close it
|
||||
|
||||
Every bed that installs a catalogue module reads its manifest from the catalogue checkout,
|
||||
Every bed that installs a catalog module reads its manifest from the catalog checkout,
|
||||
rewriting only what the lab must (the built artifact's image, a lab-local address). The inline
|
||||
copies go. Until then, each bed's copy is updated by hand alongside the catalogue, which is how
|
||||
copies go. Until then, each bed's copy is updated by hand alongside the catalog, which is how
|
||||
the six conversions were proven.
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
# Diagnosis — 2026-09-21
|
||||
|
||||
1. Every bed was read against the catalogue. Of forty-five, thirteen already read a manifest
|
||||
from the catalogue checkout, twenty-one built one or more inline, and eleven install no
|
||||
catalogue module. The thirteen readers used five private copies of one loader, and the copies
|
||||
1. Every bed was read against the catalog. Of forty-five, thirteen already read a manifest
|
||||
from the catalog checkout, twenty-one built one or more inline, and eleven install no
|
||||
catalog module. The thirteen readers used five private copies of one loader, and the copies
|
||||
had drifted: one never resolved a runtime artifact to the image the lab stocks, so a module
|
||||
the mesh builds travelled to the machine unresolved; one still asked the catalogue for two
|
||||
the mesh builds travelled to the machine unresolved; one still asked the catalog for two
|
||||
modules by names it no longer uses and recorded the miss as "not assigned".
|
||||
2. The inline copies fall into three kinds, and only the first is what the report assumed:
|
||||
- copies that differ from the catalogue only in what the lab must rewrite — an image, a
|
||||
- copies that differ from the catalog only in what the lab must rewrite — an image, a
|
||||
build section, an optional key dropped. Ten modules across eight beds;
|
||||
- a second provider raised beside the foundation's. The catalogue's postgres and lavinmq
|
||||
- a second provider raised beside the foundation's. The catalog's postgres and lavinmq
|
||||
*claim* the foundation's store and broker and adopt them in place; four beds raise a
|
||||
second one next to it, renamed and on a private network. Reading the catalogue there
|
||||
second one next to it, renamed and on a private network. Reading the catalog there
|
||||
changes what the bed raises, and its assertions with it;
|
||||
- a module cut down to the shape a mesh mechanism needs — no upstream server, a secret in
|
||||
the environment, a requirement edge removed so no second provider is wanted — under a
|
||||
catalogue module's name. Twelve beds. These are mesh tests wearing a module's name, and
|
||||
the honest fix is a name of their own, not a catalogue read
|
||||
catalog module's name. Twelve beds. These are mesh tests wearing a module's name, and
|
||||
the honest fix is a name of their own, not a catalog read
|
||||
([issue 074](../074-a-mesh-test-wears-a-catalogue-modules-name/00-report.md)).
|
||||
3. The receipt claimed the host, the controller and the lab and never the catalogue, so even a
|
||||
bed that read the catalogue could not be told apart from one that had read it last week.
|
||||
3. The receipt claimed the host, the controller and the lab and never the catalog, so even a
|
||||
bed that read the catalog could not be told apart from one that had read it last week.
|
||||
4. While converting, the images the beds stock for the modules' runtimes were found to date
|
||||
from two weeks before the manifests they serve — built by hand, by a script the run never
|
||||
calls. The run rebuilds the host and the controller and not these
|
||||
([issue 075](../075-a-stocked-runtime-image-is-never-rebuilt-by-the-run/00-report.md)).
|
||||
|
||||
**Located in:** mesh-lab, the integration beds and their harness. The fix gives the harness one
|
||||
loader that reads the catalogue and rewrites only what the lab must, converts the first kind of
|
||||
copy to it, folds the five private loaders onto it, makes the receipt claim the catalogue, and
|
||||
adds a unit test that refuses an inline copy naming a catalogue module unless the bed is declared
|
||||
loader that reads the catalog and rewrites only what the lab must, converts the first kind of
|
||||
copy to it, folds the five private loaders onto it, makes the receipt claim the catalog, and
|
||||
adds a unit test that refuses an inline copy naming a catalog module unless the bed is declared
|
||||
with its reason. The second and third kinds are declared there; each declaration names the work
|
||||
that removes it. Decided in ADR 0089.
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
opened: 2026-09-21
|
||||
---
|
||||
|
||||
# A mesh test wears a catalogue module's name
|
||||
# A mesh test wears a catalog module's name
|
||||
|
||||
## Symptom, as observed
|
||||
|
||||
Twelve lab beds install a module named `redis`, `grafana`, `plex`, `sonarr`, `minio`, `postgres`,
|
||||
`route-proxy` or `hello-web` that is not the catalogue's module of that name. Each is cut down to
|
||||
`route-proxy` or `hello-web` that is not the catalog's module of that name. Each is cut down to
|
||||
what the bed's mechanism needs: the sidecar runtime without the upstream server it manages; a
|
||||
token or an API key in the container's environment where the catalogue delivers a file; a
|
||||
token or an API key in the container's environment where the catalog delivers a file; a
|
||||
requirement on a route or a certificate authority removed so no second provider is needed; a
|
||||
vault-granted secret turned into one the module mints itself; a route contribution in the shape a
|
||||
decision replaced. One bed's header says its manifests are "verbatim from the catalogue" and its
|
||||
manifest adds three resources the catalogue has not got.
|
||||
manifest adds three resources the catalog has not got.
|
||||
|
||||
Found while diagnosing [issue 073](../073-beds-carry-copies-of-catalogue-manifests/00-report.md);
|
||||
the beds are listed, each with what it cuts, in the lab's `beds-read-the-catalogue` unit test.
|
||||
@@ -22,16 +22,16 @@ the beds are listed, each with what it cuts, in the lab's `beds-read-the-catalog
|
||||
|
||||
- **A green bed named for a module reads as that module proven.** The status view counts a bed
|
||||
by the module it names; a bed proving a grant mechanism with a `redis` that mints its own
|
||||
secret proves nothing about the catalogue's redis, which requires the vault's.
|
||||
secret proves nothing about the catalog's redis, which requires the vault's.
|
||||
- **The cut is invisible.** Nothing distinguishes "this is redis" from "this is a redis-shaped
|
||||
fixture" except reading the literal against the catalogue, which is what issue 073 found nobody
|
||||
fixture" except reading the literal against the catalog, which is what issue 073 found nobody
|
||||
had done.
|
||||
- ADR 0089 now refuses the
|
||||
copy; until each bed is renamed or made to read the catalogue, it is declared debt.
|
||||
copy; until each bed is renamed or made to read the catalog, it is declared debt.
|
||||
|
||||
## What would close it
|
||||
|
||||
Each of the twelve beds either reads the catalogue's module and meets its declared requirements
|
||||
Each of the twelve beds either reads the catalog's module and meets its declared requirements
|
||||
(a route needs the route module beside it; a secret needs the vault), or gives its fixture a name
|
||||
that is no catalogue module's — `a-cache`, `a-store`, `a-sidecar` — so a green run claims exactly
|
||||
that is no catalog module's — `a-cache`, `a-store`, `a-sidecar` — so a green run claims exactly
|
||||
what it proved. The declared list in the unit test is empty of the `WEARING` reason.
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
from the manifest's name, and the tool runtime binds its queues under the module name baked
|
||||
into its image. A fixture named `redis-fixture` running the real redis runtime would be
|
||||
refused the queues it serves on. The name is not a label. **A fixture that runs a module's
|
||||
runtime carries the module's name**, and therefore reads the catalogue
|
||||
runtime carries the module's name**, and therefore reads the catalog
|
||||
(ADR 0093).
|
||||
3. Of the twelve, the two whose only cut was the vault's secret — the grant bed and the
|
||||
backend-network bed, both redis — read the catalogue's redis now and install the vault beside
|
||||
backend-network bed, both redis — read the catalog's redis now and install the vault beside
|
||||
it, as the vault bed does. The rest are declared with this reason: four sidecar beds need the
|
||||
module's server raised (and, for grafana and sonarr, the route module); the minio and postgres
|
||||
grant beds raise a second store beside the foundation's; the route-forwarding bed needs the
|
||||
@@ -22,13 +22,13 @@
|
||||
by decision; converted two at a time as the modules they need are raised beside them.
|
||||
|
||||
*2026-09-21, later.* Ten conversions were the wrong shape. Six of the ten beds duplicated coverage
|
||||
that exists elsewhere since the catalogue beds and the whole-mesh beds read the catalogue: the four
|
||||
that exists elsewhere since the catalog beds and the whole-mesh beds read the catalog: the four
|
||||
sidecar beds proved a sidecar alone, which the module beds prove whole; the minio and postgres
|
||||
grant beds proved a grant with a second store beside the foundation's, which the grant bed and
|
||||
the vault bed prove against the catalogue. Retired, with their scenarios. Two remain declared:
|
||||
the vault bed prove against the catalog. Retired, with their scenarios. Two remain declared:
|
||||
route-forwarding, which needs the certificate authority beside the proxy, and the large mesh test.
|
||||
|
||||
*Route-forwarding's conversion is blocked:* the catalogue's authority cannot be raised as written
|
||||
*Route-forwarding's conversion is blocked:* the catalog's authority cannot be raised as written
|
||||
([issue 076](../076-a-served-fact-made-at-first-start-cannot-be-served/00-report.md)).
|
||||
|
||||
*Later the same day.* Route-forwarding converted, with the authority beside the proxy
|
||||
@@ -37,23 +37,23 @@ route-forwarding, which needs the certificate authority beside the proxy, and th
|
||||
*2026-09-21, late.* The last three `WEARING` declarations are gone. The large mesh test's fixtures
|
||||
are `a-store` (a provider with no resources) and `adopted-analytics`, its builder fixture retired below; the
|
||||
runtime-restart bed's fixture is `a-runtime`. The credential-mechanism bed could not be renamed:
|
||||
its runtime image is the catalogue's, and a runtime names its queues by the module compiled into
|
||||
its runtime image is the catalog's, and a runtime names its queues by the module compiled into
|
||||
it, so the mesh's account for a module of another name is refused at the broker. It was retired —
|
||||
the grant end-to-end bed proves the same login against the catalogue's redis, with the mesh writing
|
||||
the grant end-to-end bed proves the same login against the catalog's redis, with the mesh writing
|
||||
the contributions the retired bed wrote by hand — and its one assertion nothing else made (the
|
||||
provider needs no seal key) moved there. Running the large test's renamed tests found two things
|
||||
its long idleness had hidden: the consumer fixture's login exceeded a backend's twenty characters
|
||||
(ADR 0049; it has a slug now), and the builder-as-module test declared the builder's image as an
|
||||
upstream artifact by bare image ID, which a registry copy cannot fetch since ADR 0096 — retired,
|
||||
since genesis installs the catalogue's builder and proves the same. The large test as a whole has
|
||||
since genesis installs the catalog's builder and proves the same. The large test as a whole has
|
||||
not been run end to end; its renamed tests and the tests around them have.
|
||||
|
||||
*2026-09-22.* The large mesh test was run end to end, for the first time in weeks: 12 of 25 passed.
|
||||
Everything that failed was the bed lagging the mesh, and one defect of the mesh's own. The bed now
|
||||
derives the anchor's filter after placing it on the overlay (ADR 0088), declares the ports its
|
||||
fixtures listen on, waits for an image pulled at apply, starts the hand-started host and builder
|
||||
once on a warm return, and reads the resolver modules from the catalogue instead of the example
|
||||
modules that moved there. Three more tests were retired for the catalogue beds that prove them
|
||||
once on a warm return, and reads the resolver modules from the catalog instead of the example
|
||||
modules that moved there. Three more tests were retired for the catalog beds that prove them
|
||||
(the modules resolving together and the forge, whole-mesh-novox; the cache grant, the grant bed)
|
||||
and one for what every later test proves (the hub filtered without severing the mesh). The
|
||||
defect: every machine named twice over in the files the mesh writes
|
||||
|
||||
@@ -10,7 +10,7 @@ A per-module bed stocks the module's runtime image — the tool runtime carrying
|
||||
code — from the workstation's image store, by tag. The image is built by hand, by a script in the
|
||||
lab repository that the suite never calls. On the day issue 073 was worked, the images for six
|
||||
modules whose beds were about to run dated from two weeks before the manifests they were to be
|
||||
installed with; the catalogue's code for those modules had changed since, and every bed would have
|
||||
installed with; the catalog's code for those modules had changed since, and every bed would have
|
||||
passed against the old image. The suite's own rule — *the run rebuilds what it tests* — is held
|
||||
for the host binary and the controller image and not for these.
|
||||
|
||||
@@ -20,13 +20,13 @@ for the host binary and the controller image and not for these.
|
||||
the module proven; nothing says the image predates the code.
|
||||
- **It is the same fault [issue 005](../005-pipeline-test-harness-unbuildable/00-report.md)
|
||||
named**, one layer down: a stale artifact reporting success against code that moved.
|
||||
- The receipt now names the catalogue's commit (ADR 0089),
|
||||
- The receipt now names the catalog's commit (ADR 0089),
|
||||
which makes this sharper, not better: the receipt claims a commit whose runtime code was never
|
||||
built into what ran.
|
||||
|
||||
## What would close it
|
||||
|
||||
The suite builds, or refuses to stock, a module runtime whose image is older than the module's
|
||||
source in the catalogue the run is pointed at — the same treatment the host binary and the
|
||||
source in the catalog the run is pointed at — the same treatment the host binary and the
|
||||
controller image get. Or the scenario says which images it stocks stale, and the receipt says
|
||||
so too.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
image store held, built by hand by a script the suite never called.
|
||||
2. The rule that covers the rest — *the run rebuilds what it tests* — was extended to these. For
|
||||
the beds named, every runtime their scenarios stock is compared against the module's source in
|
||||
the catalogue and the tool runtime and SDK it is built on; the image is rebuilt where it is
|
||||
the catalog and the tool runtime and SDK it is built on; the image is rebuilt where it is
|
||||
older, missing, or the source has uncommitted changes, and a build that fails stops the suite.
|
||||
Proven by removing a runtime image and watching the run rebuild it before the bed passed.
|
||||
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
opened: 2026-09-21
|
||||
---
|
||||
|
||||
# A served fact made at first start cannot be served, so the catalogue's authority cannot start
|
||||
# A served fact made at first start cannot be served, so the catalog's authority cannot start
|
||||
|
||||
## Symptom, as observed
|
||||
|
||||
The catalogue's certificate authority module declares its root certificate, its root key and
|
||||
The catalog's certificate authority module declares its root certificate, its root key and
|
||||
that key's password as its own secrets, and writes each into a file the container is told to
|
||||
initialise from. An own secret nobody delivers is minted by the mesh as random bytes, and random
|
||||
bytes are not a certificate: issued that way, the authority cannot initialise. It could be
|
||||
raised by an operator making a root with openssl and delivering all three through `secret
|
||||
accept` — the whole-mesh bed did exactly that, and has not run since it was converted — but a
|
||||
module that only starts once a person has hand-made its key material is not a module a mesh
|
||||
can raise. Found while converting the route-forwarding bed to the catalogue's proxy, which
|
||||
can raise. Found while converting the route-forwarding bed to the catalog's proxy, which
|
||||
requires the authority beside it.
|
||||
|
||||
The authority can make its own root at first start — the certificate bed raises it that way and
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
ADR 0098.
|
||||
|
||||
**Proven** the same day: the route-forwarding bed raises the authority, the proxy and a consumer
|
||||
from the catalogue on one node and serves a public name through the proxy. Two things the run
|
||||
from the catalog on one node and serves a public name through the proxy. Two things the run
|
||||
taught, both about the bed rather than the decision:
|
||||
|
||||
- The authority certifies itself for the machine's private-network address, which is what a
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
**Located in:** the two refusals and the proxy's manifest. Decided in
|
||||
ADR 0099; proven by unit
|
||||
tests on the host (the step runs again when its file changed, and not when it did not; the
|
||||
container naming the step is recreated after it ran) and the catalogue-wide manifest test.
|
||||
container naming the step is recreated after it ran) and the catalog-wide manifest test.
|
||||
|
||||
**What closed it, and what did not.** The report named two ways the fact changes: the provider
|
||||
moved, or its state wiped. The move is closed: it rewrites the consumer's binding, the step names
|
||||
|
||||
@@ -18,7 +18,7 @@ Found by carrying the large mesh bed's retired tenancy assertions into the grant
|
||||
|
||||
A grant is the mesh's promise that a consumer gets what it asked for and nothing else. The
|
||||
promise was checked on the key pattern and never on the command set, and the one bed that had
|
||||
asked was retired before it was run against the catalogue's module.
|
||||
asked was retired before it was run against the catalog's module.
|
||||
|
||||
## What would close it
|
||||
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
**Located in:** the redis module's client. Not a decision. Proven by the grant end-to-end bed.
|
||||
|
||||
*On review.* `INFO` is in the dangerous category and several client libraries ask it at connect;
|
||||
it reads nothing a consumer keeps, so it is allowed back. Whether the catalogue's two cache
|
||||
it reads nothing a consumer keeps, so it is allowed back. Whether the catalog's two cache
|
||||
consumers need anything else the category removes is unverified — and moot until they present the
|
||||
login they were granted ([issue 081](../081-a-cache-consumer-does-not-use-the-login-it-was-granted/00-report.md)).
|
||||
|
||||
@@ -6,7 +6,7 @@ opened: 2026-09-22
|
||||
|
||||
## Symptom
|
||||
|
||||
Two catalogue modules require the cache and are given a login and a password under a keyspace of
|
||||
Two catalog modules require the cache and are given a login and a password under a keyspace of
|
||||
their own ([issue 080](../080-a-cache-grant-lets-the-consumer-flush-the-server/00-report.md)),
|
||||
and neither tells its software the login: each hands it the host, the port and the password only,
|
||||
so the software authenticates as the server's default user and writes wherever it likes — or is
|
||||
@@ -14,7 +14,7 @@ refused, if the default user is closed. The grant is honoured by the provider an
|
||||
consumer.
|
||||
|
||||
Found on review of issue 080, by reading the two manifests. Not run: no bed installs either
|
||||
module against the catalogue's cache and reads what it wrote.
|
||||
module against the catalog's cache and reads what it wrote.
|
||||
|
||||
## Why it matters beyond the instance
|
||||
|
||||
@@ -25,5 +25,5 @@ working — and a module that works as the default user works.
|
||||
## What would close it
|
||||
|
||||
Each module passes the login it was bound (`${bound:redis-cache:as}`) to its software's username
|
||||
setting, and a bed installs one of them against the catalogue's cache and reads back that its keys
|
||||
setting, and a bed installs one of them against the catalog's cache and reads back that its keys
|
||||
sit under its login.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user