Self-update works, and a delivered host is one fact short of usable #211
@@ -95,3 +95,26 @@ and then read by nothing: it does not reach the compiler, no machine is matched
|
||||
chooses between two artifacts by it. The host built here is x86-64 because the build machine is, not
|
||||
because anything in the declaration said so — correct for this mesh by coincidence. That is
|
||||
[issue 159](../159-an-artifacts-system-is-checked-and-then-ignored/00-report.md).
|
||||
|
||||
## Delivered, started, and one fact short (2026-09-30, later)
|
||||
|
||||
The loop closed. The launcher is delivered as a **file** resource rather than inside the archive, and
|
||||
that difference is the safety: a file is written atomically — temp file, then rename — so the running
|
||||
launcher keeps the inode it was started from, where an archive writes in place with truncate and would
|
||||
cut the script a running shell is reading. The manifest carries a second copy of the launcher and a
|
||||
test refuses any difference from `packaging/nox-mesh-host-launch`.
|
||||
|
||||
On the workstation, in order: the version landed, the launcher was replaced, the running host saw a
|
||||
delivered version and stood aside, and after one restart of the unit the launcher started
|
||||
`/usr/lib/nox-mesh-host/versions/637f65559d16/nox-mesh-host`. **A host the mesh compiled, published,
|
||||
delivered and started.**
|
||||
|
||||
It would then have refused the first declaration it was asked to apply. The host's Makefile links in
|
||||
two facts the mesh's toolchain does not, and one of them — the system it was built for — is read before
|
||||
anything is applied. That is
|
||||
[issue 161](../161-a-delivered-host-carries-none-of-its-link-time-facts/00-report.md), and the machine
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
status: located
|
||||
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:
|
||||
amended-design:
|
||||
---
|
||||
|
||||
# 161 — A host the mesh built carries none of the facts its Makefile stamps in
|
||||
|
||||
## What was observed
|
||||
|
||||
*2026-09-30, on the workstation, having just made the host self-updating.*
|
||||
|
||||
The mesh compiled the host, published it, delivered it and the launcher started it. It ran, read the
|
||||
machine correctly, and **would have refused the first declaration it was asked to apply.**
|
||||
|
||||
The host's own Makefile links in two facts:
|
||||
|
||||
```
|
||||
LDFLAGS := -s -w -X main.builtFor=$(SYSTEM) -X main.version=$(VERSION)
|
||||
```
|
||||
|
||||
The mesh's Go toolchain links in neither, on purpose: a toolchain accepts nothing from the module,
|
||||
because anything a module could override there it would be writing a Dockerfile to override
|
||||
([ADR 0142](../../02-DECISIONS/0142-the-mesh-delivers-its-own-components-as-binaries.md)). So a
|
||||
delivered host has `builtFor = ""` and `version = "development build"`.
|
||||
|
||||
**`builtFor` empty is the one that bites.** Before applying anything, the host asks which system it
|
||||
was built for:
|
||||
|
||||
```go
|
||||
sys, err := system.For(builtFor)
|
||||
```
|
||||
|
||||
and that answers, for an empty name:
|
||||
|
||||
```
|
||||
this host was built for "", which is not a system it knows. Built hosts are: …
|
||||
```
|
||||
|
||||
It is called before any resource is applied, so the failure is in the safe direction — the machine is
|
||||
not half-configured. It is still a host that cannot do its job, and nothing about it looks wrong: the
|
||||
unit is active, the link to the bus is up, and the log says it is hearing what the node should be.
|
||||
|
||||
Measured: after the crossover the machine logged nothing further, where the previous host had written
|
||||
a reconcile line every five minutes.
|
||||
|
||||
## Why this was found rather than reported
|
||||
|
||||
Nothing reports it. The host does not check its own stamps at start, the mesh does not ask, and the
|
||||
declaration that would fail is the same declaration that would deliver a fix — so **a machine in this
|
||||
state cannot be repaired by the mesh.** It was restored by moving the delivered versions aside and
|
||||
letting the launcher fall back to the hand-placed binary, which is the fallback working exactly as
|
||||
designed.
|
||||
|
||||
## What the records already say about half of it
|
||||
|
||||
[ADR 0142](../../02-DECISIONS/0142-the-mesh-delivers-its-own-components-as-binaries.md) settles the
|
||||
version and its answer is not implemented:
|
||||
|
||||
> A component's version comes from where it sits, not from its linker. It is unpacked into a directory
|
||||
> named for its version, so it can read its own version from its path. The stamp goes, and with it the
|
||||
> need for a build to know what it will be called.
|
||||
|
||||
That is exactly right and would also fix what the mesh reports: a delivered host would say
|
||||
`637f65559d16` rather than `development build`, and
|
||||
[issue 087](../087-the-controller-cannot-tell-a-host-is-too-old/00-report.md)'s host comparison would
|
||||
mean something for delivered hosts.
|
||||
|
||||
**The system pin has no answer yet**, and it needs one before any mesh-built host can apply anything.
|
||||
The tension is real: the target is a property of the artifact and 0142 says so, but a toolchain that
|
||||
passed it would be linking a value into a variable whose name belongs to the module — which is the
|
||||
coupling the toolchain exists to avoid. Candidates, none decided:
|
||||
|
||||
- the path carries it as well as the version, so the host reads both from where it sits, as 0142 does
|
||||
for the version;
|
||||
- the bundle carries a small file beside the binary saying what it was built for, written by the
|
||||
builder from the artifact's declaration;
|
||||
- the host stops being pinned at link time and refuses on a fact it reads from the machine instead —
|
||||
which changes what [ADR 0005](../../02-DECISIONS/0005-the-node-host.md) decided and is the biggest of
|
||||
the three.
|
||||
|
||||
## What is true in the meantime
|
||||
|
||||
Self-update works end to end and is one fact short of usable: the mesh builds the host, publishes it,
|
||||
delivers it to a machine, the running host stands aside, and the launcher starts the delivered one. The
|
||||
machine is left on its hand-placed binary until this is answered, which is one command to undo.
|
||||
|
||||
## How a fix is checked
|
||||
|
||||
A host the mesh built and delivered applies a declaration on a machine, shown by the machine's own
|
||||
reconcile line; and it reports a version that names the build it came from rather than a placeholder.
|
||||
Reference in New Issue
Block a user