0054 -- things that change together share an authority, not a package. The constitution instructs agents to group "how a node is reachable" into one module, citing superseded ADR 0017; ADR 0044 says there is no networking thing to install. Since the constitution is injected where work is decided, the superseded rule is the one actually steering work. The observation behind it was right -- research 005 measured that reachability is the only place modules genuinely change together -- but the conclusion was wrong: tight coupling means a shared authority, not one artifact. wireguard and traefik deploy to different node sets, so the merged module would be assigned where half is unwanted. Requires amending how-we-build and republishing the derived page. 0055 -- the control plane is the node-coordinating contexts. Three context lists were in circulation (0015 says nine, 06 says ten, the README said eight) and none was decided. Research 006 said explicitly that the change "belongs in a new record -- not written here", and the design used the list anyway. Reconciling them shows `stream` and `ai` were dropped with no reasoning at all. Applying 06's own test -- needs to know about more than one node -- gives seven contexts plus the api, with work, knowledge and stream as hosted applications and `ai` folded into config as an ordinary grant. The record defers rather than lists. The cost is stated rather than reassured away: a board composing across the boundary reads more than one interface. That was raised before as "only moves the problem up a layer", and the answer is that 0045 already requires surfaces to read interfaces rather than stores -- what changes is the count, not the kind of work. 0056 -- the authority is the control plane, not a database. Every clause of 0003 has been decided against in four separate records and it is still accepted and cited as live. The error underneath is the same category error 0054 corrects: "source of truth" named a storage location when it meant an authority, and once the store is the answer, shared schemas follow. The half that was right -- the repository defines what exists, the mesh defines what runs where -- survives untouched. Best consequence: the cache mode disappears, so a node that has not heard from the mesh is no longer indistinguishable from one that has. 0003 is left accepted until 0056 is.
132 lines
7.5 KiB
Markdown
132 lines
7.5 KiB
Markdown
---
|
||
status: proposed
|
||
date: 2026-08-27
|
||
deciders: jochen
|
||
reconstructed: false
|
||
extends: 0015-mesh-brokers-nodes-host-agents-think.md
|
||
---
|
||
|
||
# 55. The control plane is the node-coordinating contexts, and the rest are hosted
|
||
|
||
## Context
|
||
|
||
Three different context lists are in circulation and none of them was decided:
|
||
|
||
| Where | Count | Named |
|
||
|---|---|---|
|
||
| [ADR 0015](0015-mesh-brokers-nodes-host-agents-think.md), **accepted** | **nine** | mesh, agents, work, stream, delivery, knowledge, ai, observability, config |
|
||
| [`06-the-control-plane.md`](../03-DESIGN/01-to-be/06-the-control-plane.md) | **ten** + `api` | record, inventory, config, connectivity, provisioning, delivery, observability, identity, work, knowledge |
|
||
| `01-to-be/README.md` (until today) | **eight** | — |
|
||
|
||
`06` cites ADR 0015 in its own frontmatter while presenting a list that is not 0015's. And
|
||
[research 006](../01-RESEARCH/006-mesh-from-scratch/skeleton.md), which produced the ten, said
|
||
plainly what should happen next:
|
||
|
||
> This is an addition to an accepted record, so it is a **decision, not a drafting choice**. It
|
||
> belongs in a new record that extends ADR 0015 — **not written here.**
|
||
|
||
**That record was never written, and the design used the list anyway.** This is exactly what
|
||
`01-to-be`'s own rule forbids — *every statement here traces to a record; nothing arrives by
|
||
drafting* — and it is why the count could drift three ways without anybody noticing.
|
||
|
||
### What changed silently
|
||
|
||
Reconciling the two lists, the differences are not cosmetic:
|
||
|
||
| | |
|
||
|---|---|
|
||
| **added, with reasoning** | `connectivity` (research 006 Move 3; now designed in [`08`](../03-DESIGN/01-to-be/08-connectivity.md) and settled by [ADR 0049](0049-a-route-is-a-grant.md)–[0052](0052-a-filter-rule-names-its-source.md)) |
|
||
| **added, argued but open** | `record` — research 006 explicitly leaves *where the record lives* unresolved |
|
||
| **split** | 0015's `mesh` became `inventory` + `provisioning` |
|
||
| **renamed** | 0015's `agents` became `identity` |
|
||
| **dropped with no reasoning at all** | **`stream`** — threads, mentions, messages, meetings, notifications; **`ai`** — provider grants and rotation |
|
||
|
||
The last row is the finding. Two contexts holding real behaviour vanished between an accepted
|
||
record and a design document, and nothing anywhere says they were removed or where their content
|
||
went.
|
||
|
||
## Decision
|
||
|
||
**Apply `06`'s own test honestly, and it sorts the list for us.**
|
||
|
||
The test is *everything that needs to know about more than one node.* Run it:
|
||
|
||
| Context | Needs to know about more than one node? | |
|
||
|---|---|---|
|
||
| **inventory** | which nodes exist, what is assigned where | **control plane** |
|
||
| **config** | derives settings and secrets **onto nodes** | **control plane** |
|
||
| **connectivity** | who peers with whom, which node is reachable | **control plane** |
|
||
| **provisioning** | grants between modules on different nodes | **control plane** |
|
||
| **delivery** | source to artifact **to node** | **control plane** |
|
||
| **observability** | health of nodes, including *unreachable for a week* | **control plane** |
|
||
| **identity** | credentials delivered per agent's node bindings and modality ([ADR 0015](0015-mesh-brokers-nodes-host-agents-think.md)) | **control plane** |
|
||
| **work** | a task does not need to know a node exists | **hosted** |
|
||
| **knowledge** | a document does not either | **hosted** |
|
||
| **stream** | nor does a message | **hosted** |
|
||
| **ai** | a provider licence is a **grant** ([ADR 0005](0005-capabilities-are-provisioned-on-declaration.md)) and its delivery is `config`'s | **folded** |
|
||
|
||
**So: seven contexts and one interface.**
|
||
|
||
> **inventory, config, connectivity, provisioning, delivery, observability, identity** — plus
|
||
> **`api`**, the one interface every surface speaks to.
|
||
|
||
**`work`, `knowledge` and `stream` are mesh-hosted applications, not control plane.** They are
|
||
first-party, they ship with everything else, and they run on the mesh exactly the way anything
|
||
else does. Being ours does not make them infrastructure.
|
||
|
||
**`ai` is not a context.** A provider licence is a grant like a database or a bucket, and
|
||
delivering it is `config`'s existing job. 0015 already did the hard part here by removing the node
|
||
licence — *a node holds no licence; an agent holds credentials* — and what remains needs no
|
||
authority of its own.
|
||
|
||
**`record` is deferred, deliberately.** [ADR 0045](0045-a-context-owns-its-store.md) makes it
|
||
load-bearing — contexts integrate through it — and research 006 leaves *where it lives* open, on
|
||
the grounds that putting it in the substrate risks recreating the circularity the tier design just
|
||
removed. Naming it a context here would settle by listing what has not been settled by arguing.
|
||
**Seven is the decided count; the record is an eighth question, not an eighth entry.**
|
||
|
||
## The cost, stated plainly
|
||
|
||
**A surface composing across this boundary now reads more than one interface.** The board shows
|
||
nodes *and* tasks *and* documents; under this decision that is the control plane's `api` plus
|
||
work's and knowledge's.
|
||
|
||
This was raised as an objection before — *that only moves the problem up a layer* — and it
|
||
deserves an honest answer rather than a reassurance. The answer is that
|
||
[ADR 0045](0045-a-context-owns-its-store.md) already requires it: a surface reads **interfaces,
|
||
never stores**, so a board was always going to compose rather than join. What this decision
|
||
changes is the *number* of interfaces, not the kind of work. And `06`'s constraint — a single
|
||
surface can compose contexts only while one interface sits in front of them — was already written
|
||
about the control plane's contexts, and still holds for the seven.
|
||
|
||
**The alternative is available and should be named:** keep all ten under tier 2 and accept that
|
||
*control plane* means *everything first-party*, not *everything node-coordinating*. Rejected
|
||
because the node-coordinating test is doing real work elsewhere — it is what justified
|
||
`connectivity`, and it is the same test that defines the substrate. A definition that sorts
|
||
cleanly in one place and is waved through in another is not a definition.
|
||
|
||
## Consequences
|
||
|
||
- **Three lists become one, and it is a decision rather than a draft.** `06` and the to-be README
|
||
are corrected to seven, and `06`'s frontmatter stops citing a record it contradicts.
|
||
- **`stream` is reinstated, as a hosted application.** It was dropped by accident; this puts it
|
||
somewhere on purpose. Its content — threads, notifications, meetings — is real and has to live
|
||
somewhere nameable.
|
||
- **Tier 4 gains its first named residents.** Until now the tier existed with nothing in it, which
|
||
is part of why contexts drifted upward into tier 2 unopposed.
|
||
- **The eight-nine-ten drift had no mechanism that would have caught it**, and neither does the
|
||
next one. A design document cites decisions in its frontmatter and nothing checks that what it
|
||
says matches what they say.
|
||
- **Splitting `mesh` into `inventory` and `provisioning` is inherited without fresh argument.**
|
||
It is right under [ADR 0045](0045-a-context-owns-its-store.md) — they would own separate
|
||
stores — but this record adopts it from research 006 rather than re-deriving it, and that is
|
||
worth saying rather than implying it was examined.
|
||
|
||
## References
|
||
|
||
- [ADR 0015](0015-mesh-brokers-nodes-host-agents-think.md) — the nine this extends.
|
||
- [Research 006](../01-RESEARCH/006-mesh-from-scratch/skeleton.md) — the ten, and the instruction
|
||
to write this record.
|
||
- [ADR 0045](0045-a-context-owns-its-store.md) — why the boundary costs what it costs.
|
||
- [`06-the-control-plane.md`](../03-DESIGN/01-to-be/06-the-control-plane.md) — the document this corrects.
|