145, partly resolved. The sentence that was true for eleven hours of a mesh in which no module could reach another now says what it is not a claim about: that is the mesh and the machines agreeing, and nothing here dials a provision. It checks nothing and does not pretend to — ADR 0146 decides the check and is deliberately not built. What changed is that the report no longer implies otherwise. Stays open for that reason. Carried forward: 0146's check needs an internal name fetched over TLS with the certificate verified, and until today no machine trusted the mesh's authority. Three of four do now, so whoever builds it does not have to solve that first. 107, diagnosed and deliberately not built. The premise is confirmed in the host's own words — unknown fields are refused because "a field the host does not know is a thing the control plane believes it asked for" — so the fix is a flag day, not an addition. 087 now makes the cost measurable, and the measurement is why it waits: one machine of four runs an older host, it is parked, and nothing delivers a host at all (142). Shipping the field means hand-placing binaries and unparking a machine, and one missed in that sequence is unreachable, not degraded. The fault it prevents has never been observed. 142 gains the note that it is 107's gate, and that it is what makes a declaration field cost a rollout instead of an expedition. A judgement about order, not a refusal, and cheap to overrule.
5.4 KiB
status, opened, located-in, fixed-by, amended-design
| status | opened | located-in | fixed-by | amended-design | |||
|---|---|---|---|---|---|---|---|
| located | 2026-09-29 |
|
03-DESIGN/01-to-be/05-the-node-host.md |
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/upgradecan 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. ButReplaced()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?
What now waits on this (2026-09-30)
Issue 107 — a declaration carries no order, so a host cannot tell an older one from a newer. Its fix adds a field to the declaration, and a host refuses a declaration carrying a field it does not know, whole. So it is a flag day: every host upgraded, then the controller.
With nothing delivering a host, that means placing a binary by hand on every machine and unparking the adopted one, and a machine missed in the sequence is a machine the mesh cannot send anything to at all. 107 is held for that reason rather than for anything in its own diagnosis.
This is what makes a declaration field cost a rollout instead of an expedition, which is a use for this record beyond keeping machines current: it is the thing standing between the mesh and its own protocol evolving.