Consolidate the design layer: one place per topic
Jochen: a jungle of specs that slightly contradict or patch each other, and what matters is a working state rather than history. Both are fair and both are mine. Measured rather than assumed. 05-the-node-host and 09-the-node-lifecycle both covered enrolment, the install commands, the unit file, the launcher and reconcile -- I wrote 09 without taking anything out of 05, so the same things were said twice and could drift apart. Split by what each document IS. 05 is the component: what the host is, its parts, the declaration vocabulary, the build order, how it is verified. 09 is what happens to it: install, enrol, run, upgrade, retire. The whole "The process" section left 05, and the unit file moved to 09 where installing is described. 05 goes from 338 lines to 245 and now points at 09 rather than restating it. 09 also carried a 105-line "Resolved" section -- six mechanisms framed as "these were open and here is the answer". The content is needed; the framing is history, and history is what makes a document read as a changelog rather than a description. Renamed to what it actually is and the was-open phrasing removed. Also added 10-delivery.md, which did not exist: four accepted decisions -- 0054, 0063, 0064, 0065 -- had no design document at all, which is the specific reason the delivery picture felt scattered. It is now one document covering modules, the three edges, the core library, and how a change becomes a running thing, with a table of what each property is designed against and what must exist before it can be built.
This commit is contained in:
@@ -84,6 +84,45 @@ hosted on the mesh. Any route that needs the mesh in order to install the thing
|
||||
mesh is a circle — unusable on a first node, and unusable by whoever is repairing a mesh that is
|
||||
down, which is exactly when it is wanted.
|
||||
|
||||
### The unit it installs
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Novox Mesh node host
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/lib/nox-mesh-host/launch
|
||||
Restart=always
|
||||
RestartSec=5s
|
||||
StateDirectory=mesh-host
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
**Two lines of policy, and that is deliberate**
|
||||
([ADR 0061](../../02-DECISIONS/0061-the-host-asks-an-init-for-start-and-restart.md)). The init is
|
||||
asked to *start this at boot* and *start it again if it exits*, and nothing else. Both are
|
||||
expressible in OpenRC, runit, s6 and an Android `init.rc`, so porting this file is transcription
|
||||
rather than design.
|
||||
|
||||
**`Restart=always` and not `on-failure`**: the host restarts onto a new binary by exiting
|
||||
*cleanly*, so a supervisor that only restarts on failure would leave every upgraded node stopped,
|
||||
having successfully upgraded.
|
||||
|
||||
**What the init does not do is decide when to give up.** Counting failed starts and rolling back
|
||||
lives in the launcher, where it can be tested — `OnFailure=` in a unit file can only be read and
|
||||
hoped for, and it is the one thing that has to work on a machine where nothing else does.
|
||||
|
||||
**The package owns this file. The host never does.** It manages `service` resources, and its own
|
||||
unit is a service — the temptation is obvious and it ends with a host stopping itself half way
|
||||
through an apply, leaving a machine with nothing running to fix it. A declaration naming the
|
||||
host's own unit is **refused**, and that refusal is a test rather than a convention.
|
||||
|
||||
The line to hold: **the installation owns the host; the host owns everything else.**
|
||||
|
||||
At this point the host is running and **doing nothing**. It has no identity, so there is nobody
|
||||
to link to and nothing to apply. It answers `profile`, `inventory` and `version`, and waits.
|
||||
|
||||
@@ -485,13 +524,14 @@ every node, each one goes quiet, and the mesh reports a fleet of sleeping laptop
|
||||
|
||||
---
|
||||
|
||||
## Resolved
|
||||
## Details that are easy to get wrong
|
||||
|
||||
The items this document opened, with the reasoning, because each was open for a reason.
|
||||
Each of these has a wrong answer that looks reasonable, which is why they are written down
|
||||
rather than left to be worked out.
|
||||
|
||||
### Re-enrolling as the same node
|
||||
|
||||
**A token is issued *for* a node record, and that is where the question is answered.**
|
||||
**A token is issued *for* a node record**, and that is where a re-enrolment is decided.
|
||||
|
||||
```
|
||||
mesh-control token issue --node workstation # this machine is that node again
|
||||
@@ -539,11 +579,9 @@ whoever is looking, not a constant in the design.
|
||||
**Adoption always completes. A node with a `failed` line is a node, and it is not eligible for
|
||||
assignment until the failure is resolved.**
|
||||
|
||||
This keeps *flags inform, they do not block*
|
||||
([research 012](../../01-RESEARCH/012-the-minimum-viable-node/00-overview.md)) exactly where it
|
||||
was decided — for **conflicts**, where the mesh chose deliberately and the machine works — and
|
||||
gives **failures** the different treatment they need, because a failure is not *we chose* but
|
||||
*we could not*.
|
||||
*Flags inform, they do not block* holds for **conflicts** — where the mesh chose deliberately and
|
||||
the machine still works. A **failure** is different in kind: not *we chose* but *we could not*,
|
||||
and it gets different treatment for that reason.
|
||||
|
||||
The distinction is between **joining** and **being given work**. Refusing to join makes a
|
||||
machine in use unadoptable, which is the outcome that rule exists to prevent. Placing work on a
|
||||
@@ -580,8 +618,8 @@ keeps cataloguing.
|
||||
**`mesh-control token issue` prints it once**, to the person running it. Single-use, and it
|
||||
expires whether used or not ([ADR 0039](../../02-DECISIONS/0039-the-link-is-the-security-boundary.md)).
|
||||
|
||||
It is carried by hand — read off a screen, pasted into a terminal. That is not a gap in the
|
||||
design, it is the design: its authenticity comes from the channel it travelled, which is what
|
||||
It is carried by hand — read off a screen, pasted into a terminal. That is the design rather than
|
||||
a gap in it: its authenticity comes from the channel it travelled, which is what
|
||||
lets a node verify a mesh it has never spoken to
|
||||
([ADR 0051](../../02-DECISIONS/0051-the-enrolment-token-carries-the-mesh.md)). A token emailed,
|
||||
committed, or dropped in shared storage has lost the only property that makes it worth carrying.
|
||||
|
||||
Reference in New Issue
Block a user