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:
@@ -116,112 +116,19 @@ work: a node is a node, and what varies between them is here rather than in the
|
||||
What this machine *is* — its identity, what it holds, what it has applied. Reported upward over
|
||||
the link; never asked downward.
|
||||
|
||||
## The process
|
||||
## What it is not
|
||||
|
||||
[ADR 0057](../../02-DECISIONS/0057-the-host-is-a-root-service-installed-as-a-package.md).
|
||||
- It does not decide anything that needs another node.
|
||||
- It never queries the mesh database.
|
||||
- It has no listening surface.
|
||||
- **It does not manage its own unit.** It manages `service` resources and its own unit is one —
|
||||
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. The installation owns the host; the host owns
|
||||
everything else.
|
||||
|
||||
**One `root` service on every node, plus command-line entry points for a person.** A machine
|
||||
without one is not a node ([ADR 0036](../../02-DECISIONS/0036-a-node-is-a-managed-machine.md)) —
|
||||
the host is what makes a machine managed, so there is no agentless node and no partial mode.
|
||||
|
||||
Root because there is no useful unprivileged subset: it writes under `/etc`, installs packages,
|
||||
manages units and runs containers.
|
||||
|
||||
### Installing it
|
||||
|
||||
Two steps, and there is nothing else:
|
||||
|
||||
```
|
||||
# 1 — put the host on the machine, in that machine's own idiom
|
||||
apk add nox-mesh-host && rc-update add nox-mesh-host && rc-service nox-mesh-host start
|
||||
pacman -S nox-mesh-host && systemctl enable --now nox-mesh-host
|
||||
|
||||
# 2 — hand it the mesh. The same on every machine.
|
||||
nox-mesh-host enrol --token <one-time token>
|
||||
```
|
||||
|
||||
**Step 1 differs per system and step 2 never does**, which is the shape of
|
||||
[ADR 0060](../../02-DECISIONS/0060-the-host-is-built-per-operating-system.md): the package
|
||||
manager and the init file are the system's, and everything after them is the mesh's.
|
||||
|
||||
The token carries the broker's address, the fingerprint to expect, and the right to join
|
||||
([ADR 0051](../../02-DECISIONS/0051-the-enrolment-token-carries-the-mesh.md)). After that the
|
||||
node is in the mesh and takes declarations like every other one.
|
||||
|
||||
**On a machine with no package repository, or no mesh to serve one:**
|
||||
|
||||
```
|
||||
curl -fsSL https://<release>/mesh-host-<version>-x86_64.tar.gz | tar -xz -C /usr/local/bin
|
||||
```
|
||||
|
||||
That path must never acquire a dependency. **The mesh's package repository is hosted on the
|
||||
mesh**, so any installation route that needs the mesh is a circle — a first node cannot use it,
|
||||
and neither can anyone repairing a mesh that is down.
|
||||
|
||||
### The unit
|
||||
|
||||
```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.**
|
||||
|
||||
### What it does while running
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| holds the **link** | one outbound connection, node-initiated, nothing listening ([ADR 0039](../../02-DECISIONS/0039-the-link-is-the-security-boundary.md)) |
|
||||
| **applies** what arrives | declarations of known shape, in the order given |
|
||||
| **reads back and reports** | what it did, and what it now owns |
|
||||
| **reconciles** | on start, on a declaration, on a timer, and on reconnect |
|
||||
|
||||
**The timer is the one that is easy to leave out.** Without it, a machine that drifted — a
|
||||
person edited a file, a package upgrade replaced a config — stays drifted until somebody happens
|
||||
to change a declaration. `owned` would then report what the host *applied* rather than what is
|
||||
*there*, which is [ADR 0035](../../02-DECISIONS/0035-a-picture-is-read-from-what-runs.md)
|
||||
violated by omission. **Ten minutes, configurable.**
|
||||
|
||||
**Disconnected is a working state, not a degraded one.** The host keeps reconciling against its
|
||||
own store, so a laptop shut for a week comes back and reconciles rather than coming back and
|
||||
asking what it is. That is
|
||||
[ADR 0036](../../02-DECISIONS/0036-a-node-is-a-managed-machine.md) made operational.
|
||||
|
||||
### Upgrading it
|
||||
|
||||
**By the package manager, not by the mesh.** A running process that replaces its own binary and
|
||||
restarts part-way through an apply is the self-management problem again. Recorded as a limit:
|
||||
a fleet-wide host upgrade is not currently a mesh operation.
|
||||
**How it is installed, enrolled, run, upgraded and retired is
|
||||
[`09-the-node-lifecycle.md`](09-the-node-lifecycle.md)**, in full and in one place. This document
|
||||
is the component; that one is what happens to it.
|
||||
|
||||
## Where a declaration comes from
|
||||
|
||||
|
||||
Reference in New Issue
Block a user