The host self-updates, and an archive cannot be undeclared

161 resolved and verified on a machine: the workstation runs a host the
mesh compiled, published, delivered and started, applying declarations
and reporting the version it was delivered as.

The system it was built for comes from the artifact — the one thing a
toolchain takes from a module, which 0142 already allowed because the
target is a property of the artifact. The version comes from where the
binary sits, which 0142 decided and nothing had implemented.

Two mistakes on the way, both caught by reading the output rather than
the line that claimed success. A second -ldflags does not merge with the
first: the binary gained its system and lost -s -w, 12.2MB against 8.5MB.
And the delivered binary was named after its package, so the first
delivery was correct, reported success and was invisible to the launcher.

A delivered host that cannot apply is a machine the mesh cannot repair,
because the declaration that would fix it is the one it cannot apply. The
launcher's fallback is what made that an inconvenience instead of an
expedition.

162 is new and not about the host: an archive has no removal, so a module
using one can never be unassigned, and the attempt takes the whole apply
with it — the machine applies nothing else either. It is how undoing the
first delivery froze the workstation.
This commit is contained in:
2026-09-30 13:16:37 +02:00
parent 4cf941d858
commit 3c535ead31
4 changed files with 137 additions and 2 deletions
@@ -118,3 +118,23 @@ is back on its hand-placed binary until it is answered.
**The fallback is what made that safe**, and it was not luck: the launcher runs the pinned version, or
the newest delivered one, or the one placed by hand — so moving the delivered versions aside restored
the machine in one step.
## Self-update works (2026-09-30, end of the day)
```
running: /usr/lib/nox-mesh-host/versions/093231796eb0/nox-mesh-host
mesh-controller node show shanks
host 093231796eb0
```
One machine runs a host the mesh compiled, published, delivered and started, applying declarations and
reporting the version it was delivered as. The two facts a delivered binary was missing are
[issue 161](../161-a-delivered-host-carries-none-of-its-link-time-facts/01-resolution.md) and resolved:
the system comes from the artifact, the version from where the binary sits.
**Three machines still run a hand-placed host**, and rolling each forward is one assignment and one
push. The control node is worth last.
One thing this found on the way out: an archive cannot be undeclared, and the attempt stops the machine
applying anything at all — [issue 162](../162-an-archive-cannot-be-undeclared/00-report.md). It is how
undoing the first delivery froze the workstation, and it is not specific to the host.
@@ -1,10 +1,10 @@
---
status: located
status: resolved
opened: 2026-09-30
located-in:
- mesh-host cmd/mesh-host/main.go (version and builtFor, both set at link time)
- mesh-controller internal/builder (the toolchain, which deliberately takes nothing from the module)
fixed-by:
fixed-by: mesh-controller (the system stamp, and one linker flag rather than two), mesh-host (the version read from the path) — verified on a machine 2026-09-30, 01-resolution.md
amended-design:
---
@@ -0,0 +1,59 @@
# 161 — resolved: a host the mesh built runs a machine
*2026-09-30. Measured on the workstation.*
```
running: /usr/lib/nox-mesh-host/versions/093231796eb0/nox-mesh-host
agent: active
reconciles in the last six minutes: 1
mesh-controller node show shanks
host 093231796eb0
```
A binary the mesh compiled, published to its own registry, delivered over the bus, started by the
launcher, applying declarations, and reporting a version that names the build it came from.
## The two facts, and where each now comes from
**The system it was built for comes from the artifact.** ADR 0142 already made the target a property
of the artifact rather than of the recipe, so the toolchain names the variable it fills and the
artifact supplies the value. It is the one thing a toolchain takes from a module, and it is stated
rather than inferred.
**The version comes from where the binary sits**, which is what
[ADR 0142](../../02-DECISIONS/0142-the-mesh-delivers-its-own-components-as-binaries.md) decided and
nothing had implemented: a delivered host reads the directory it was unpacked into. A host placed by
hand keeps its link-time stamp, which is the honest answer for one the mesh did not deliver — and is
every other machine today.
## Two mistakes on the way, both found by reading the output
**A repeated flag is not a merged one.** The stamp was appended as a second `-ldflags`, and the Go
command takes the last and drops the first. The binary gained its system and lost `-s -w`: 12.2MB
against 8.5MB, with its debug info. The comment I had written said the linker "accepts and merges"
them. It does not. Linker flags are the toolchain's own list now, composed into one flag, and a test
refuses a compile line that carries `-ldflags` itself.
**The delivered binary was named after its package.** `cmd/mesh-host` builds `mesh-host`; every
machine runs `nox-mesh-host`, which is what the launcher looks for inside a version. The first
delivery landed, reported `created … 1 file(s)`, and was invisible. An artifact says what its
executable is called now.
Both were caught by listing the directory and reading the binary rather than believing the line that
said it worked.
## What this cost while it was wrong, and what saved it
A delivered host that cannot apply is a machine the mesh cannot repair, because the declaration that
would fix it is the declaration it cannot apply. The workstation was restored by moving the delivered
versions aside so the launcher fell back to the hand-placed binary — **the fallback in the launcher,
working exactly as designed**, and the reason this was an inconvenience rather than an expedition.
It also loops if you are not careful: the working binary applies, delivers a version, stands aside,
and the broken one starts. Stopping the unit while the fix was built was the way through.
## What is left
**Three machines still run a hand-placed host.** Rolling them forward is one assignment and one push
each, and the control node is worth doing last and watching.
@@ -0,0 +1,56 @@
---
status: located
opened: 2026-09-30
located-in: [mesh-host internal/apply (no removal for an archive)]
fixed-by:
amended-design:
---
# 162 — An archive cannot be undeclared, and trying stops the machine applying anything
## What was observed
*2026-09-30, unassigning the host module from the workstation to undo a delivery.*
```
holding this machine: 0 applied, and map[apply:applying "mesh-host.next": no way to remove a "archive"
0 resource(s) were applied and remain; everything was attempted, so what is not listed as failed was done.
```
**Nothing was applied at all** — not the archive, not the other forty resources that had nothing to do
with it. The machine stopped reconciling and stayed that way until the module was assigned again.
## Why it matters
Every other resource kind can be taken away. A file is removed and what was found under it is put
back; a container is stopped and removed; a unit is given back the state it was found in
([ADR 0118](../../02-DECISIONS/0118-undeclaring-gives-a-unit-back-the-state-it-was-found-in.md)). An
archive has no removal at all, so:
- **a module with an archive can never be unassigned** — the attempt fails for ever;
- **the failure takes the whole apply with it**, so the machine applies nothing else either, and one
unassignable resource is a machine frozen against every other change;
- it is silent in the mesh's terms: the push reported sent, and only the machine's own journal says
what happened.
The host module is the obvious case and not the only one. An archive is for what inlining cannot
serve — a theme, an icon set, a tree of configuration — and any module using one is in the same
position.
## What the right answer probably is, and the question in it
The other kinds answer this by remembering what they found. An archive unpacks many files into a
directory the mesh did not necessarily create, so removal has a real question in it: **remove what the
archive put there, or remove the directory?** The first needs the applier to have recorded the file
list; the second would delete whatever else lives there — and for the host's own versions directory,
that is every other delivered version.
Recording what was unpacked is the answer that matches how the rest of the host behaves, and it is
what [issue 126](../126-a-volume-path-is-not-in-the-spec-comparison/00-report.md) and ADR 0118 already
argue for elsewhere: the mesh gives back what it found.
## How a fix is checked
A module with an archive is assigned, pushed, unassigned and pushed again; what the archive put on the
machine is gone, anything that was in the directory beforehand is still there, and the apply that
removed it applied everything else in the same declaration.