Reconcile: adopt initialization's consolidated HQ as canonical, re-home this session's new work #24
@@ -114,12 +114,14 @@ and it runs on no node at all.
|
||||
|
||||
Anatomy makes attractive names and poor boundaries. Name the thing the domain calls it.
|
||||
|
||||
### Group by domain, not by single function
|
||||
### Things that change together share an authority, not a package
|
||||
|
||||
A module is a purpose, not a piece of software. Four modules that together constitute "how a
|
||||
node is reachable" and cannot be assigned, versioned or replaced as one thing are four
|
||||
accidents, not four boundaries.
|
||||
[ADR 0017](../02-DECISIONS/0017-modules-outside-the-core-are-grouped-by-domain.md)
|
||||
When several modules always change together under one intent, name the **context** that decides
|
||||
for them. Do not merge them into one module: they are delivered to different nodes, and a module
|
||||
that must be assigned where half of it is unwanted is not a boundary either.
|
||||
|
||||
Coherence is a context. Delivery is a module. Relationships are edges, not folders.
|
||||
[ADR 0054](../02-DECISIONS/0054-things-that-change-together-share-an-authority.md)
|
||||
|
||||
### Contexts integrate through the record, never through a shared schema
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
status: accepted
|
||||
status: superseded
|
||||
superseded-by: 02-DECISIONS/0056-the-authority-is-the-control-plane-not-a-database.md
|
||||
date: 2026-04-02
|
||||
deciders: jochen
|
||||
reconstructed: true
|
||||
|
||||
@@ -47,9 +47,16 @@ beside `02-DECISIONS/`, holding different things.
|
||||
**If a decision is worth recording, it is worth a record. If it is not worth a record, it is
|
||||
not recorded.**
|
||||
|
||||
`02-DECISIONS` holds every decision. There is no ledger, no index file, and no central status
|
||||
of any kind. The chronological view — decisions in the order they were taken — is *generated*
|
||||
from record frontmatter, which is what the ledger was actually for.
|
||||
`02-DECISIONS` holds every decision, and **there is no ledger** — no separate document in which
|
||||
a decision is also summarised, ranked or tracked. The chronological view — decisions in the order
|
||||
they were taken — is *generated* from record frontmatter, which is what the ledger was actually
|
||||
for.
|
||||
|
||||
That generation is not this record's rule. It is
|
||||
[ADR 0022](0022-status-lives-in-frontmatter.md), which already decides repository-wide that
|
||||
status lives in frontmatter and every cross-cutting view is generated rather than written. This
|
||||
record does not restate it — 0022's own words are *prose does not restate status; one place, and
|
||||
two is one too many*, and an earlier version of this paragraph did exactly that.
|
||||
|
||||
Content that was only in the ledger was rehomed rather than dropped:
|
||||
|
||||
|
||||
@@ -55,14 +55,35 @@ mesh.
|
||||
|
||||
## Decision
|
||||
|
||||
**The enrolment token carries three things**, and it is the only thing a joining node needs:
|
||||
**The enrolment token carries four things**, and it is the only thing a joining node needs:
|
||||
|
||||
| | | |
|
||||
|---|---|---|
|
||||
| **where** | an **address**, not a name | there is no resolution yet, and this is why none is needed |
|
||||
| **who** | the fingerprint of the identity to expect | what makes the mesh provable rather than assumed |
|
||||
| **where** | the **broker's address**, not a name | a node dials the broker ([ADR 0001](0001-nodes-communicate-over-a-broker.md)); there is no resolution yet, and this is why none is needed |
|
||||
| **what it is connecting to** | the fingerprint of the **broker's** certificate | so the node reaches the mesh's bus and not something answering in its place |
|
||||
| **who it will believe** | the **control plane's** signing identity | what makes the mesh provable rather than assumed |
|
||||
| **the right to join** | the one-time secret ADR 0039 already specifies | useless once used, useless after it expires |
|
||||
|
||||
### The endpoint and the authority are two identities, not one
|
||||
|
||||
This is worth separating because collapsing it is the easy mistake, and the collapsed version
|
||||
silently fails to deliver what [ADR 0039](0039-the-link-is-the-security-boundary.md) asks for.
|
||||
|
||||
A node connects to the **broker** and takes instruction from the **control plane**, which sits
|
||||
behind it. Pinning only the broker would make the control plane's authority *transitive* — the
|
||||
node would believe a declaration because of where it arrived from. **A compromised broker could
|
||||
then forge declarations**, and since the host applies whatever the link delivers, that is the
|
||||
whole machine.
|
||||
|
||||
So the node verifies **the transport** and **each declaration** separately:
|
||||
|
||||
- the broker, by its certificate, at connect time;
|
||||
- the control plane, by a **signature on the declaration itself**, every time.
|
||||
|
||||
Then 0039's *the control plane proves it is the mesh* holds against a hostile broker rather than
|
||||
assuming a friendly one — which matters because the broker is the one component every node must
|
||||
reach and the one most exposed.
|
||||
|
||||
The token is issued by the mesh for one enrolment and **carried out of band** — by the person
|
||||
adopting the machine. That is what breaks both circles: its authenticity comes from the channel
|
||||
it travelled, not from anything the node can check afterwards.
|
||||
@@ -104,6 +125,13 @@ being a design statement and being a fact.
|
||||
- **The address in the token can go stale.** If the control plane moves, unissued tokens point
|
||||
somewhere wrong. Tokens are short-lived, which bounds it; moving the control plane is
|
||||
[`06`](../03-DESIGN/01-to-be/06-the-control-plane.md)'s undesigned territory regardless.
|
||||
- **Declarations must be signed, and that is a real requirement rather than a note.** The host
|
||||
verifies a signature before applying anything, which adds a key to what it must carry and a
|
||||
failure mode it must report legibly — *this declaration is not from the mesh I joined* is a
|
||||
different condition from *this declaration is malformed*, and they must not read alike.
|
||||
- **Rotating the control plane's signing identity is a fleet-wide operation**, because every node
|
||||
holds the previous one. That is the cost of not trusting the broker, it is accepted, and it
|
||||
needs a rollover that overlaps rather than a flag day.
|
||||
- **A rejoining node is an ordinary case, not a special one.** A node that has lost its identity
|
||||
gets a new token. There is no recovery path to design because there is no long-lived secret to
|
||||
recover.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
status: proposed
|
||||
status: accepted
|
||||
date: 2026-08-27
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
@@ -91,10 +91,16 @@ replacement keeps the observation and changes the instruction:
|
||||
|
||||
- **The instruction now matches the design.** An agent reading the constitution and an agent
|
||||
reading 0044 reach the same answer, which they currently do not.
|
||||
- **The republication is the point, not a formality.** Until the derived page is regenerated the
|
||||
mesh is still governed by the old rule, and this record has changed nothing where it matters.
|
||||
Verified by reading the rule back out of the published page
|
||||
([ADR 0035](0035-a-picture-is-read-from-what-runs.md)), not by the publish reporting success.
|
||||
- **Synced 2026-08-27**, playbook [05](../00-META/process/05-constitution-sync.md). The derived
|
||||
page carries the new rule and §4 keeps its number. **Verified by reading back**, not by the
|
||||
publish reporting success: the replacement text is present, and the old section's body — *scope
|
||||
under measurement: grouping is evidenced for reachability* — returns nothing.
|
||||
- **The sync found a drift the playbook exists to catch.** The published §4 and the source did not
|
||||
say the same thing: the source spoke of *four accidents, not four boundaries*, the published
|
||||
page of *one intent expressed four times*, and only the published page carried the scope
|
||||
caveat. Same rule, two texts, already diverging — which is the exact failure
|
||||
[ADR 0025](0025-hq-is-the-source-of-the-constitution.md) predicted and the reason the playbook
|
||||
re-publishes the whole page rather than patching a section.
|
||||
- **`context` becomes a word the constitution uses**, which raises the obvious next question —
|
||||
*which contexts are there* — and that is
|
||||
[ADR 0055](0055-the-control-plane-is-the-node-coordinating-contexts.md), not this record.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
status: proposed
|
||||
status: accepted
|
||||
date: 2026-08-27
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
status: proposed
|
||||
status: accepted
|
||||
date: 2026-08-27
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
|
||||
@@ -8,6 +8,7 @@ decisions:
|
||||
- 02-DECISIONS/0053-one-control-plane-and-no-failover.md
|
||||
- 02-DECISIONS/0030-the-repository-structure.md
|
||||
- 02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md
|
||||
- 02-DECISIONS/0055-the-control-plane-is-the-node-coordinating-contexts.md
|
||||
---
|
||||
|
||||
# The control plane
|
||||
@@ -42,22 +43,31 @@ catch it because the dependency direction is still correct.
|
||||
|
||||
## What is inside it
|
||||
|
||||
Ten contexts and one interface, from the skeleton
|
||||
([research 006](../../01-RESEARCH/006-mesh-from-scratch/skeleton.md)):
|
||||
**Seven contexts and one interface**
|
||||
([ADR 0055](../../02-DECISIONS/0055-the-control-plane-is-the-node-coordinating-contexts.md)) —
|
||||
each one earning its place by the test above rather than by being ours:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **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`](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 |
|
||||
| | | needs to know about more than one node because |
|
||||
|---|---|---|
|
||||
| **inventory** | nodes, modules, assignments, versions | that *is* the mesh-wide fact |
|
||||
| **config** | settings, secrets, and deriving them onto nodes | it derives **onto nodes** |
|
||||
| **connectivity** | overlay, resolution, exposure, filtering, certificates — **specified in full in [`08-connectivity.md`](08-connectivity.md)** | who peers with whom; which node is reachable |
|
||||
| **provisioning** | resource grants between modules | consumer and provider may be on different nodes |
|
||||
| **delivery** | source to artifact to node | it targets nodes |
|
||||
| **observability** | health, logs, metrics, alerts | *unreachable for a week* is nobody else's to notice |
|
||||
| **identity** | agents, humans, services, authorisation | credentials follow an agent's node bindings and modality |
|
||||
| **api** | the one interface every surface speaks to | — it is an interface, not a context |
|
||||
|
||||
**What is deliberately not here.** `work`, `knowledge` and `stream` are **mesh-hosted
|
||||
applications** — first-party, shipped with everything else, and running on the mesh the way
|
||||
anything else does. A task does not need to know a node exists, and *being ours does not make
|
||||
something infrastructure*. `ai` is folded into `config`: a provider licence is an ordinary grant.
|
||||
|
||||
**`record` is an open question rather than an eighth entry.** Contexts integrate through it
|
||||
([ADR 0045](../../02-DECISIONS/0045-a-context-owns-its-store.md)), which makes it load-bearing,
|
||||
and [research 006](../../01-RESEARCH/006-mesh-from-scratch/skeleton.md) leaves *where it lives*
|
||||
unresolved — putting it in the substrate risks recreating the circularity the tier design just
|
||||
removed. Listing it here would settle by naming what has not been settled by arguing.
|
||||
|
||||
**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
|
||||
@@ -118,9 +128,10 @@ every public name.
|
||||
|
||||
## 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.
|
||||
- ~~**The contexts themselves.**~~ **Decided** — seven, by
|
||||
[ADR 0055](../../02-DECISIONS/0055-the-control-plane-is-the-node-coordinating-contexts.md).
|
||||
What remains open is narrower and named there: **where the record lives**, which research 006
|
||||
leaves unresolved because the substrate is the one place it must not go.
|
||||
- **How far it may be split.** One deployable today. Splitting a context out costs the single
|
||||
interface a surface depends on
|
||||
([research 011](../../01-RESEARCH/011-the-module-graph/00-overview.md)).
|
||||
|
||||
@@ -17,14 +17,15 @@ document is written and this one's status becomes `implemented`.
|
||||
| [`05-the-node-host.md`](05-the-node-host.md) | Tier 0 — the one thing installed by hand, and the only thing that changes a machine | [ADR 0037](../../02-DECISIONS/0037-the-host-applies-it-does-not-decide.md) |
|
||||
| [`06-the-control-plane.md`](06-the-control-plane.md) | Tier 2 — what the term means, and the test for what belongs in it | [ADR 0037](../../02-DECISIONS/0037-the-host-applies-it-does-not-decide.md) |
|
||||
| [`07-the-substrate.md`](07-the-substrate.md) | Tier 1 — what the control plane consumes and cannot grant itself | [ADR 0038](../../02-DECISIONS/0038-a-node-joins-by-linking-first.md), [0048](../../02-DECISIONS/0048-the-substrate-is-named.md) |
|
||||
| [`08-connectivity.md`](08-connectivity.md) | One context in full — overlay, resolution, exposure, filtering, certificates | [ADR 0049](../../02-DECISIONS/0049-a-route-is-a-grant.md), [0050](../../02-DECISIONS/0050-reachability-is-a-property-of-the-address.md), [0051](../../02-DECISIONS/0051-the-enrolment-token-carries-the-mesh.md) |
|
||||
| [`08-connectivity.md`](08-connectivity.md) | One context in full — overlay, resolution, exposure, filtering, certificates | [ADR 0049](../../02-DECISIONS/0049-a-route-is-a-grant.md), [0050](../../02-DECISIONS/0050-reachability-is-a-property-of-the-address.md), [0051](../../02-DECISIONS/0051-the-enrolment-token-carries-the-mesh.md), [0055](../../02-DECISIONS/0055-the-control-plane-is-the-node-coordinating-contexts.md) |
|
||||
|
||||
## Not yet written
|
||||
|
||||
- **The remaining contexts.** [ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md)
|
||||
decides the decomposition; `connectivity` is the first written in full
|
||||
([`08`](08-connectivity.md)) and the others do not exist yet. The work breakdown says in what
|
||||
order they are needed.
|
||||
- **The remaining six contexts.**
|
||||
[ADR 0055](../../02-DECISIONS/0055-the-control-plane-is-the-node-coordinating-contexts.md)
|
||||
settles the list at seven; `connectivity` is the first written in full
|
||||
([`08`](08-connectivity.md)) and the other six do not exist yet. The work breakdown says in
|
||||
what order they are needed.
|
||||
- ~~**Domain grouping outside the core.**~~ **Not needed.**
|
||||
[ADR 0017](../../02-DECISIONS/0017-modules-outside-the-core-are-grouped-by-domain.md) is
|
||||
superseded by [ADR 0044](../../02-DECISIONS/0044-a-module-declares-presence-instantiation-and-exclusion.md):
|
||||
|
||||
Reference in New Issue
Block a user