Files
hq/02-DECISIONS/0041-the-host-depends-on-nothing.md
T
jschoubben 5e83ac2c22 Consolidate: 65 decision records to 52
Jochen: a normal application has 3-5 ADRs, maybe 10 for a large one, and we are
at 65. Fair, and the cause is mine -- I recorded every FINDING as a decision
rather than every fork in the road.

Two merges, both cases where one decision had been split across many records
because it was taken over several days rather than at once.

0019 absorbs ten records about how this repository works: what it is and that
it is public, the folder flow, the two design layers, the issue front door,
status in frontmatter, playbooks, the naming rule, the product name. Those were
never ten decisions -- they were one, seen from ten angles as the repository
took shape.

0016 absorbs the five about the lab: a node is a virtual machine, a router is
scenery, a scenario declares the underlay, a scenario is a closed address
space, and the two scenario classes. Same pattern -- one design, split by the
order it was worked out in.

The consolidated 0019 also raises the bar for what earns a record, since that
is what produced 65: a record is warranted when there is a genuine fork -- a
direction reversed, an alternative that will be proposed again, something
contested. A finding is not a decision, and a bug is certainly not. Everything
else belongs in the design document where the reasoning is actually read.

The checker earned its place here. Deleting nine records left 13 dangling links
across the repository and it named every one, including in AGENTS.md. Nothing
was found by reading.

Remaining clusters worth the same treatment: the host (8 records), delivery
(5), modules (6), connectivity (4), substrate and control plane (4). That would
be 52 down to roughly 30.
2026-08-28 18:53:19 +02:00

4.3 KiB

status, date, deciders, reconstructed, extends
status date deciders reconstructed extends
accepted 2026-08-26 jochen false 0037-the-host-applies-it-does-not-decide.md

41. The host depends on nothing that must be installed first

Context

ADR 0019 calls tier 0 "the one binary installed by hand", and research 006 states the property the whole tier rests on: "a binary whose whole argument is that it has no dependencies".

Building it forced the question that phrase had been carrying unexamined. Everything else in the mesh is TypeScript, and how-we-build §8 says so. A TypeScript host needs a runtime present before it can run — so the thing installed by hand becomes two things, and the second must be installed by the means the host exists to replace.

Considered options

  1. TypeScript, with a runtime installed first. Simplest, and matches every other repository. Rejected: it breaks the property the tier is built on. A host that cannot run until something else has been installed by hand is not the bottom of the stack.
  2. TypeScript, bundled as a single executable. Preserves the language and produces one file. Rejected on two grounds: it carries roughly ninety megabytes of runtime to preserve a language choice, and single-executable bundling is a young feature — tier 0 is the worst place in the system to discover its edges.
  3. A statically linked binary in a language built for it. Chosen; Go.

Decision

The host is a single statically linked binary that requires nothing to be present. Copy it onto a machine and run it. That is the whole installation.

It is written in Go. The job is system-level — run commands, write files, speak to the firewall, the overlay, the service manager and the package manager — which is what Go's ecosystem is for, and it cross-compiles to every architecture the mesh might reach, including the lighter devices requirement 6 anticipates.

The second language costs less here than anywhere else it could appear, and the reason is architectural rather than convenient. ADR 0037 means the host never queries the mesh database. ADR 0039 means it only ever receives declarations. So the host shares no code with any other tier — not a client, not a schema, not the SDK. It is joined to the mesh by a message contract and nothing else.

The language boundary therefore falls exactly on an architectural boundary that already exists. A second language usually costs duplicated logic; here there is none to duplicate.

Consequences

  • how-we-build §8 needs a scope. It reads "TypeScript throughout", which was true when everything was a service or a surface. It is now scoped to those, with tier 0 named as the exception and this record as the reason. That is a constitution change, and the sync it owes is part of it.
  • Agents must write Go to work on the host. A real cost, and the one genuine argument against this. It is bounded by the host being the only thing in tier 0 — nothing else in the mesh acquires a second language because of this.
  • The dependency-direction lint the design calls for gets easier, not harder. A Go module cannot accidentally import a TypeScript control-plane client; the boundary is enforced by there being no path across it.
  • Cross-compilation replaces per-node builds. The host is built once per architecture and copied, rather than built on the machine it runs on — which is what makes "copy it and run it" true rather than nearly true.
  • Two toolchains in the lab. Scenarios that place a host need a Go build available, and the lab is TypeScript. The binary is built before the scenario runs, not inside it.
  • This is reversible at a cost that will only grow. It is being taken at the moment the first line is written, which is the cheapest point it will ever be taken.

References