Commit Graph
2 Commits
Author SHA1 Message Date
jschoubben 4a80cc1002 apply: tier 0 consumes a declaration and converges this machine
Stage 2 begins. The host stops only reporting and starts doing its one
job (ADR 0037): take an ordered list of typed resources and make the
machine match it, from a local file, with no mesh present.

What lands in this slice — the network-free vocabulary ADR 0043 names
first:
- Parse: JSON, refused WHOLE on an unknown version, type, field, a
  missing id/type/path, or a duplicate id. An older host cannot be
  handed a newer vocabulary and do half of it.
- directory and file appliers, each reading back after it writes —
  mode and owner asserted against the machine, content compared byte
  for byte. A value that did not take is a failed apply, not a success.
- store: the applied-state record, authoritative while disconnected,
  written atomically. It is what makes removal possible.
- Convergence: apply in the stated order (the host never reorders),
  record each success AFTER it works (ADR 0035), and remove what was
  applied before and is no longer declared — in reverse order, so a
  file goes before the directory that held it.
- The data-loss guard: the host removes ONLY what it created, never
  what it adopted, and a created directory that now holds data is
  refused (os.Remove, never RemoveAll) rather than deleted (ADR 0018,
  0030). created is sticky across re-applies — caught by running the
  real binary, not just the unit tests: recomputing it from disk made
  a re-applied resource look adopted and leak on the next drop.
- Addressing: a declaration for another node is refused; a host with
  no identity yet applies its bundle (the first-node path).

Not yet: sealed secrets, and the types that need the network or a
runtime (container, package, network, service, archive, user, action)
— they follow, and until then the host refuses them rather than doing
part of a declaration.

CLI: mesh-host apply [--store P] FILE.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-02 22:34:44 +02:00
jschoubben 73c010e7ef Stage 1 — the host reports what a machine is and can do
Tier 0's first slice, per novox/hq 03-DESIGN/01-to-be/05-the-node-host.md. It
applies nothing, connects to nothing, listens on nothing. 2.9 MB, static, no
dynamic dependencies: copy it onto a machine and run it is the whole install,
which is the property ADR 0041 rests on.

A capability is detected, never assumed. Every detector runs something that only
succeeds if the thing FUNCTIONS — the daemon is asked for its version, the
package database is queried, the firewall is asked to list a ruleset, which
needs the privilege as well as the tool. 04-ISSUES/007 is the fault this
prevents: a client on disk with its daemon down looks exactly like a working
runtime, and a node assigned work on that basis fails when the work arrives.

Every verdict carries the reason and the method. A capability reported absent
with no reason is the same fault in a new place: something nobody can act on.

Two bugs found by running rather than reasoning, both silent:

systemctl is-system-running exits non-zero for every state except `running` —
including `degraded`, which means units failed and the init is emphatically
there. Reading the exit code reported NO service manager on a machine whose init
it was. That is 007 in the mirror, and both directions place work wrongly. A
verdict now reads what a tool says about itself, not only how it exited.

And `mesh-host inventory --json` printed text: the standard library stops
parsing at the first non-flag argument, so the flag sat unread and the command
exited 0 having ignored what was asked. The parser now takes the subcommand off
the front, and a stray or mistyped argument is refused rather than dropped.

Detection deliberately does NOT follow ADR 0008. That rule governs applying
state, where a failed step means the machine is not what was asked for. A failed
probe is a finding — "absent, because the probe failed" — and aborting would
replace one legible absence with total ignorance of the rest.

25 tests: structure and logic with a fake runner, and the same detectors against
this machine, because a test that fakes the system under detection asserts only
that the fake behaves as expected.
2026-08-26 00:25:08 +02:00