Files
jschoubben 9eef0bd525 Issue 107 is resolved: a declaration carries its order
Hosts first, then the controller — a build and a push each, now that the
mesh delivers the host. The host refuses a lower sequence than it kept
and drains a batch by sequence rather than arrival; the controller
numbers each send under the node's hold, inside the signed bytes.

Measured: two pushes, sequence 2 in the kept declaration, counters in
the store agree, no machine reads as behind. That last one is the
subtlety: the mesh compares the digest of what it would send against
what it did, and a number changes the bytes, so the read-only comparison
composes with the last number sent rather than a fresh one.
2026-09-30 14:13:50 +02:00

56 lines
3.2 KiB
Markdown

---
status: resolved
opened: 2026-09-23
located-in: [mesh-controller internal/link, mesh-host internal/link]
fixed-by: mesh-host PR 59 (the host refuses an older sequence and drains by it), mesh-controller PR 160 (each send is numbered under the node's hold) — measured 2026-09-30, 02-resolution.md
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?
## What has since made this safer to do (2026-09-30)
Adding a `sequence` to a declaration is adding a field, and a host refuses a declaration carrying a
field it does not know — whole. That was
[issue 087](../087-the-controller-cannot-tell-a-host-is-too-old/00-report.md), and it is resolved: the
mesh now records which host each machine reports and `status` names every machine running an older one
than another does.
So the flag day is visible before it is walked into, which it was not when this was filed. It does not
make the field free: **the oldest host in the mesh is still what the mesh may send**, and one machine of
four is behind today. A sequence that an old host refuses takes that machine out of the mesh's reach
entirely — worse than the replay it prevents, which has never been observed.