diff --git a/04-ISSUES/142-the-host-is-the-one-thing-the-mesh-does-not-deliver/00-report.md b/04-ISSUES/142-the-host-is-the-one-thing-the-mesh-does-not-deliver/00-report.md new file mode 100644 index 0000000..657c501 --- /dev/null +++ b/04-ISSUES/142-the-host-is-the-one-thing-the-mesh-does-not-deliver/00-report.md @@ -0,0 +1,76 @@ +--- +status: located +opened: 2026-09-29 +located-in: + - mesh-host internal/upgrade + - mesh-host cmd/mesh-host + - mesh-controller (no build source for the host; no resource delivers it) +fixed-by: +amended-design: +--- + +# 142 — The host is the one thing the mesh does not deliver + +## What was observed + +A change to the host was merged and could not reach any machine without a person copying a file. + +Checked on the mesh of four machines, 2026-09-29: + +- **The host is not a build target.** Asked what had been built for it, the control plane answered + `nothing has been built for mesh-host`. A merge on the forge builds every changed module and the + control plane itself, because the control plane is a module. The host is not one, and nothing + builds it. +- **No declaration delivers it.** No resource kind names an executable to place on a machine, and + nothing on a machine fetches one. +- **The half that recovers from a bad host exists and is unused.** `internal/upgrade` can report that + the executable this process started from has been replaced on disk, and records which version last + completed a reconcile so a shell script can roll back a host that will not start. The launcher reads + that record and rolls back. But `Replaced()` is called by nothing except its own tests — the + recovery is wired and the delivery was never built. +- **Every machine runs a byte-identical binary, stamped by hand.** All four carry the same size and + the same timestamp, from the last time somebody built it on a workstation and copied it out. No + package owns the file. + +## Why it matters beyond this instance + +**The component that implements updating is the one thing not updated.** The mesh's stated shape is +that a push produces the right builds and they reach the machines running them with nobody asking. It +is true of every module and of the control plane. It is false for the host, which is what applies all +of them. + +**It is a bootstrap problem being answered by a person.** The host cannot be an ordinary module +because the host is what applies modules; a module that replaces the thing applying it has to survive +its own replacement. That is a real difficulty, and the work already done — noticing that the +executable changed, recording a known-good version, a launcher that rolls back — is the hard half of +solving it. What is missing is the easy half, and its absence makes the hard half dead code. + +**A hand-copied binary has no record anywhere.** Nothing says which version a machine runs, so +nothing can say a machine is behind, and the mesh's own account of itself — every machine current with +its source — cannot include the host. Four machines agreeing today is luck, not a property. + +**And it silently gates any change that starts in the host.** A change that needs the host to report +something new cannot be rolled out by merging it: the control plane must wait for a person, and until +then it either refuses what depends on the new report or renders something wrong. That cost is paid by +every future change of this shape, and it was paid today. + +## Open questions + +- How is the host delivered without being applied by itself? A candidate shape: the host is built like + anything else, published as an artifact, and the *running* host fetches and stages the next one, then + stands aside — which is what `Replaced()` was written for and what the launcher's rollback already + covers. +- **Should this ride the bus, rather than becoming a mechanism of its own?** Everything else that + reaches a machine already does: a declaration is sent over it, a report comes back over it, and a + build announces what it produced on it, which is how a module's new version reaches the machines + running it. A host build announcing itself the same way, consumed by the host already running, + would make this the existing mechanism pointed at one more artifact rather than a second way of + delivering things. It would also give the machine somewhere to say which host it is running, on the + report it already sends. +- What records which version of the host a machine runs, so "behind" is answerable? Nothing does now. +- Does the host's version belong in its report, beside the other facts a machine states about itself? +- Who decides when a machine takes a new host — the mesh, on a build, or an operator per machine as + with converging? The rollback path means a bad host costs a reconcile rather than a machine, which + argues for the former. +- Does the same gap apply to the launcher and the units beside the binary, which are also files no + declaration names?