Issue 142: the host is the one thing the mesh does not deliver #172
@@ -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?
|
||||
Reference in New Issue
Block a user