diff --git a/02-DECISIONS/0031-the-control-plane-authenticates-nobody.md b/02-DECISIONS/0031-the-control-plane-authenticates-nobody.md new file mode 100644 index 0000000..850bf76 --- /dev/null +++ b/02-DECISIONS/0031-the-control-plane-authenticates-nobody.md @@ -0,0 +1,72 @@ +--- +topic: the tiers +status: accepted +date: 2026-08-31 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0006-the-substrate-and-the-control-plane.md +--- + +# 31. The control plane authenticates nobody, so identity is a module + +## Context + +[ADR 0006](0006-the-substrate-and-the-control-plane.md) left one member of the substrate +conditional, and said exactly why: + +| role | product | | +|---|---|---| +| identity provider | — | **conditional**: substrate only if the control plane delegates authentication, which is undecided | + +[`07-the-substrate.md`](../03-DESIGN/01-to-be/07-the-substrate.md) carried it as an open question — +*whether identity is the fifth* — noting it followed from a decision nobody had taken. + +**The decision is taken: the control plane does not delegate authentication.** There is no mesh +identity provider. + +**Nothing in the mesh's own machinery ever needed one.** A node proves itself with a keypair it +generated, over a broker account issued at enrolment +([ADR 0004](0004-a-node-and-how-it-joins.md)). Declarations are verified by signature. None of +that touches an identity provider, and the conditional was never about machines — it was only ever +about whether a *person* signing in to a mesh surface would be authenticated by something else. + +## Decision + +**Identity is a module**, like the mail system and the forge. It runs *on* the mesh, not *of* it +([ADR 0001](0001-mesh-brokers-nodes-host-agents-think.md)) — a provider other modules require, +which is the ordinary shape and needs nothing new to express. + +**So the substrate is three, and no longer conditional**: a relational store, a message bus, and +an image registry. Together with +[ADR 0028](0028-the-substrate-supplies-the-control-plane-and-nothing-else.md), which removed the +object store, the list is settled and every member is there for the same reason — the control +plane needs it and cannot ask itself for it. + +**A mesh that wants no identity provider runs none.** That is now expressible, and was not while +it sat in the substrate as a maybe. + +## Consequences + +**The last open question about substrate membership is closed.** Both halves of ADR 0006's test +now have an answer for every candidate, and the answer for identity is *the control plane does not +need it*. + +**It does not settle how a person signs in to a mesh surface**, and that is deliberately left +open. What is settled is that whatever answers it is not part of what must exist before the mesh +does — so it can be decided late, changed, or replaced, which is precisely what being substrate +would have prevented. + +**It becomes a real test of the module graph.** An identity provider is a module that *other +modules require* — the object store already consumes it — so it exercises the provider chain more +seriously than anything ported so far, where the provider was written alongside its consumer. + +**Ordering follows from it rather than from preference.** Anything requiring identity has to move +after it, which is a dependency the graph can state rather than something a person has to +remember. + +## References + +- [ADR 0006](0006-the-substrate-and-the-control-plane.md) — the conditional this closes +- [ADR 0028](0028-the-substrate-supplies-the-control-plane-and-nothing-else.md) — the other member + removed, and the test applied properly +- [ADR 0004](0004-a-node-and-how-it-joins.md) — how a node proves itself, which needs none of this diff --git a/02-DECISIONS/README.md b/02-DECISIONS/README.md index 6a45ec7..8077008 100644 --- a/02-DECISIONS/README.md +++ b/02-DECISIONS/README.md @@ -95,6 +95,7 @@ python3 00-META/checks/index.py fail if stale - **0028** — [The substrate supplies the control plane and nothing else](0028-the-substrate-supplies-the-control-plane-and-nothing-else.md) - **0029** — [A network is a shape, because an action cannot be undone](0029-a-network-is-a-shape-because-an-action-cannot-be-undone.md) - **0030** — [Data outlives the mesh that declared it](0030-data-outlives-the-mesh-that-declared-it.md) +- **0031** — [The control plane authenticates nobody, so identity is a module](0031-the-control-plane-authenticates-nobody.md) ### What runs on them, and how it gets there diff --git a/03-DESIGN/01-to-be/07-the-substrate.md b/03-DESIGN/01-to-be/07-the-substrate.md index 6ccf9cc..d1ff74d 100644 --- a/03-DESIGN/01-to-be/07-the-substrate.md +++ b/03-DESIGN/01-to-be/07-the-substrate.md @@ -38,7 +38,7 @@ The test, applied: | a message bus — **LavinMQ** | it reaches nodes over it ([ADR 0002](../../02-DECISIONS/0002-nodes-communicate-over-a-broker.md)) | no — it cannot grant itself a virtual host | **substrate** | | ~~an object store~~ | ~~artifacts and blobs it delivers~~ | — | **not substrate** — [ADR 0028](../../02-DECISIONS/0028-the-substrate-supplies-the-control-plane-and-nothing-else.md) | | an image registry — **the OCI registry** | images it delivers to nodes | no — it needs a repository | **substrate** | -| an identity provider | only if it delegates authentication | — | **conditional, below** | +| ~~an identity provider~~ | ~~only if it delegates authentication~~ | — | **not substrate** — it delegates to nothing ([ADR 0031](../../02-DECISIONS/0031-the-control-plane-authenticates-nobody.md)) | | ingress — **Traefik** | not to start; only to be reached by name | — it grants itself one afterwards | **not substrate** ([ADR 0007](../../02-DECISIONS/0007-connectivity.md)) | | anything else the mesh hosts | no | — | not substrate | @@ -206,7 +206,11 @@ host's vocabulary grows by one shape rather than by one resource type per substr ## Open -- **Whether identity is the fifth.** Above; it follows from a decision not yet taken. +- ~~**Whether identity is the fifth.**~~ **Closed 2026-08-31** by + [ADR 0031](../../02-DECISIONS/0031-the-control-plane-authenticates-nobody.md): the control + plane delegates authentication to nothing, so identity is an ordinary module. With the object + store gone ([ADR 0028](../../02-DECISIONS/0028-the-substrate-supplies-the-control-plane-and-nothing-else.md)) + the substrate is three, and no member is conditional. - ~~**Whether the bus must precede the control plane.**~~ **Resolved** by [ADR 0006](../../02-DECISIONS/0006-the-substrate-and-the-control-plane.md) — it must, and the question as posed here could not have answered it. This asked whether the control plane's contexts talk to