Written as one document because the five are one design. They share inputs, they must agree, and every one of them today is computed in a different place by a different module from a different copy of the same facts. The through-line is that none of the five can be answered by a machine alone, so all five are decided centrally and delivered as `file` resources. That costs no new host vocabulary and removes both remaining direct database connections from nodes -- wireguard and traefik are the only two, and both are connectivity. Three decisions fall out, all proposed: 0050 -- reachability is declared, not inferred from an address. The RFC1918 regex is wrong for carrier-grade NAT (100.64/10 tests as public, so an endpoint is written to an address nothing can reach), wrong for IPv6, and wrong for a routable address behind a closed firewall. The lab needing TEST-NET-3 to satisfy the regex is the same bug from the other side. Also kills hub election by address prefix, which fails silently and makes renumbering an outage. 0051 -- the enrolment token carries where the mesh is and how to recognise it. Closes two circles with one mechanism: verifying the mesh needed the CA, and obtaining the CA meant trusting whoever handed it over; and a node had to reach the mesh before it could resolve any mesh name. An address plus a fingerprint, carried out of band, resolves both -- and closes the CA question 0049 deferred. 0052 -- a filter rule names its source. `scope:` is declared in five manifests, is part of no rule type, and is referenced by no code, so those manifests appear to restrict ports and restrict nothing. Removed rather than implemented; the general fix is refusing unknown keys, which the host already does and manifests do not. Also corrects two claims in 0049 asserting wireguard was already handled. Research 006 says both modules still reach upward; neither is.
5.9 KiB
layer, status, code, updated, decisions
| layer | status | code | updated | decisions | |||
|---|---|---|---|---|---|---|---|
| to-be | designed | 2026-08-27 |
|
The control plane
Tier 2. The term appears seventy-nine times across this repository and was defined nowhere,
which is how-we-build §5 failing on this repository's own vocabulary.
This document defines it. It does not design the contexts inside it; those are open in research 006.
The definition
The control plane is everything that needs to know about more than one node.
That is the whole test, and it is not arbitrary — it follows from ADR 0037. The host applies and does not decide because deciding needs knowledge the machine does not have. So the line falls exactly there:
| Question | Whose |
|---|---|
| write this file, with this content, with this mode | the host — one machine |
| which nodes should run the store | the control plane — needs every node |
| is this unit running | the host — one machine |
| which peers belong in this node's overlay | the control plane — needs every node |
| what does this machine have installed | the host reports; the control plane records |
| has this node been unreachable for a week | the control plane — nobody else is watching |
A useful consequence: anything a single machine could answer alone is not the control plane's. If it needs no second node, putting it here is a mistake, and the tier rule will not catch it because the dependency direction is still correct.
What is inside it
Ten contexts and one interface, from the skeleton (research 006):
| record | the event log every other context integrates through |
| inventory | nodes, modules, assignments, versions |
| config | settings, secrets, and deriving them onto nodes |
| connectivity | overlay, resolution, exposure, filtering, certificates — specified in full in 08-connectivity.md |
| provisioning | resource grants between modules |
| delivery | source to artifact to node |
| observability | health, logs, metrics, alerts |
| identity | agents, humans, services, authorisation |
| work | tasks, workflows, runs |
| knowledge | memory, documents, retrieval |
| api | the one interface every surface speaks to |
These are contexts, not services. They are separate in the sense that matters — each owns
its own store, and they integrate through the record rather than by reading one another
(how-we-build §4). They are not separate deployables, and
research 011 records why that
constraint is load-bearing: a single surface can compose them only while there is one interface
in front of them.
What it is not
- Not the thing that changes machines. It decides; the host applies. It never reaches into a node except through the host.
- Not a surface. Tier 3 is how people and agents reach it. It has one interface; the surfaces are what speak to that interface.
- Not the substrate. It runs on tier 1 — PostgreSQL, LavinMQ, MinIO, an OCI registry (ADR 0048) — and cannot start without them, which is what makes them a lower tier.
- Not privileged on a node. It has no more access to a machine than the declaration vocabulary allows (ADR 0039).
It is also a consumer
The property that makes tier 2 unlike the others: the control plane has requirements of its own. It needs a PostgreSQL database, an AMQP virtual host, and a bucket — the same things any module needs, granted the same way.
That is the circularity the tiers exist to resolve rather than hide: the control plane cannot provision its own database, because it is not running yet. So its store is raised from the bundle the host carries, before there is a control plane to ask (ADR 0038, research 011).
Its virtual host and its bucket are not in the bundle — by the time they are wanted there is a control plane to grant them. Whether the bus must come first is open, and it turns on whether these contexts talk to each other over it.
Where it runs
On nodes, like anything else. It is not a place outside the mesh; it is modules the mesh hosts, assigned to nodes by the same mechanism as everything else.
Which raises a question this document does not answer: how many nodes run it, and what happens when the one running it is down. The broker is one per mesh by decision; whether the control plane is, and what a node does while it cannot reach it, is ADR 0036's ordinary situation seen from the other end — and it is not designed.
Open
- The contexts themselves. Ten is the skeleton's claim, not a settled list. Research 006 asks whether the record belongs here or in the substrate, and whether identity is a context or a substrate service.
- How far it may be split. One deployable today. Splitting a context out costs the single interface a surface depends on (research 011).
- How many run, and what a node does without one. Above.
- What the interface is. One interface is stated; its shape, and whether it is request, subscription or both, is not (research 011).