Files
hq/03-DESIGN/01-to-be/11-a-board.md
T
jschoubben 1111bd84d7 Establish the repo for the completed Phase 0-3 build
Settles the design repository now that the self-upgrade build is on main:
- Records the two decisions that shipped without a record — ADR 0077 (the
  controller/foundation/node vocabulary) and ADR 0078 (the store and broker are
  ordinary modules); accepts ADR 0075 and 0076, which shipped work rests on.
- Fills issue 051's amended-design and wires ADR 0078 into 07-the-foundation.
- Sweeps the repo rename (mesh-control -> mesh-controller) into the mutable docs
  now that the forge repo is renamed; updates the glossary note and repos.md.
- Fixes the six broken links from the design-doc renames, indexes the glossary,
  regenerates the decisions reading order.

Both checks (records.py, index.py) are green. Statuses stay honest: the build is
on main and lab-proven but not deployed as the production mesh, so the to-be docs
remain in-progress and the as-is layer (the hal mesh) is unchanged — graduation
to implemented + as-is belongs to deployment, not merge.

https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
2026-09-17 00:04:58 +02:00

143 lines
8.0 KiB
Markdown

---
layer: to-be
status: in-progress
code:
- mesh-controller cmd/mesh-controller/board.go
- mesh-controller cmd/mesh-controller/readable.go
updated: 2026-08-31
decisions:
- 02-DECISIONS/0008-a-context-owns-its-store.md
- 02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md
---
# A board
**A place to see the mesh.** Read from a survey of the one that exists, so what is proposed here
is a shorter list than what is there, deliberately.
## What the existing board does, and what of it belongs here
Eight sections. Four are about work and workers and are held back with the rest of that domain;
the other four are about the mesh itself.
| | what it shows | where it stands here |
|---|---|---|
| **the mesh** | every node, what each runs, what each takes from another, module versions | **everything behind it exists** — it is a reader, not a second source |
| **what is wrong** | machines not doing what they were told, machines not answering | **the page nobody had thought to ask for**, and the one a person opens first |
| **builds** | a build, its stages, its log | **everything behind it exists** — every result is kept, failures included |
| **sessions** | model sessions, their usage, and switching between accounts | [ADR 0024](../../02-DECISIONS/0024-model-access-is-a-provision.md) |
| **channels** | nodes messaging each other | the agent layer |
## The constraint that matters, and it is not a feature
**The existing board is one service that reads every context's database.** It joins nodes to
provisions to modules to sessions by querying each store directly, because that is the shortest
path to a page that shows all of them at once.
That is [ADR 0008](../../02-DECISIONS/0008-a-context-owns-its-store.md) violated by the one
component with a reason to violate it, and the cost is not hypothetical — it is the same cost the
shared library has: **a boundary nothing may cross is a boundary that can move; one thing crossing
it is enough to freeze it.** A board that reads the provisioning tables directly is a board that
breaks when provisioning changes its tables, and the change then gets weighed against the board.
**So a board reads through interfaces and holds nothing.** Everything on the mesh page above is
already answerable by asking the controller — what nodes exist, what each resolves to, what it
takes from elsewhere, which module came from which commit. A board that asks those questions is a
client. A board that queries `inventory` is a second controller with a worse contract.
**It stores nothing of its own.** No cache that can disagree, no table of "what the mesh looked
like last time". If a question is slow to answer, the answer belongs in the context that owns it,
where everything else asking gets it too.
## Where it is reachable from, which decides everything else about it
*Written 2026-08-31. It was assumed throughout and stated nowhere, which is the wrong way round
for the most consequential fact about this component.*
**The board is published on a public name.** Not reachable only over the private network — on the
internet, behind the reverse proxy, like any other published workload.
**So its login is a perimeter, not defence in depth.** A board on the overlay alone would sit
inside the boundary that [ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md) already
calls the security boundary, and a login there would guard a room whose door is inside the
building. This one faces everybody.
**Which makes the identity provider the mesh's outermost gate.** The board is a presentation layer
over the controller and the controller's networked surfaces can change the mesh
([ADR 0035](../../02-DECISIONS/0035-one-implementation-several-surfaces.md)), so **whoever that
provider admits can assign modules, from anywhere.** Said flatly because it is easy to arrive at
one reasonable step at a time and then be surprised by.
Three things follow, and none of them are the board's own design:
- **Who may log in, and how, is a decision about the mesh** rather than about an application. A
realm that lets somebody in has let them into the mesh.
- **A public name needs a public certificate**, from an authority the world trusts rather than the
mesh's own — which is why that exists at all.
- **The provider failing is not only "the board is down".** Its configuration going wrong in the
other direction — a realm that admits too much — is a mesh-wide exposure with no local symptom.
**The command line is unaffected and is the reason this is tolerable.** It authenticates through
nothing, needs no network, and answers to the machine's own login — so the mesh remains operable
by somebody standing at it whatever happens to the gate. *That is the property to protect if the
rest of this is ever traded away.*
## What it is not
- **Not the way to change things.** Reading is the whole of it to begin with. Every action the
board could offer already exists as a command, and a button that does something no command does
is a second implementation of a decision.
- **Not a dashboard of graphs.** What a person needs from a mesh is *which machine is not doing
what it was told*, and that is a list, not a chart.
## The three questions, in order
Written down because the order is the design. A person opens this when something is wrong, and a
page that led with the third would bury the first:
1. **Is anything broken?** A machine whose last declaration was refused or partly failed. It has
consequences now.
2. **Is anything not answering?** A machine not heard from. It may be new, switched off or
unreachable — **which is not the same as tried and could not**, and collapsing the two sends
somebody to debug a machine that was never sent anything.
3. **Is anything out of date?** A module behind its source, and the machines running the old one.
A plan for later rather than a problem now.
**Refused and failed stay distinct all the way to the page.** Refused means the machine is exactly
as it was and what is wrong is in what was sent; failed means it is in a state nobody declared and
what is wrong is on the machine. They are fixed in different places, so a page that said "error"
for both would send half its readers to the wrong one.
## What was built
*2026-08-31.*
**One reading, three ways of saying it.** The questions are asked once, by one function, and
answered as a person's `status`, as its JSON, and as this page. Three implementations of *which
machine is not doing what it was told* would be three chances to disagree about it — and the
disagreement would surface as two people looking at two screens arguing about which machine is
broken.
**It holds nothing and changes nothing.** Every request reads the mesh now. There is no cache to
go stale, no table of what the mesh looked like last time, and no button: every action a board
could offer already exists as a command, and one that did something no command does would be a
second implementation of a decision.
**It never touches a context's store.** That is the whole constraint above, kept: the board is a
client of the same functions the commands use, so provisioning can change its tables without the
change being weighed against a page.
**A board that cannot read the mesh says so.** An empty page says *nothing is wrong* in the one
situation where nobody can know that, so the failure is rendered instead — and it says explicitly
that it is a statement about the page rather than about the mesh.
**A machine's own words are shown, and are not markup.** They are the whole reason the page is
useful — a board that said only *failed* would send a person to ask the thing they opened the
board to avoid asking. They are also the only text on the page that nobody in this repository
wrote, which is why the escaping is a test rather than an assumption.
*Checked by giving a machine a declaration it cannot apply and requiring the page to name that
machine, say `failed` rather than `error`, and quote what the host said — then by comparing the
page's own JSON against the command's, because two answers to "which machine is broken" would be
worse than either alone.*