Reconcile: adopt initialization's consolidated HQ as canonical, re-home this session's new work #24
@@ -0,0 +1,115 @@
|
||||
---
|
||||
topic: what runs on it
|
||||
status: accepted
|
||||
date: 2026-08-31
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
extends: 02-DECISIONS/0034-the-local-account-owns-the-mesh.md
|
||||
---
|
||||
|
||||
# 35. One implementation, several surfaces, and what that costs
|
||||
|
||||
## Context
|
||||
|
||||
The mesh is operated from a command line today. It needs to be operable from a browser and from a
|
||||
model's tools as well, and the three must not be three different systems.
|
||||
|
||||
**The pattern is already in the code and unnamed.** `board` serves HTTP by calling the same
|
||||
functions the CLI calls; it holds nothing and decides nothing. What follows makes that the rule
|
||||
rather than a property of one command.
|
||||
|
||||
**The board is a presentation layer over the control plane.** Not an application beside it holding
|
||||
a database credential — the thing that shows what the control plane knows, and asks it to do what
|
||||
a person asked for.
|
||||
|
||||
## Decision
|
||||
|
||||
**The logic lives once, in the context that owns it. A surface is an adapter with no decisions in
|
||||
it.**
|
||||
|
||||
| surface | for |
|
||||
|---|---|
|
||||
| **command line** | a person on a machine, and the recovery path below |
|
||||
| **HTTP** | the board, and anything else that speaks to the mesh over a network |
|
||||
| **model tools** | an agent asking the mesh to do something |
|
||||
|
||||
**Every surface refuses identically, because the refusal is not in the surface.** An assignment
|
||||
that cannot be satisfied is refused by the same resolution whichever way it arrived. The moment a
|
||||
surface can accept something another would reject, the mesh has two answers to one question and
|
||||
people learn which to trust.
|
||||
|
||||
**Reading and doing are both exposed.** The HTTP surface is not read-only: managing the mesh from
|
||||
a browser is the point. This takes the decision
|
||||
[ADR 0034](0034-the-local-account-owns-the-mesh.md) said had to be taken deliberately —
|
||||
**a browser login now carries authority over the mesh** — and takes it knowingly rather than
|
||||
letting it arrive with a feature.
|
||||
|
||||
**The networked surfaces authenticate through an OAuth2 identity provider.** Named by protocol
|
||||
rather than by product, like every other dependency the mesh takes — AMQP for the bus, S3 for an
|
||||
object store, OCI for the registry
|
||||
([ADR 0006](0006-the-substrate-and-the-control-plane.md)). What fills the role today is a module
|
||||
running Keycloak; what the control plane knows is that it validates a token against a provider
|
||||
speaking OAuth2, and replacing that provider is a migration rather than a redesign.
|
||||
|
||||
**The command line does not authenticate at all**: it is already behind the machine's own login,
|
||||
which is what owns the mesh (ADR 0034).
|
||||
|
||||
## What this is not: a kernel every module imports
|
||||
|
||||
**The shared library is the failure this project was started over**, and the difference has to be
|
||||
stated or it will be rebuilt. The old one is 155 files and 34,636 lines *containing code from
|
||||
every context* — work-domain logic sitting in the kernel every module imports, each piece landing
|
||||
there to avoid a cycle between two modules that both needed it.
|
||||
|
||||
**Shared surfaces are not a shared library.** What is shared here is that three adapters call the
|
||||
same functions. Those functions stay in the context that owns them — provisioning's logic in
|
||||
provisioning, identity's in identity — and no module imports another's. A surface may call many
|
||||
contexts; a context still may not reach into another's store
|
||||
([ADR 0008](0008-a-context-owns-its-store.md)).
|
||||
|
||||
The test, when something is about to be put "somewhere shared": *does this belong to a context, or
|
||||
does it only belong to the surface?* If it belongs to a context it goes there, even if two
|
||||
surfaces want it.
|
||||
|
||||
## The loop this creates, and the way out
|
||||
|
||||
**The control plane's networked surfaces will depend on a module the control plane assigns.**
|
||||
An identity provider is an ordinary module ([ADR 0031](0031-the-control-plane-authenticates-nobody.md)). When
|
||||
it is down, or being migrated, or misconfigured, the HTTP and tool surfaces cannot authenticate
|
||||
anybody — including the person trying to fix it.
|
||||
|
||||
**The command line is the way out, and it is why local ownership matters more rather than less.**
|
||||
It authenticates through nothing, needs no network, and is available on the machine to the account
|
||||
that owns the mesh. **A mesh must always be operable by somebody standing at it.**
|
||||
|
||||
So the rule: **no capability exists only behind an authenticated surface.** Anything the board can
|
||||
do, the command line can do. That is not a courtesy to CLI users; it is the recovery path, and a
|
||||
capability that exists only over HTTP is one that disappears exactly when identity does.
|
||||
|
||||
## Consequences
|
||||
|
||||
**Identity is still not substrate**, and the test still answers no: the control plane runs, applies
|
||||
declarations and reaches nodes with no identity provider in existence
|
||||
([ADR 0033](0033-the-substrate-is-a-store-and-a-broker.md)). What is unavailable without it is two
|
||||
surfaces, not the mesh.
|
||||
|
||||
**Whoever the identity provider admits has authority over the mesh.** That is now a real perimeter
|
||||
with real consequences, where before it guarded a page that only read. Who may log in, and to
|
||||
which realm, becomes a decision about the mesh rather than about an application.
|
||||
|
||||
**A surface must not grow an opinion.** The likely erosion is a validation added to the board
|
||||
because it was quicker there — and then the CLI accepts something the board rejects, or worse the
|
||||
reverse. Adapters hold no decisions.
|
||||
|
||||
**Three surfaces over one implementation is a cost paid three times if it is not one
|
||||
implementation.** The reason to write this down now is that the second surface is the cheapest
|
||||
moment to get it right, and the third is where the drift usually starts.
|
||||
|
||||
## References
|
||||
|
||||
- [ADR 0034](0034-the-local-account-owns-the-mesh.md) — the local account owns the mesh, and the
|
||||
line this record deliberately crosses
|
||||
- [ADR 0001](0001-mesh-brokers-nodes-host-agents-think.md) — the shared library this must not
|
||||
become
|
||||
- [ADR 0008](0008-a-context-owns-its-store.md) — a context owns its store, which a surface does
|
||||
not change
|
||||
@@ -105,6 +105,7 @@ python3 00-META/checks/index.py fail if stale
|
||||
- **0024** — [Model access is a provision, and a licence is a thing with a name](0024-model-access-is-a-provision.md)
|
||||
- **0026** — [The mesh has a session of its own, and it is the node session's mechanism](0026-the-mesh-has-a-session-of-its-own.md)
|
||||
- **0027** — [A provision names what the consumer is coupled to, not the role it plays](0027-a-provision-names-what-the-consumer-is-coupled-to.md)
|
||||
- **0035** — [One implementation, several surfaces, and what that costs](0035-one-implementation-several-surfaces.md)
|
||||
|
||||
### How it is built
|
||||
|
||||
|
||||
Reference in New Issue
Block a user