Issue 107: a declaration carries no order; rescue on an enrolled node is reconcile, not apply FILE

Both from the review of the issue-104 fix: a hand-applied file on an enrolled node is
recorded as carried and would remove the foundation, and nothing on the wire orders one
declaration against another.
This commit is contained in:
2026-09-23 23:27:08 +02:00
parent 2445d80565
commit 671c2f3881
2 changed files with 53 additions and 1 deletions
+11 -1
View File
@@ -8,7 +8,7 @@ code:
- mesh-host packaging/nox-mesh-host-network.sh
- mesh-controller internal/token
- mesh-controller internal/inventory/nodes.go
updated: 2026-09-22
updated: 2026-09-23
decisions:
- 02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md
- 02-DECISIONS/0004-a-node-and-how-it-joins.md
@@ -436,6 +436,16 @@ root can already do anything it can. The bound in
[ADR 0005](../../02-DECISIONS/0005-the-node-host.md) is on what a
**remote** party may push, not on what a person at the machine may do.
**But not on a node the mesh has spoken to.** Amended 2026-09-23, after
[issue 104](../../04-ISSUES/104-reconcile-applies-a-stale-declaration-and-refuses-nothing/00-report.md):
once a controller declaration has been kept on the node, `apply FILE` is refused, whatever the
file — a hand-applied file is recorded as *carried*, which the mesh can never remove and reports
as the machine's own, and a declaration carries no order, so the host cannot tell a newer file
from an older one ([issue 107](../../04-ISSUES/107-a-declaration-carries-no-order/00-report.md)).
Rescue on an enrolled node is `reconcile`, which re-applies what the mesh last said, previewed;
`apply FILE` is for a machine before enrolment. A `--rescue` that applies a hand-written file to an
enrolled node under a confirmation is open, not decided.
This replaces the three hand-run scripts that exist today — first node, joining, rescue — with
one binary that has always been the same binary.
@@ -0,0 +1,42 @@
---
status: located
opened: 2026-09-23
located-in: [mesh-controller internal/link, mesh-host internal/link]
fixed-by:
amended-design:
---
# 107 — A declaration carries no order, so a host cannot tell an older one from a newer
## What was observed
Reviewing the fix for [issue 104](../104-reconcile-applies-a-stale-declaration-and-refuses-nothing/00-report.md),
2026-09-23. A signed declaration carries a vocabulary version, the node it is for, its mode and
its resources — and nothing that orders it against another. Its only identity is the digest of
its bytes. So a host asked to apply a file can say "this is not the one the mesh last sent"; it
cannot say "this is older".
The same absence reaches the link. The host drains a backlog of declarations and applies the
newest it received, but "newest" is decided by arrival within a batch of sixteen and a 750 ms
window: a backlog of more than sixteen pushes queued across a `converge`/`adopt` pair, or a slow
broker splitting one, applies a declaration the controller had already superseded. Not observed;
constructed from the code, and narrow — but a converged declaration applied to a node that has
since been returned to adopted is the incident of issue 104 by another door.
## Why it matters beyond this instance
Ordering is the one property a declaration needs that its signature does not give it. Every
refusal the host can make about staleness today is "not the last", which is both too strict (a
legitimately newer file is refused too) and too weak (a replay within a batch is not caught). The
controller already holds a per-node lock while it composes and records each send; the order
exists there and is thrown away at the wire.
## Open questions
- Should the signed declaration carry a per-node `sequence`, assigned under the controller's node
hold and persisted with the node, and `supersedes` — the digest of the previous send — so a host
refuses anything not strictly newer, on the link and from a file alike?
- Should genesis sign its rewritten bundle as sequence zero, so one rule covers the bundle and no
separate genesis-digest branch is needed on the host?
- Is a sequence enough, or does a mode change deserve its own marker, so a replayed converged
declaration is refused by mode as well as by order?