Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ffb7eec55 | ||
|
|
7499f1e50c | ||
|
|
214b486a50 | ||
|
|
90b44a48df | ||
|
|
860331dc37 | ||
|
|
37b46d5349 | ||
|
|
16855ade02 | ||
|
|
8dd566c0aa | ||
|
|
a98ee0f529 | ||
|
|
ad4a5ea004 | ||
|
|
0cf1ad5dad | ||
|
|
69a002fce3 | ||
|
|
16a1a52cd8 | ||
|
|
af170e3a67 | ||
|
|
6c2d5f5913 |
@@ -75,6 +75,18 @@ another — and a mesh you cannot name precisely is a mesh two people describe d
|
|||||||
is a role you occupy, and the two meet where a seat delivers a provision: occupying the seat is
|
is a role you occupy, and the two meet where a seat delivers a provision: occupying the seat is
|
||||||
what makes a module *the* provider of it.
|
what makes a module *the* provider of it.
|
||||||
|
|
||||||
|
## The surfaces
|
||||||
|
|
||||||
|
- **console** — the module (`mesh-console`) that puts the mesh's tools in front of whoever is on a
|
||||||
|
machine: an MCP endpoint on the machine's loopback for an agent, the same endpoint for a person. It
|
||||||
|
is assigned like any module, holds a credential the mesh minted, and calls tools under a grant its
|
||||||
|
manifest declares (`invokes`). Loopback is the authority boundary: whoever is on the machine owns the
|
||||||
|
mesh there ([ADR 0152](../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md)).
|
||||||
|
Not "the tool bridge", "the brain" or "the MCP server" — those name the predecessor's program or a
|
||||||
|
protocol, and the console is a module.
|
||||||
|
- **invokes** — the manifest word for the tools a module calls, `<module>.<tool>` each or `*` for
|
||||||
|
every one. A grant on the publish side and nothing else; a module that declares none calls nothing.
|
||||||
|
|
||||||
## How this page is kept
|
## How this page is kept
|
||||||
|
|
||||||
A new name for an existing thing lands here first, in the same change that introduces it in code. A
|
A new name for an existing thing lands here first, in the same change that introduces it in code. A
|
||||||
|
|||||||
@@ -72,6 +72,12 @@ answers from it. Nothing flows back: this repository is public, the mesh is not,
|
|||||||
would be how installation-specific detail arrives into documents that must not carry it
|
would be how installation-specific detail arrives into documents that must not carry it
|
||||||
([`README.md`](../README.md)).
|
([`README.md`](../README.md)).
|
||||||
|
|
||||||
|
> **The mechanism changed — 2026-09-30, by ADR 0153.** What stands: read where it is written, no copy,
|
||||||
|
> one-way. What moved: the reader is a module the mesh assigns (`records`, a checkout at a commit every
|
||||||
|
> answer names) rather than the agent session of design 15, which is not built; and "the search consults
|
||||||
|
> the agent" has no store to consult since the cut-over — the console's tool list is where the record
|
||||||
|
> appears beside everything else. [ADR 0153](0153-the-record-is-read-by-a-module-and-the-console-lists-it.md).
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
**This repository stops being a fourth knowledge system, properly.** The original objection was
|
**This repository stops being a fourth knowledge system, properly.** The original objection was
|
||||||
|
|||||||
@@ -47,6 +47,14 @@ Anything with the control plane in reach can ask any module anything it serves.
|
|||||||
harder: nothing outside the control plane can, and the control plane's connection is one more
|
harder: nothing outside the control plane can, and the control plane's connection is one more
|
||||||
thing on the path of every question — a cost accepted for the audit it buys.
|
thing on the path of every question — a cost accepted for the audit it buys.
|
||||||
|
|
||||||
|
> **The mechanism changed — 2026-09-30, by ADR 0152.** What stands: `ask` on the control plane, and
|
||||||
|
> that every call passes an account whose permission list says what it may ask. What moved: "nothing
|
||||||
|
> outside the control plane can" stopped being true when a person's account gained a publish grant per
|
||||||
|
> tool (design 25 §7, 2026-09-28), and [ADR 0152](0152-the-operators-surface-is-a-module-the-console.md)
|
||||||
|
> takes the first option above for a module as well — a manifest declares `invokes`, and the bus grants
|
||||||
|
> exactly that publish side. The audit the second option bought is the bus's permission list, which
|
||||||
|
> derives both.
|
||||||
|
|
||||||
## How it is checked
|
## How it is checked
|
||||||
|
|
||||||
A tools-only bed asks a served tool through the control plane and asserts an answer arrived —
|
A tools-only bed asks a served tool through the control plane and asserts an answer arrived —
|
||||||
|
|||||||
@@ -150,6 +150,16 @@ closed by this record, only answered by it.
|
|||||||
resolvable inside the mesh — holds unchanged and by the same means the machine already uses.
|
resolvable inside the mesh — holds unchanged and by the same means the machine already uses.
|
||||||
- **Issue 110 stops being a container-DNS inconvenience and becomes a prerequisite** for the mesh not
|
- **Issue 110 stops being a container-DNS inconvenience and becomes a prerequisite** for the mesh not
|
||||||
restarting itself whenever it learns a name.
|
restarting itself whenever it learns a name.
|
||||||
|
- **A container that names a resolver of its own has opted out of the machine's**, and the copy this
|
||||||
|
record removes was the only reason such a container could reach anything by a mesh name.
|
||||||
|
|
||||||
|
> **Progressive insight — 2026-09-30, the afternoon this landed. Found the hard way.** The mail
|
||||||
|
> system's admin, behind Mailu's own resolver, lost its database the moment the copy went
|
||||||
|
> ([issue 171](../04-ISSUES/171-a-modules-own-resolver-knows-no-mesh-name/00-report.md)). A `dns` on
|
||||||
|
> a container is a decision about whether mesh names exist inside it, not a preference; the module
|
||||||
|
> was corrected, and whether the controller should refuse the contradiction is that issue's open
|
||||||
|
> question.
|
||||||
|
|
||||||
- **A container started by hand gets the mesh's names too**, where before only declared containers did.
|
- **A container started by hand gets the mesh's names too**, where before only declared containers did.
|
||||||
Design 08 drew that boundary deliberately, on the grounds that reaching into every container is what
|
Design 08 drew that boundary deliberately, on the grounds that reaching into every container is what
|
||||||
a nameserver would be for. This record accepts that consequence rather than working around it: a
|
a nameserver would be for. This record accepts that consequence rather than working around it: a
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
---
|
||||||
|
topic: what runs on it
|
||||||
|
status: accepted
|
||||||
|
date: 2026-09-30
|
||||||
|
deciders: jochen
|
||||||
|
reconstructed: false
|
||||||
|
extends: 02-DECISIONS/0095-the-control-plane-is-the-way-to-ask-a-module.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 152. The operator's surface is a module the mesh assigns: the console
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
**Since the bus moved, nobody can ask the mesh anything without opening a shell on a machine.** Every
|
||||||
|
tool call an operator's assistant makes fails, on every machine including the one the operator sits
|
||||||
|
at, with *AMQP not connected*
|
||||||
|
([issue 147](../04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md)).
|
||||||
|
The program answering is the predecessor's tool server, started on the workstation by hand, with the
|
||||||
|
predecessor's broker address written into the assistant's own configuration. It has no manifest, no
|
||||||
|
assignment, no account on the bus, and the mesh has never known it exists. Nothing regressed: the
|
||||||
|
mesh removed a transport that a program outside the mesh still dials.
|
||||||
|
|
||||||
|
**The mesh has a tool model, and it works.** A module serves each tool on its own subject and its
|
||||||
|
account may serve nothing else ([ADR 0047](0047-a-module-runs-its-code-as-its-own-process-with-its-own-account.md));
|
||||||
|
a person is issued an account whose only permission is to publish the tool subjects named at issue
|
||||||
|
([25 — The bus on NATS](../03-DESIGN/01-to-be/25-the-bus-on-nats.md) §7); a client on the
|
||||||
|
runtime repository's main branch speaks that account as a command line and as an MCP server. Measured
|
||||||
|
on the live mesh on 2026-09-28: a call to the forge's `gitea_list_repos` answered with real
|
||||||
|
repositories; the tool list came back empty, because it asks the catalogue for a tool nothing serves
|
||||||
|
([ADR 0132](0132-a-seat-carries-the-tools-its-holder-must-serve.md)).
|
||||||
|
|
||||||
|
**Two records have already said where the surface belongs.** ADR 0132's consequences: *the MCP
|
||||||
|
surface belongs inside the mesh — a module the mesh assigns to the machine where the agent sits, with
|
||||||
|
a credential the mesh minted and authority derived from what it may call, not a program started by
|
||||||
|
hand with a credential printed to a terminal.* Design [33](../03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md)
|
||||||
|
§6 says the same. Neither is a decision about the surface: 0132 decided where a seat's tools live,
|
||||||
|
and named the surface in passing.
|
||||||
|
|
||||||
|
**And one record says the opposite, in the letter.** [ADR 0095](0095-the-control-plane-is-the-way-to-ask-a-module.md)
|
||||||
|
made the control plane *the* way to ask a module, deferred "calling is a grant" until something asked
|
||||||
|
for it, and recorded that *nothing outside the control plane can*. A person's account (design 25 §7,
|
||||||
|
built 2026-09-28) is exactly that grant, minted for a person. So 0095's exclusivity has already been
|
||||||
|
widened once without a record saying so; a module that calls tools widens it a second time, and this
|
||||||
|
record is where that is said.
|
||||||
|
|
||||||
|
**What a module's account may do today, counted from the composition** (`internal/broker`): publish
|
||||||
|
its own events, publish the accept subjects of seats it uses, subscribe its own tools and what it
|
||||||
|
consumes. No module principal may publish another module's tool subject. Of 72 modules in the
|
||||||
|
catalogue, 45 serve tools and 0 may call one.
|
||||||
|
|
||||||
|
The question the work order asks before any code: **does the mesh grow its own operator surface, or is
|
||||||
|
the surface an ordinary module that happens to serve tools?**
|
||||||
|
|
||||||
|
## Considered Options
|
||||||
|
|
||||||
|
**1. The control plane serves the agent protocol itself** — a listener on the controller, or a verb
|
||||||
|
its binary runs. Rejected. It puts a surface for tools the control plane does not implement on the one
|
||||||
|
component that must stay answerable while it is itself being replaced, which is the reason 0132
|
||||||
|
rejected the control plane as the answer to discovery. A person on a workstation would reach it over
|
||||||
|
the network, and the networked surface [ADR 0035](0035-one-implementation-several-surfaces.md)
|
||||||
|
reserves for that authenticates through an OAuth2 provider that is not configured — so the controller's
|
||||||
|
`api` verb correctly serves nothing today, and this option would either wait for it or bypass it.
|
||||||
|
|
||||||
|
**2. A program a person installs and starts by hand with a printed credential** — what exists on the
|
||||||
|
runtime repository's main. Rejected as the end state. It is outside the mesh in every way issue 147
|
||||||
|
names: no assignment, no declaration, no seat, no check that it reaches anything, revoked only by a
|
||||||
|
person remembering to. It is the predecessor's arrangement one bus later, and it fails the same way
|
||||||
|
the next time an address moves. It stays as the recovery path, the way the command line does
|
||||||
|
(ADR 0035): a credential from `operator issue` and the `mesh` client work with no console assigned.
|
||||||
|
|
||||||
|
**3. An ordinary module, assigned to the machine the person sits at, holding a credential the mesh
|
||||||
|
minted, serving the mesh's tools on that machine's loopback.** Chosen.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**The console is a module.** `mesh-console` is built by the mesh, registered like any module,
|
||||||
|
assigned to a machine, and given a bus credential sealed to that machine. It serves the mesh's tools to
|
||||||
|
whoever is on that machine: to an agent over MCP, and to a person through the same endpoint. Assigning
|
||||||
|
it to a machine is what makes the mesh reachable from there; unassigning it revokes that, at the next
|
||||||
|
composition, with nothing on the machine to remember to remove.
|
||||||
|
|
||||||
|
**A grant to call is a manifest word: `invokes`.** A module declares the tools it calls, each as
|
||||||
|
`<module>.<tool>`, or the single entry `*` for every tool on the mesh. The bus grants exactly that
|
||||||
|
publish side and nothing beside it — no event, no subscription, no seat. This is ADR 0095's deferred
|
||||||
|
first option, taken now that a consumer asks for it; a person's account already has this shape, and
|
||||||
|
the same composition derives both. The control plane's `ask` stands, and 0095's audit point with it:
|
||||||
|
every call still passes one account whose permission list says what it may ask.
|
||||||
|
|
||||||
|
**Authority is the machine's login.** The console listens on the machine's loopback only, declared
|
||||||
|
`from: machine`, so whoever can open a socket on the machine is whoever owns the machine, and *the
|
||||||
|
account that installed the host owns the mesh on that node*
|
||||||
|
([ADR 0034](0034-the-local-account-owns-the-mesh.md)). Anything on a machine may call anything on it,
|
||||||
|
and that is the whole of local ([ADR 0144](0144-anything-on-a-machine-may-call-anything-on-it.md)).
|
||||||
|
The mesh knows no person: what the audit sees is which console asked, under the account
|
||||||
|
`<node>.mesh-console`. How a person's identity reaches a session is the question design 15 leaves
|
||||||
|
open, and this record does not close it.
|
||||||
|
|
||||||
|
**What the console lists is asked of the modules.** Design 33 §5: a module's own tools are answered by
|
||||||
|
the module, from the code that defines them. The tool runtime therefore answers one reserved verb for
|
||||||
|
every module it serves — `tools`, the module's tool names, descriptions and argument schemas — and the
|
||||||
|
console assembles its list by asking the catalogue which modules the mesh holds and each module what
|
||||||
|
it answers. A module that is not running is absent from the list and says so; a tool an agent already
|
||||||
|
knows the name of can be called whether or not it was listed. A module may not name a tool of its own
|
||||||
|
`tools`; the runtime refuses the collision at load rather than letting one shadow the other. A
|
||||||
|
role's tools, and the mesh's own verbs, join the list when the `mesh-controller` seat serves them
|
||||||
|
(design 33 §1, third family) — the console reads whatever the mesh can say about itself, and grows as
|
||||||
|
that does.
|
||||||
|
|
||||||
|
**The console holds one credential and one grant: `*`.** It is the operator's surface on a machine the
|
||||||
|
operator owns; narrowing what it may call is a setting on its assignment, which
|
||||||
|
[ADR 0046](0046-a-module-configuration-is-its-assignments-not-its-manifest.md) already provides for
|
||||||
|
and nothing here builds.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **The way a person drives the mesh is inside the mesh.** It is declared, delivered, replaced and
|
||||||
|
revoked by the same machinery as everything else, and `status` says whether the machine carrying it
|
||||||
|
has applied. Issue 147's shape — a surface kept alive by an address in a file — cannot recur, because
|
||||||
|
there is no file: the console's credential names the bus the mesh is on, and moves when it does.
|
||||||
|
- **A module may now call tools, which ADR 0095 had reserved to the control plane.** The grant is
|
||||||
|
explicit, per tool or `*`, and derived by the same composition that grants everything else. A module
|
||||||
|
that declares no `invokes` gains nothing. What got harder: a manifest reviewer has one more field to
|
||||||
|
read for authority, and `*` in it deserves the reader's attention every time.
|
||||||
|
- **A new manifest word ships one release before any manifest uses it**, and must reach both parsers:
|
||||||
|
the build machine's and the running controller's. The console's manifest cannot be registered until
|
||||||
|
the controller and the builder that packages it have been rebuilt with the word.
|
||||||
|
- **Discovery costs a fan-out per list.** One request per module the mesh holds, answered at once by
|
||||||
|
the bus for every module nothing serves, so the cost is bounded by the modules that are up. The list
|
||||||
|
is cached briefly in the console; a module assigned a moment ago appears at the next refresh.
|
||||||
|
- **Every tool runtime must be rebuilt once** to answer `tools`. Until a module is, it is callable and
|
||||||
|
not listed, and the console says which modules did not answer.
|
||||||
|
- **The mesh's own verbs are not in the console yet.** `status`, `push`, `assign` are the
|
||||||
|
`mesh-controller` seat's tools under 0132, and the three prerequisites 0132 names are still not in
|
||||||
|
place. A person asking what a node runs still opens a shell for that question, and that gap is design
|
||||||
|
33's to close, not this record's — recorded here so nobody reads the console as the whole of 147.
|
||||||
|
- **The person's credential is not retired.** `operator issue` and the `mesh` client remain the path
|
||||||
|
when no console is assigned, and the path an operator uses to bring a mesh up far enough to assign
|
||||||
|
one.
|
||||||
|
|
||||||
|
## How this is checked
|
||||||
|
|
||||||
|
| Rule | Checked by |
|
||||||
|
|---|---|
|
||||||
|
| A module's `invokes` becomes exactly that publish grant, and nothing else | the bus user composition test: a module invoking `shop.price` may publish that subject and no other tool's; `*` may publish every tool subject; neither may publish an event or subscribe anything it did not consume |
|
||||||
|
| A malformed `invokes` entry is refused at registration | a parser test: an entry naming no tool is a problem named in the manifest's words |
|
||||||
|
| The runtime answers `tools` for every module it serves | the runtime's test: a module registering two tools answers three names, and a module naming one of its own `tools` is refused at load |
|
||||||
|
| The console's list is what the modules answer | the client's test against a real bus: two modules up, a third the catalogue holds and nothing serves, and the list carries the two and names the third as not answering |
|
||||||
|
| A call from the console reaches a module over the bus | the same test, and the live mesh: the console assigned to a workstation answers `tools/list` on its loopback and a call to the forge returns repositories |
|
||||||
|
| The console listens on loopback and nowhere else | its manifest declares `from: machine`, and the filter composed for the machine opens nothing for it |
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [issue 147](../04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md) — the surface outside the mesh
|
||||||
|
- [ADR 0095](0095-the-control-plane-is-the-way-to-ask-a-module.md) — extended: a grant to call, for a module as for a person
|
||||||
|
- [ADR 0132](0132-a-seat-carries-the-tools-its-holder-must-serve.md) — where a seat's tools live, and the sentence that named the surface
|
||||||
|
- [ADR 0035](0035-one-implementation-several-surfaces.md) — three surfaces over one implementation
|
||||||
|
- [ADR 0034](0034-the-local-account-owns-the-mesh.md), [ADR 0144](0144-anything-on-a-machine-may-call-anything-on-it.md) — why loopback is the authority boundary
|
||||||
|
- [33 — The tools the mesh answers](../03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md) §5, §6 — discovery, and what serves it to an agent
|
||||||
|
- [34 — The console](../03-DESIGN/01-to-be/34-the-console.md) — the design this record authorises
|
||||||
|
- mesh-tools `src/client.ts`, `src/mcp.ts`, `src/mesh.ts` — the client this makes a module of
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
---
|
||||||
|
topic: how we work
|
||||||
|
status: accepted
|
||||||
|
date: 2026-09-30
|
||||||
|
deciders: jochen
|
||||||
|
reconstructed: false
|
||||||
|
extends: 02-DECISIONS/0025-the-design-record-is-read-not-copied.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 153. The record is read by a module the mesh assigns, and the console lists it
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
[ADR 0025](0025-the-design-record-is-read-not-copied.md) decided that this repository is **read where
|
||||||
|
it is written, never copied to be found**: an agent reads it directly, and a search of the mesh's
|
||||||
|
memory consults that agent so its answers appear beside ordinary results. It named the check that
|
||||||
|
closes [issue 006](../04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md): search
|
||||||
|
for a phrase that appears only in a design document here, and get it back. It gated the build on an
|
||||||
|
agent that did not exist — the mesh session of
|
||||||
|
[15 — The agent session](../03-DESIGN/01-to-be/15-the-agent-session.md) — and on a search that
|
||||||
|
does not exist either, now: the knowledge base 0025 meant was the predecessor's, and since the
|
||||||
|
cut-over nothing reaches it ([issue 147](../04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md)).
|
||||||
|
|
||||||
|
**So the two halves of 0025 have no home.** There is no store to be "beside", and no session to be
|
||||||
|
the reader. What the mesh has instead, since today: a tool model in which every module answers what
|
||||||
|
it serves, and a console on the machine a person sits at that lists every tool the running modules
|
||||||
|
answer ([ADR 0152](0152-the-operators-surface-is-a-module-the-console.md)). An agent holding the
|
||||||
|
console does not search a store; it reads a tool list and calls what fits the question.
|
||||||
|
|
||||||
|
**What 0025 could not tolerate was a derived copy** — the enforced copy winning while the reasoned one
|
||||||
|
quietly stops being true. It rejected a sync for that reason and for no other. A git checkout is not a
|
||||||
|
derived copy: it is the same bytes at a commit the answer names, and the only way it can differ from
|
||||||
|
the source is by lagging behind it, which is measurable and stated. 0025's own words allow it —
|
||||||
|
*retrieval is an agent reading this repository, not a copy living in a second store* — and the
|
||||||
|
transformation that makes a copy dangerous is exactly what a checkout does not do.
|
||||||
|
|
||||||
|
## Considered Options
|
||||||
|
|
||||||
|
**1. Wait for the mesh session.** Rejected. Design 15 is `designed` with nothing built, its model
|
||||||
|
access is a provisions question with no consumer identity yet, and 006 has waited since 2026-08-23.
|
||||||
|
A record whose check cannot run is a rule enforced by nothing.
|
||||||
|
|
||||||
|
**2. The console reads the repository itself.** Rejected. The console holds nothing and decides
|
||||||
|
nothing (ADR 0152, ADR 0035); a reader inside it would be a second implementation of a thing that
|
||||||
|
should be one module, unavailable to a person's client and to any other module.
|
||||||
|
|
||||||
|
**3. A module that keeps a checkout of the repository and answers questions about it, listed by the
|
||||||
|
console like any tool.** Chosen.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**The reader is a module: `records`.** It requires the `git` provision — the forge — clones the
|
||||||
|
repository its settings name, keeps the checkout current on every merge the forge announces and on a
|
||||||
|
timer, and answers over the bus: where a phrase appears as written (document, line, nearest heading),
|
||||||
|
one document whole, what a folder holds, and where the checkout stands — always with the commit it
|
||||||
|
read. Nothing is indexed, ranked or summarised: a design record is found by its own words, and a
|
||||||
|
reader deciding which words matter would be a second opinion about somebody else's document.
|
||||||
|
|
||||||
|
**The repository is a setting, not a manifest field.** The module names no mesh
|
||||||
|
([ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md)): which repository it reads is
|
||||||
|
the assignment's business, and an installation that keeps its record elsewhere sets that. Until a
|
||||||
|
repository is set it serves no tools and says why. Public repositories only; it asks for no
|
||||||
|
credential, because a secret it did not need would be one more thing to seal.
|
||||||
|
|
||||||
|
**"Beside everything else" is the console's tool list.** 0025's second half — the search consults the
|
||||||
|
agent — has no store to consult and needs none: the console lists `records_search` beside the forge's
|
||||||
|
tools and the mesh's own verbs, with a description that says when to call it, and an agent choosing
|
||||||
|
tools for a symptom is the search. That is surfacing, not merely reaching: nobody has to know this
|
||||||
|
repository exists to be offered it.
|
||||||
|
|
||||||
|
**Reading stays one-way.** The module reads the forge and answers; nothing flows back into the
|
||||||
|
repository. It holds no credential that could write.
|
||||||
|
|
||||||
|
**The mesh session, when it exists, is a caller of this module, not a replacement for it.** Design 15's
|
||||||
|
*it holds the design record by reading it* is satisfied by asking `records`; the session brings
|
||||||
|
judgement, this brings the text.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **Issue 006 closes on 0025's own check**, run through the console: `records_search` for a phrase
|
||||||
|
that appears in one design document here returns that document. The module's test does the same
|
||||||
|
against a repository it makes.
|
||||||
|
- **The as-is knowledge document is rewritten.** [`07-knowledge.md`](../03-DESIGN/00-as-is/07-knowledge.md)
|
||||||
|
described the predecessor's two stores; neither is reachable from the mesh, and what the mesh knows
|
||||||
|
is now what its modules answer. Saying otherwise is the failure this repository exists to name.
|
||||||
|
- **A checkout lags.** Between a merge and the next sync — seconds when the forge announces it,
|
||||||
|
minutes when it does not — an answer is the previous commit's, and says which. That is the cost of
|
||||||
|
no copy, and it is a number rather than a silence.
|
||||||
|
- **The reader depends on the forge module's event, by name.** `consumes: gitea.pull.merged` names a
|
||||||
|
module rather than the `git` seat, because the seat declares no events. A forge that is not gitea
|
||||||
|
leaves the timer as the only refresh, which still works.
|
||||||
|
- **What got harder:** the record is now reachable from every machine holding a console, which is what
|
||||||
|
was wanted, and a reader must remember that this repository is public and the mesh is not — the
|
||||||
|
module reads the public repository and nothing about the installation.
|
||||||
|
|
||||||
|
## How this is checked
|
||||||
|
|
||||||
|
| Rule | Checked by |
|
||||||
|
|---|---|
|
||||||
|
| A phrase in one document comes back from where it is written, with the commit | the module's test against a repository it makes; and live, through the console |
|
||||||
|
| A merge on the origin is pulled and the next answer names the new commit | the same test |
|
||||||
|
| A path outside the checkout is refused, not resolved | a test per shape |
|
||||||
|
| A failed sync leaves the checkout standing and is said | a test against an unreachable origin |
|
||||||
|
| Without a repository set, no tools are served and the log says why | the module's own start |
|
||||||
|
| The console lists `records_search` beside every other tool | the console's listing, live |
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [ADR 0025](0025-the-design-record-is-read-not-copied.md) — extended: the reader is a module, the search is the console's list
|
||||||
|
- [ADR 0152](0152-the-operators-surface-is-a-module-the-console.md) — what lists it
|
||||||
|
- [issue 006](../04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md) — what closes
|
||||||
|
- [35 — Reading the record](../03-DESIGN/01-to-be/35-reading-the-record.md) — the design
|
||||||
|
- mesh-catalog `modules/records` — the module (PR 183)
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
---
|
||||||
|
topic: the mesh
|
||||||
|
status: accepted
|
||||||
|
date: 2026-09-30
|
||||||
|
deciders: jochen
|
||||||
|
reconstructed: false
|
||||||
|
extends: 02-DECISIONS/0132-a-seat-carries-the-tools-its-holder-must-serve.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 154. The mesh's own verbs are the mesh-controller seat's tools, and which verbs those are
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
[ADR 0132](0132-a-seat-carries-the-tools-its-holder-must-serve.md) decided that a seat's protocol
|
||||||
|
carries its tools in full, that holding a seat means serving them, and that the mesh's own verbs are
|
||||||
|
the `mesh-controller` seat's. It named three prerequisites, none in place: the protocol in the store
|
||||||
|
rather than in compiled defaults; a protocol richer than a list of verbs; a node-scoped seat's subject
|
||||||
|
carrying the node. And it left one thing to a decision per seat: **which verbs each seat serves**,
|
||||||
|
because a seat's tools bind every future holder.
|
||||||
|
|
||||||
|
The console shipped the same day ([ADR 0152](0152-the-operators-surface-is-a-module-the-console.md))
|
||||||
|
and made the gap visible from the operator's chair: a person on a workstation could call every tool a
|
||||||
|
*module* serves and none of the mesh's own. What a node runs, what is assigned, whether a push
|
||||||
|
applied — the questions issue 147 opened with — still meant a shell on the control node. The console's
|
||||||
|
own handshake said so.
|
||||||
|
|
||||||
|
The control plane already answers every one of those questions, as commands: `status --json`,
|
||||||
|
`node show`, `plan --json`, `assign`, `push`. [ADR 0035](0035-one-implementation-several-surfaces.md)
|
||||||
|
says a surface is an adapter over those with no decisions in it, and the `api` verb proves the shape:
|
||||||
|
every route calls the function the command line calls.
|
||||||
|
|
||||||
|
## Considered Options
|
||||||
|
|
||||||
|
**1. Leave the mesh's verbs to the shell until an identity provider authenticates the HTTP API.**
|
||||||
|
Rejected. The authenticated network surface is for a browser on another machine; the console is
|
||||||
|
already behind the machine's login (0152), and the bus already carries every other tool call under an
|
||||||
|
account whose permission list says what it may ask. Waiting would keep the one surface the mesh has
|
||||||
|
from answering the mesh's own questions, for a reason that does not apply to it.
|
||||||
|
|
||||||
|
**2. Serve the verbs as the mesh-controller *module's* tools, `mesh.mod.mesh-controller.tool.<verb>`.**
|
||||||
|
Rejected; 0132 rejected it already. The controller holds a seat, and the verbs must keep their address
|
||||||
|
while the control plane is being replaced, which is the moment they are most needed. A module's name
|
||||||
|
would change with the implementation; the seat's does not.
|
||||||
|
|
||||||
|
**3. Call each command's function inside the serving process.** Rejected on two facts: the commands
|
||||||
|
print, to the process's standard output, and two calls answered at once would read each other's
|
||||||
|
words; and each command opens and closes its own stores, which the serving process holds open. Making
|
||||||
|
every command return a value is the larger refactor, and it would give the tools a second code path to
|
||||||
|
keep in step with the command line — the thing ADR 0035 forbids.
|
||||||
|
|
||||||
|
**4. The holder of the seat runs the command it names, in its own binary, and answers what it
|
||||||
|
printed.** Chosen.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**The `mesh-controller` seat serves twelve verbs**, and these are its interface, additive within a
|
||||||
|
version ([33 — The tools the mesh answers](../03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md) §7):
|
||||||
|
|
||||||
|
| verb | answers with | takes |
|
||||||
|
|---|---|---|
|
||||||
|
| `tools` | every seat's tools, from the mesh's records | nothing |
|
||||||
|
| `status` | what is wrong, quiet, behind, waiting — `status --json` | nothing |
|
||||||
|
| `nodes` | every machine and its mode | nothing |
|
||||||
|
| `node` | what one machine reported, what it is assigned, why | `node` |
|
||||||
|
| `modules` | every module, its version, commit and machines | nothing |
|
||||||
|
| `seats` | every seat, what it delivers, who holds it — `seats --json` | nothing |
|
||||||
|
| `builds` | what was built lately and what came of it | `module` (optional) |
|
||||||
|
| `plan` | the declaration a machine would be sent — `plan --json` | `node` |
|
||||||
|
| `assign`, `unassign` | the mesh's own words, refusal included | `node`, `module` |
|
||||||
|
| `push` | that it was sent; `status` says what the machine did | `node` (optional: every machine behind) |
|
||||||
|
| `build` | that the build machine was asked; `builds` says what came of it | `repository`, `path`, `ref` |
|
||||||
|
|
||||||
|
**Each verb runs the command it names, in the controller's own binary, and answers what the command
|
||||||
|
printed** — the output, whether it succeeded, and, where the command speaks JSON, the same as data. A
|
||||||
|
refusal is the command's refusal in the command's words, because it is the same output. A verb takes
|
||||||
|
only the arguments its schema names; nothing reaches a flag the schema did not declare. `push` and
|
||||||
|
`build` are sent and not waited for: a call that blocked for a whole apply would time out on every
|
||||||
|
machine that takes a minute and say nothing about the others.
|
||||||
|
|
||||||
|
**The three prerequisites are built.** A seat's protocol is three columns on its row, seeded from the
|
||||||
|
compiled defaults where a row had none and additively thereafter, so a verb a release adds joins the
|
||||||
|
row and nothing an operator wrote is taken away. A served verb is its name, what it does, and the
|
||||||
|
schema of its arguments and answer; a manifest may still write a bare name. A node-scoped seat's tool
|
||||||
|
carries the node it is asked of, as the last token of its subject; a mesh-scoped seat's stays flat.
|
||||||
|
|
||||||
|
**Holding a mesh seat requires serving its verbs**, judged where the store's set is loaded, and the
|
||||||
|
refusal names the missing verbs. The controller's own manifest lists the twelve under `tools`.
|
||||||
|
|
||||||
|
**Discovery reads the records, through the seat.** `tools` is one of the twelve because the console
|
||||||
|
cannot read the store and should not: the mesh answers for its own records through the role that owns
|
||||||
|
them, and the answer is true while any *other* holder restarts. It is not true while the control plane
|
||||||
|
itself restarts, and the console says so rather than hiding the modules' tools with it.
|
||||||
|
|
||||||
|
**A grant of `*` reaches a role's tools; `seat:<seat>.<verb>` grants one.** The console's `*` needed no
|
||||||
|
change to reach the mesh's verbs, which is what a grant meaning *every tool* should mean.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **The console answers the mesh's own questions.** Issue 147's first paragraph closes: what a node
|
||||||
|
runs, what is assigned, whether a push applied, from the machine the person sits at, over the bus,
|
||||||
|
under an account whose permission list says so.
|
||||||
|
- **Whoever may call `mesh-controller.push` may change the mesh.** That is the console's `*` on a
|
||||||
|
machine whose login owns the mesh (0152), and a person's account only if `operator issue` says so.
|
||||||
|
A grant reviewer reads `*` and `seat:mesh-controller.` with the same care.
|
||||||
|
- **A verb here binds every future controller.** Twelve is deliberate: what an operator asks weekly,
|
||||||
|
and nothing that is still finding its shape (`take`, `converge`, `settings`, `secret` stay commands).
|
||||||
|
- **A command's text is the answer**, and text changes. The three verbs that speak JSON carry it as
|
||||||
|
data; the rest are read by a person or an agent, not parsed. Anything that needs a shape asks for
|
||||||
|
`--json` to be added to the command first, which is the right order.
|
||||||
|
- **What got harder:** the mesh-controller seat's row now carries a protocol an operator could edit, and
|
||||||
|
a verb removed from the row is a verb the controller stops serving without a build. That is
|
||||||
|
ADR 0122's arrangement applied to tools, and `seats` shows the row.
|
||||||
|
|
||||||
|
## How this is checked
|
||||||
|
|
||||||
|
| Rule | Checked by |
|
||||||
|
|---|---|
|
||||||
|
| Every declared verb is one the binary can run, with the arguments its schema names | a test walks the table and derives a command line for each |
|
||||||
|
| A verb missing a required argument is refused in its own words, before anything runs | a test per shape |
|
||||||
|
| A holder that does not serve a mesh seat's verbs cannot hold it, and the refusal names them | a catalogue test against a seat with two verbs and a holder with one |
|
||||||
|
| A node-scoped seat's tool carries the node; a mesh seat's does not | the bus composition test: two nodes derive two addresses |
|
||||||
|
| The controller subscribes its seat's tools and may answer | the composition test, and the golden user list |
|
||||||
|
| `*` reaches a role's tools; `seat:` grants one and refuses a name with no verb | the composition test |
|
||||||
|
| The protocol is seeded into the row and widened additively | the store-backed seat test |
|
||||||
|
| Live: the console lists `mesh-controller.status` and a call answers what `status --json` prints | the rollout of this record |
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [ADR 0132](0132-a-seat-carries-the-tools-its-holder-must-serve.md) — extended: the prerequisites built, the verbs decided
|
||||||
|
- [ADR 0152](0152-the-operators-surface-is-a-module-the-console.md) — the surface that lists them
|
||||||
|
- [ADR 0035](0035-one-implementation-several-surfaces.md) — a surface is an adapter with no decisions in it
|
||||||
|
- [ADR 0122](0122-a-seat-is-data-a-rename-is-a-database-update.md) — the row is the mesh's, and now carries the protocol
|
||||||
|
- [33 — The tools the mesh answers](../03-DESIGN/01-to-be/33-the-tools-the-mesh-answers.md) — the design this completes
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
---
|
||||||
|
topic: what runs on it
|
||||||
|
status: accepted
|
||||||
|
date: 2026-09-30
|
||||||
|
deciders: jochen
|
||||||
|
reconstructed: false
|
||||||
|
extends: 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 155. A definition names no installation: how that is checked, and the three ways a value that did gets out
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
[ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md) decided that a module definition
|
||||||
|
names no node, no mesh and no host path, and said how the name half is checked: *a catalogue test
|
||||||
|
finds no domain name in any definition value*. No such test existed
|
||||||
|
([issue 134](../04-ISSUES/134-a-definition-may-still-name-the-mesh/00-report.md)). Written and run
|
||||||
|
over the 77 definitions on 2026-09-30, the check it describes finds **42 values**, in 15 definitions,
|
||||||
|
and they are of four kinds that want four different answers:
|
||||||
|
|
||||||
|
| kind | count | example |
|
||||||
|
|---|---|---|
|
||||||
|
| a service told its own public name as a literal | 5 | an identity provider's `KC_HOSTNAME`, an object store's console redirect, an automation tool's webhook URL |
|
||||||
|
| an operator's value written into the definition | 10 | a mail server's domain, site name, website, and the address it trusts a real-IP header from |
|
||||||
|
| this mesh's forge, by URL, as a recipe's build context | 2 | the builder and the proxy, which package the controller's source |
|
||||||
|
| an application built outside the mesh, pulled from this mesh's registry | 7 | four sites and tools whose repositories are the operator's own |
|
||||||
|
| the world's servers, named by upstream defaults | 10 | a Matrix homeserver's trusted key server, Element's integration manager |
|
||||||
|
| a module named after the domain it serves | 8 | one site module, with its paths and network named after it |
|
||||||
|
|
||||||
|
Not one was careless. Each was the value the software needs, and until today there was nowhere else
|
||||||
|
to put it ([issue 122](../04-ISSUES/122-a-module-cannot-ask-for-its-own-public-name/00-report.md)).
|
||||||
|
Two of the answers were built before this record: a module is told the name its route composes
|
||||||
|
(`${bound:<route>:name}`, controller PR 149, 2026-09-30), and a source may be a path on the git seat
|
||||||
|
([ADR 0111](0111-a-build-source-is-on-the-git-seat-or-external.md)). What was missing: an operator's
|
||||||
|
value in a file the software reads, the same for a build *context*, a way to say a name is meant, and
|
||||||
|
the check.
|
||||||
|
|
||||||
|
## Considered Options
|
||||||
|
|
||||||
|
**1. A string search for the installation's own names.** Rejected. The controller is as
|
||||||
|
mesh-agnostic as the definitions; it does not know which names are "this mesh's", and a check that had
|
||||||
|
to be told would be configured per installation and pass everywhere else. What it can know is the
|
||||||
|
*shape*: a name under a public top-level domain, a public address.
|
||||||
|
|
||||||
|
**2. Report every such shape.** Rejected. Eight of the 42 were `why` strings — prose the mesh never
|
||||||
|
reads, explaining what a port is for — and a check that reports those beside `KC_HOSTNAME` teaches
|
||||||
|
people to ignore the report. And a Matrix homeserver *must* name the federation's public key server;
|
||||||
|
a check with no way to say so would be a check people argue with rather than obey.
|
||||||
|
|
||||||
|
**3. Judge what the mesh acts on; let a definition say which names it means, one by one, with a
|
||||||
|
reason; exempt the world's services that a definition may name as a policy default.** Chosen.
|
||||||
|
|
||||||
|
**For an operator's value**, one option was to wait for design 27's requirement form in full. Rejected
|
||||||
|
for the reason 0112 gave against a slow operator provider: if asking a person for a value takes more
|
||||||
|
than a setting, module authors route around it and the literals come back. `${setting:<key>}` is the
|
||||||
|
operator provider in its first form, on the settings a module already has.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**The check.** Every string value of a definition that the mesh acts on is judged for a hostname under
|
||||||
|
a public top-level domain and for a public address. Not judged: `why` and `description`, which are
|
||||||
|
prose. Allowed where they can only mean the world: the public registries an `image` may be pulled
|
||||||
|
from, the public resolvers a machine may forward to, and the public certificate authorities' ACME
|
||||||
|
directories. The container runtime's alias for its own host is the runtime's. A module's own name is a
|
||||||
|
value too. The check runs in `module check` and as a catalogue-wide test; **it does not yet refuse at
|
||||||
|
registration**, because the list it prints is the list that shrinks, and a registration that refused a
|
||||||
|
manifest whose only remedy is a merge elsewhere would refuse the mesh's own catalogue on the day the
|
||||||
|
check landed. It moves to registration when the list has been empty for a release.
|
||||||
|
|
||||||
|
**A name a definition means is declared with its reason.** `names-on-purpose` on a resource maps each
|
||||||
|
such name to why: *the federation's public key server, the world's*; *built outside the mesh, from the
|
||||||
|
application's own repository, until that repository is a build source on the git seat*. A name the map
|
||||||
|
does not cover is still reported. The host never sees the word.
|
||||||
|
|
||||||
|
**An operator's value reaches a file as `${setting:<key>}`**, filled from the module's settings
|
||||||
|
layers — the mesh's, then the node's — the same layers a mergeable file and a contribution take, so
|
||||||
|
`settings set <module>` stays the one place a person's values go. Refused, naming the key and the
|
||||||
|
command, when nothing set it: a default for a mail domain would be the literal this removes, and a
|
||||||
|
blank written silently would be a service that comes up wrong somewhere that names nothing.
|
||||||
|
|
||||||
|
**A build context may live on the git seat.** `context: {"seat": "git", "repository": "<owner>/<name>"}`
|
||||||
|
is composed by the mesh that builds it: the request carries each seat's clone base, and a builder told
|
||||||
|
no base for a seat a context names refuses the build by the seat's name rather than guessing a forge.
|
||||||
|
|
||||||
|
**A module is named for what it is.** The site module named after its domain is `website`.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **The catalogue names no installation, and a test says so.** The forty-two became zero the same day,
|
||||||
|
by the four answers above; seven of them are declared on purpose and stay visible as the list to
|
||||||
|
shrink — four applications the mesh does not build yet.
|
||||||
|
- **An operator's values are the assignment's.** The mail module takes its domain, its site name, its
|
||||||
|
website and the address it trusts a real-IP header from as settings; a mesh that installs it without
|
||||||
|
them is refused at composition, by name, which is the right moment. The module's own README says
|
||||||
|
which.
|
||||||
|
- **What got harder:** a manifest reviewer has one more word to read, and `names-on-purpose` on an
|
||||||
|
application's image is a debt visible in the definition until the application is built here. A
|
||||||
|
reader of `settings set` output sees more keys than files, because a key a file asks for is a
|
||||||
|
destination too.
|
||||||
|
- **Not decided here:** ADR 0112's requirement form (design 27) still replaces `${setting:…}` and the
|
||||||
|
other placeholders when it lands; this is its first case, the way [ADR 0038](0038-the-mesh-assigns-the-port.md)
|
||||||
|
was for ports. Host paths ([issue 119](../04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md))
|
||||||
|
are the next step of the same group, and the registry's name ([issue 123](../04-ISSUES/123-the-image-registry-is-named-after-a-role/00-report.md))
|
||||||
|
the one after.
|
||||||
|
|
||||||
|
## How this is checked
|
||||||
|
|
||||||
|
| Rule | Checked by |
|
||||||
|
|---|---|
|
||||||
|
| A value naming an installation is reported at its path, in the definition's words | a catalogue unit test over a definition with a hostname in an env value and a public address in a file |
|
||||||
|
| Prose, the world's registries in an image, public resolvers, ACME directories and the runtime's own alias are not reported | the same tests |
|
||||||
|
| A name declared on purpose is not reported; a name beside it that is not declared is | a test with a homeserver's config |
|
||||||
|
| An image from an installation's registry needs a reason | a test without and with the word |
|
||||||
|
| A module named after a domain is reported | a test |
|
||||||
|
| No definition in the catalogue names an installation | `TestNoCatalogueManifestNamesAnInstallation` over the checkout, and `module check modules/` |
|
||||||
|
| `${setting:key}` fills from the layers, node over mesh; refused by name when unset; not stray when set | three tests |
|
||||||
|
| A context on a seat is cloned from the base the mesh sent; a seat with no base is refused by name | the builder's test |
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md) — extended: the check it promised, and the operator provider's first form
|
||||||
|
- [ADR 0111](0111-a-build-source-is-on-the-git-seat-or-external.md) — a source on the seat; now a context too
|
||||||
|
- [issue 122](../04-ISSUES/122-a-module-cannot-ask-for-its-own-public-name/00-report.md), [issue 134](../04-ISSUES/134-a-definition-may-still-name-the-mesh/00-report.md) — what this closes
|
||||||
|
- [27 — A module requires, the mesh resolves](../03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md) — where this sits in the larger design
|
||||||
|
- mesh-controller PR 169, mesh-catalog PR 188 — the check, the words, and the catalogue that passes it
|
||||||
@@ -168,6 +168,7 @@ python3 00-META/checks/index.py fail if stale
|
|||||||
- **0132** — [A seat carries the tools its holder must serve](0132-a-seat-carries-the-tools-its-holder-must-serve.md)
|
- **0132** — [A seat carries the tools its holder must serve](0132-a-seat-carries-the-tools-its-holder-must-serve.md)
|
||||||
- **0134** — [The mesh says what it applied](0134-the-mesh-says-what-it-applied.md)
|
- **0134** — [The mesh says what it applied](0134-the-mesh-says-what-it-applied.md)
|
||||||
- **0142** — [The mesh delivers its own components as binaries, not as container images](0142-the-mesh-delivers-its-own-components-as-binaries.md)
|
- **0142** — [The mesh delivers its own components as binaries, not as container images](0142-the-mesh-delivers-its-own-components-as-binaries.md)
|
||||||
|
- **0154** — [The mesh's own verbs are the mesh-controller seat's tools, and which verbs those are](0154-the-meshs-own-verbs-are-the-controller-seats-tools.md)
|
||||||
|
|
||||||
### Its tiers, from the bottom up
|
### Its tiers, from the bottom up
|
||||||
|
|
||||||
@@ -256,6 +257,8 @@ python3 00-META/checks/index.py fail if stale
|
|||||||
- **0146** — [Connectivity is checked by name, per hosting form, with a valid certificate](0146-connectivity-is-checked-by-name-per-hosting-form.md)
|
- **0146** — [Connectivity is checked by name, per hosting form, with a valid certificate](0146-connectivity-is-checked-by-name-per-hosting-form.md)
|
||||||
- **0147** — [A module anchors the mesh's authority on a machine, and takes it away again](0147-a-module-anchors-the-meshs-authority.md)
|
- **0147** — [A module anchors the mesh's authority on a machine, and takes it away again](0147-a-module-anchors-the-meshs-authority.md)
|
||||||
- **0150** — [A module's own code runs as supervised processes under the module's one account](0150-a-modules-own-code-runs-as-supervised-processes-under-one-account.md)
|
- **0150** — [A module's own code runs as supervised processes under the module's one account](0150-a-modules-own-code-runs-as-supervised-processes-under-one-account.md)
|
||||||
|
- **0152** — [The operator's surface is a module the mesh assigns: the console](0152-the-operators-surface-is-a-module-the-console.md)
|
||||||
|
- **0155** — [A definition names no installation: how that is checked, and the three ways a value that did gets out](0155-a-definition-names-no-installation-and-how-that-is-checked.md)
|
||||||
|
|
||||||
### How it is built
|
### How it is built
|
||||||
|
|
||||||
@@ -297,5 +300,6 @@ python3 00-META/checks/index.py fail if stale
|
|||||||
- **0034** — [The local account owns the mesh, and a web application's login is not that](0034-the-local-account-owns-the-mesh.md)
|
- **0034** — [The local account owns the mesh, and a web application's login is not that](0034-the-local-account-owns-the-mesh.md)
|
||||||
- **0080** — [The development cycle is checked, not trusted](0080-the-development-cycle-is-checked.md)
|
- **0080** — [The development cycle is checked, not trusted](0080-the-development-cycle-is-checked.md)
|
||||||
- **0081** — [A decision nothing cites is not yet in the chain](0081-a-decision-nothing-cites-is-not-yet-in-the-chain.md)
|
- **0081** — [A decision nothing cites is not yet in the chain](0081-a-decision-nothing-cites-is-not-yet-in-the-chain.md)
|
||||||
|
- **0153** — [The record is read by a module the mesh assigns, and the console lists it](0153-the-record-is-read-by-a-module-and-the-console-lists-it.md)
|
||||||
|
|
||||||
<!-- index:end -->
|
<!-- index:end -->
|
||||||
|
|||||||
@@ -1,78 +1,58 @@
|
|||||||
---
|
---
|
||||||
layer: as-is
|
layer: as-is
|
||||||
status: implemented
|
status: implemented
|
||||||
code: [hal]
|
code: [mesh-catalog modules/records, mesh-catalog modules/mesh-console]
|
||||||
updated: 2026-08-23
|
updated: 2026-09-30
|
||||||
decisions: []
|
decisions:
|
||||||
|
- 02-DECISIONS/0025-the-design-record-is-read-not-copied.md
|
||||||
|
- 02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md
|
||||||
|
- 02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# Knowledge
|
# Knowledge
|
||||||
|
|
||||||
The mesh keeps two knowledge stores. They are not redundant, and knowing which is which is the
|
**The mesh keeps no knowledge store.** What it knows is what its modules answer, and the way a person
|
||||||
difference between finding an answer in one search and rediscovering it over several hours.
|
or an agent asks is the console's tool list on the machine they sit at
|
||||||
|
([13 — The console](13-the-console.md)). This document used to describe two stores; it is rewritten
|
||||||
|
because neither exists from the mesh's side, and an as-is document that describes what is gone is a
|
||||||
|
brochure.
|
||||||
|
|
||||||
## The operational memory
|
## What was here, and where it went
|
||||||
|
|
||||||
A store of operational notes, written and read by whoever — human or agent — is working. Each
|
Until the cut-over of 2026-09-28 the predecessor ran two stores: an operational memory of notes
|
||||||
note is a slug and a body: how something works, what went wrong, what the fix was, what
|
indexed on symptoms, and a structured archive of governed documents with a librarian approving
|
||||||
assumption turned out to be false.
|
promotion. Both were reached through the predecessor's tool server over the bus the mesh removed
|
||||||
|
([issue 147](../../04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md)).
|
||||||
|
Nothing in the mesh reaches them now, and nothing in the mesh has replaced them: there is no note
|
||||||
|
store, no archive, no librarian, and the lessons of the last days were written into this repository by
|
||||||
|
hand. That is a gap, and it is stated here rather than papered over. What replaces a symptom-indexed
|
||||||
|
memory, if anything does, is undecided.
|
||||||
|
|
||||||
It is indexed on **symptoms**. The entry someone needs is usually titled after the error they
|
## The record
|
||||||
are staring at, which is why the standing instruction is to search the literal error text
|
|
||||||
before forming a hypothesis rather than after one fails.
|
|
||||||
|
|
||||||
Its content is overwhelmingly the record of previous debugging: a large body of
|
**The design record is read where it is written.** Since 2026-09-30 a module, `records`, keeps a
|
||||||
troubleshooting entries, module conventions, and standing notes about work that is open. It is
|
checkout of this repository from the forge — cloned from the `git` seat, reset to the origin on every
|
||||||
the mesh's institutional memory of *what has already gone wrong*.
|
merge the forge announces and every ten minutes — and answers over the bus: where a phrase appears as
|
||||||
|
written, one document whole, what a folder holds, and where the checkout stands, each naming the
|
||||||
|
commit it read. Which repository it reads is a setting on its assignment; the module names no mesh.
|
||||||
|
|
||||||
The cost of skipping it is documented in the mesh's own record: entries have been rediscovered
|
It is listed by the console beside every other tool, with a description that says to search the
|
||||||
from scratch, over hours, in sessions where the search was skipped because the trail felt
|
literal words of a symptom before forming a hypothesis. That is what
|
||||||
confident. It fires hardest on familiar ground, not unfamiliar ground.
|
[ADR 0025](../../02-DECISIONS/0025-the-design-record-is-read-not-copied.md) meant by *beside
|
||||||
|
everything else*, in a mesh with no store to be beside
|
||||||
|
([ADR 0153](../../02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md)).
|
||||||
|
|
||||||
## The structured archive
|
Nothing is copied. A checkout lags the source by seconds after an announced merge and by minutes
|
||||||
|
otherwise, and says so.
|
||||||
|
|
||||||
A second store, structured rather than flat: spaces, pages, revisions, tiers, and full-text
|
## The constitution
|
||||||
search. Where the operational memory is a note, this is a document with an owner and a
|
|
||||||
lifecycle.
|
|
||||||
|
|
||||||
Content is promoted through tiers — private, then team, then platform — with a librarian agent
|
[`00-META/how-we-build.md`](../../00-META/how-we-build.md) is the source of the mesh constitution
|
||||||
owning approval and promotion at the boundary. Proposals to edit are reviewed rather than
|
([ADR 0021](../../02-DECISIONS/0021-hq-is-the-source-of-the-constitution.md)). The page it used to be
|
||||||
applied.
|
synchronised into lived in the predecessor's archive and is unreachable; the constitution today is
|
||||||
|
read from this repository, through the same module, and playbook 05's sync has nothing to write to.
|
||||||
This is where the mesh's **governed** documents live, including the constitution injected into
|
|
||||||
design sessions ([ADR 0020](../../02-DECISIONS/0020-the-mesh-is-governed-by-a-constitution.md)).
|
|
||||||
|
|
||||||
## Why both
|
|
||||||
|
|
||||||
The distinction is by lifecycle, not by subject.
|
|
||||||
|
|
||||||
| Operational memory | Structured archive |
|
|
||||||
|---|---|
|
|
||||||
| Written the moment something is learned | Written deliberately, reviewed |
|
|
||||||
| Flat, symptom-indexed | Structured, tiered, owned |
|
|
||||||
| Anyone writes; nothing approves | Promotion is approved |
|
|
||||||
| Truth is "this happened" | Truth is "this is agreed" |
|
|
||||||
|
|
||||||
Collapsing them would cost one of the two properties: either every hard-won note waits for
|
|
||||||
review, or governed documents can be changed by anyone mid-incident.
|
|
||||||
|
|
||||||
## Where this repository sits
|
## Where this repository sits
|
||||||
|
|
||||||
This repository is a third thing, and the objection was raised when it was created: a fourth
|
A third thing beside two that are gone, which makes it the first: the one governed record the mesh
|
||||||
knowledge system repeats the mistake the split was made to fix.
|
has, public, read by a module the mesh assigns, and edited nowhere else.
|
||||||
|
|
||||||
The answer given was **indexing, not location** — that these documents are indexed into the
|
|
||||||
knowledge base so that a symptom search returns them alongside everything else. One source,
|
|
||||||
many surfaces.
|
|
||||||
|
|
||||||
**That indexing does not currently exist.** A search for this repository's content returns
|
|
||||||
nothing. The claim is load-bearing for the decision to separate the repository at all, and
|
|
||||||
until it is true, this repository is exactly the fourth knowledge system the objection
|
|
||||||
described. Recorded here because it is a statement about how the mesh's knowledge actually
|
|
||||||
works today, and as [`04-ISSUES/006`](../../04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md).
|
|
||||||
|
|
||||||
## The librarian
|
|
||||||
|
|
||||||
A single agent owns the archive's approvals and promotions. Its approval capabilities have at
|
|
||||||
times not been reachable as tools, which does not affect the operational memory but does mean
|
|
||||||
promotion stops silently — the store keeps accepting proposals that nothing can approve.
|
|
||||||
|
|||||||
@@ -82,6 +82,22 @@ to clone.
|
|||||||
The schema column added for this defaults to empty rather than null, because "not on a seat" is a
|
The schema column added for this defaults to empty rather than null, because "not on a seat" is a
|
||||||
real answer, so every row recorded before the change keeps exactly the meaning it had.
|
real answer, so every row recorded before the change keeps exactly the meaning it had.
|
||||||
|
|
||||||
|
## A seat's protocol is on its row, and the controller serves its own
|
||||||
|
|
||||||
|
*Since 2026-09-30 ([ADR 0154](../../02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md)).*
|
||||||
|
The `seat` table carries `accepts`, `emits` and `serves`; `serves` holds each verb with its description
|
||||||
|
and input schema. The rows were seeded from the compiled defaults the first time a controller with the
|
||||||
|
columns migrated, and each later migration adds any verb the defaults name that a row lacks, never
|
||||||
|
removing one. `UseSeats` still falls back to the compiled protocol for a row with none, which after the
|
||||||
|
first seeding is no row.
|
||||||
|
|
||||||
|
The `mesh-controller` seat serves twelve verbs — `tools`, `status`, `nodes`, `node`, `modules`, `seats`,
|
||||||
|
`builds`, `plan`, `assign`, `unassign`, `push`, `build` — on `mesh.seat.mesh-controller.tool.<verb>`,
|
||||||
|
each answered by the controller running that command in its own binary and returning what it printed.
|
||||||
|
A module claiming a mesh seat with verbs must list them under `tools` or registration refuses it by
|
||||||
|
name. A node-scoped seat's tool is `mesh.seat.<seat>.tool.<verb>.<node>`; no node-scoped seat declares
|
||||||
|
one yet.
|
||||||
|
|
||||||
## Where this differs from the design
|
## Where this differs from the design
|
||||||
|
|
||||||
**Capacity is not implemented.** The design's vocabulary has a seat with a capacity, and a
|
**Capacity is not implemented.** The design's vocabulary has a seat with a capacity, and a
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
layer: as-is
|
||||||
|
status: implemented
|
||||||
|
code: [mesh-catalog modules/mesh-console, mesh-tools src/mesh.ts, mesh-tools src/http.ts, mesh-tools src/runtime.ts, mesh-controller internal/broker, mesh-controller cmd/mesh-controller/check.go, mesh-controller cmd/mesh-controller/seatverbs.go]
|
||||||
|
updated: 2026-09-30
|
||||||
|
decisions:
|
||||||
|
- 02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md
|
||||||
|
- 02-DECISIONS/0095-the-control-plane-is-the-way-to-ask-a-module.md
|
||||||
|
- 02-DECISIONS/0037-where-a-module-lives.md
|
||||||
|
- 02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# The console, as it runs
|
||||||
|
|
||||||
|
**The mesh's tools reach a person through a module the mesh assigned to their machine.** Since
|
||||||
|
2026-09-30 a workstation that is a node can be assigned `mesh-console`; the mesh mints a bus account
|
||||||
|
`<node>.mesh-console`, seals its credential to the machine, and the container binds
|
||||||
|
`127.0.0.1:<port>` with the port the mesh assigned for the manifest's declared one. An agent on the
|
||||||
|
machine is pointed at `http://127.0.0.1:<port>/mcp` and sees the mesh's tools; a person uses the same
|
||||||
|
endpoint. Nothing on the machine holds a credential a person had to carry.
|
||||||
|
|
||||||
|
## What it answers
|
||||||
|
|
||||||
|
`initialize`, `tools/list`, `tools/call`, over HTTP, one JSON body per request, no session and no event
|
||||||
|
stream. `tools/list` is what the running modules answered: every tool runtime built on or after that day
|
||||||
|
serves a `tools` verb for its module, and the console asks the catalogue for the roster and each module
|
||||||
|
for its tools. A module that did not answer is named in the list's `_meta.notAnswering`. On the day it
|
||||||
|
shipped that was 36 of 51 modules — those that serve no tools at all, and those whose rebuilt runtime the
|
||||||
|
mesh records rather than rolls out — and 62 tools from the rest.
|
||||||
|
|
||||||
|
`tools/call` reaches any tool by `<module>.<tool>`, listed or not. The console's grant is `*`, so what it
|
||||||
|
may call is every tool on the mesh; its account may publish nothing else and subscribes nothing.
|
||||||
|
|
||||||
|
## The mesh's own verbs
|
||||||
|
|
||||||
|
*Since 2026-09-30 evening ([ADR 0154](../../02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md)).*
|
||||||
|
The console asks the `mesh-controller` seat's `tools` verb beside the modules and lists every role's
|
||||||
|
tools as `<seat>.<verb>` — `mesh-controller.status`, `mesh-controller.push` and the other ten. A call
|
||||||
|
to `<prefix>.<name>` reaches the seat when the prefix is a seat declaring that verb, and the module
|
||||||
|
otherwise; `seat:<seat>.<verb>` says so outright. When the control plane does not answer, the list
|
||||||
|
names `mesh-controller (seat)` as not answering and carries the modules' tools regardless. The
|
||||||
|
`mesh-controller` *module* is always named as not answering: it serves no module tools, only its seat's.
|
||||||
|
|
||||||
|
## Around it
|
||||||
|
|
||||||
|
- **`invokes`** in a manifest is the grant. It is composed into the bus's user list exactly as a
|
||||||
|
person's account is; the console is the only module that declares it.
|
||||||
|
- **`module check <file|dir>…`** on the controller's binary judges a manifest with no mesh: the strict
|
||||||
|
parse, every per-manifest problem, and the rules between the manifests given. It prints what it cannot
|
||||||
|
judge without a store rather than refusing. The console's own manifest was the first thing checked
|
||||||
|
with it, and the whole catalogue passes.
|
||||||
|
- **The person's client remains.** `operator issue` and `mesh tools|call|mcp` with a credential file
|
||||||
|
still work, for a machine that is not a node and for a mesh not yet able to assign anything.
|
||||||
|
`mesh tools --console <url>` goes through a running console with no credential; it is covered by the
|
||||||
|
runtime repository's tests and was not exercised on the live mesh.
|
||||||
|
|
||||||
|
## What shipped bent
|
||||||
|
|
||||||
|
- A module registered by hand from the catalogue with `--source <url> --path modules/<m>` records a URL,
|
||||||
|
not a place on the git seat: `--self` takes the forge path form (`<owner>/<repository>`), which the
|
||||||
|
operator did not pass. The rebuild-on-merge matched the URL anyway.
|
||||||
|
- Modules whose upgrade policy is *record* — the forge among them — answered `tools` only once
|
||||||
|
something pushed their rebuilt runtime; until then they are listed as not answering while still
|
||||||
|
callable. That is the policy doing what it says, not a fault of the console.
|
||||||
@@ -15,12 +15,13 @@ Where the two disagree, the implementation wins and the disagreement is stated.
|
|||||||
| [`04-delivery.md`](04-delivery.md) | Push to running: the three silos, levels, and what a green pipeline proves |
|
| [`04-delivery.md`](04-delivery.md) | Push to running: the three silos, levels, and what a green pipeline proves |
|
||||||
| [`05-runtime-and-installation.md`](05-runtime-and-installation.md) | The node runtime, its modes, and how a node comes into being |
|
| [`05-runtime-and-installation.md`](05-runtime-and-installation.md) | The node runtime, its modes, and how a node comes into being |
|
||||||
| [`06-configuration-and-secrets.md`](06-configuration-and-secrets.md) | Managed files, value resolution, and where secrets live |
|
| [`06-configuration-and-secrets.md`](06-configuration-and-secrets.md) | Managed files, value resolution, and where secrets live |
|
||||||
| [`07-knowledge.md`](07-knowledge.md) | The two knowledge stores, and what each is for |
|
| [`07-knowledge.md`](07-knowledge.md) | The mesh keeps no store: what it knows is what modules answer, and the record is read by one |
|
||||||
| [`08-agents-and-work.md`](08-agents-and-work.md) | Agents as employees, tasks, workflows, and the meeting model |
|
| [`08-agents-and-work.md`](08-agents-and-work.md) | Agents as employees, tasks, workflows, and the meeting model |
|
||||||
| [`09-interfaces-and-observability.md`](09-interfaces-and-observability.md) | How the mesh is reached and watched — tools, board, proxy, health, thoughts |
|
| [`09-interfaces-and-observability.md`](09-interfaces-and-observability.md) | How the mesh is reached and watched — tools, board, proxy, health, thoughts |
|
||||||
| [`10-module-catalogue.md`](10-module-catalogue.md) | The catalogue's shape, and what its shape says |
|
| [`10-module-catalogue.md`](10-module-catalogue.md) | The catalogue's shape, and what its shape says |
|
||||||
| [`11-the-lab.md`](11-the-lab.md) | The lab — the first piece of the new shape that exists, and what it does not yet do |
|
| [`11-the-lab.md`](11-the-lab.md) | The lab — the first piece of the new shape that exists, and what it does not yet do |
|
||||||
| [`12-the-seats.md`](12-the-seats.md) | The seats the mesh defines, who holds one, and where a seat changes resolution |
|
| [`12-the-seats.md`](12-the-seats.md) | The seats the mesh defines, who holds one, and where a seat changes resolution |
|
||||||
|
| [`13-the-console.md`](13-the-console.md) | The mesh's tools on the machine a person sits at, served by a module the mesh assigned there |
|
||||||
|
|
||||||
## What these documents are not
|
## What these documents are not
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ code:
|
|||||||
- mesh-controller internal/catalogue/build.go
|
- mesh-controller internal/catalogue/build.go
|
||||||
- mesh-controller internal/inventory/secrets.go
|
- mesh-controller internal/inventory/secrets.go
|
||||||
- mesh-controller cmd/mesh-builder
|
- mesh-controller cmd/mesh-builder
|
||||||
updated: 2026-09-12
|
updated: 2026-09-30
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0069-a-module-is-a-repository-and-a-path.md
|
- 02-DECISIONS/0069-a-module-is-a-repository-and-a-path.md
|
||||||
|
- 02-DECISIONS/0037-where-a-module-lives.md
|
||||||
- 02-DECISIONS/0009-modules-and-the-graph.md
|
- 02-DECISIONS/0009-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0010-delivery.md
|
- 02-DECISIONS/0010-delivery.md
|
||||||
- 02-DECISIONS/0005-the-node-host.md
|
- 02-DECISIONS/0005-the-node-host.md
|
||||||
@@ -60,6 +61,32 @@ module from a repository and a path, and the root-only reading left every existi
|
|||||||
unbuildable — pointed at the catalogue the builder finds no manifest, pointed at a module's source
|
unbuildable — pointed at the catalogue the builder finds no manifest, pointed at a module's source
|
||||||
it finds no manifest either.*
|
it finds no manifest either.*
|
||||||
|
|
||||||
|
## A manifest is checked where it is written
|
||||||
|
|
||||||
|
*Added 2026-09-30, from [issue 148](../../04-ISSUES/148-a-manifest-outside-this-catalogue-has-no-check/00-report.md).*
|
||||||
|
|
||||||
|
The check the mesh applies at registration — the manifest parses strictly, every name in it is a
|
||||||
|
usable one, its routes and events and seats are well formed, and no two manifests given together
|
||||||
|
declare one seat — is a verb on the controller's binary, `module check <manifest>…`, and it needs no
|
||||||
|
mesh. It reads the files it is given, runs the same functions registration runs, prints every problem
|
||||||
|
in the manifest's own words, and exits non-zero if there was one. Somebody describing their own
|
||||||
|
application in their own repository — the case [ADR 0037](../../02-DECISIONS/0037-where-a-module-lives.md)
|
||||||
|
calls the one that matters most — runs it before pushing, and finds out there rather than when a
|
||||||
|
running mesh refuses the registration, or later, when a machine applies something that resolved and
|
||||||
|
should not have.
|
||||||
|
|
||||||
|
**What it cannot know, it says.** A seat another module declares elsewhere is unknown to a check that
|
||||||
|
was not handed that module's manifest, and the output says so rather than refusing: pass the other
|
||||||
|
manifest too. The mesh's own seats it knows from the binary, which is the one place that set may be
|
||||||
|
read without a store ([ADR 0122](../../02-DECISIONS/0122-a-seat-is-data-a-rename-is-a-database-update.md)
|
||||||
|
keeps the store authoritative, so a claim on a mesh seat is judged fully only at registration, and the
|
||||||
|
check says that too).
|
||||||
|
|
||||||
|
*How it is checked:* the controller's test runs the check over the catalogue checkout beside it and
|
||||||
|
over a manifest with a known fault, and asserts the first passes and the second names the fault; the
|
||||||
|
test that used to be the only check, `TestEveryCatalogueManifestParses`, now stands beside a command
|
||||||
|
anybody can run.
|
||||||
|
|
||||||
## The manifest in the repository is not the manifest the mesh holds
|
## The manifest in the repository is not the manifest the mesh holds
|
||||||
|
|
||||||
A resource names an artifact:
|
A resource names an artifact:
|
||||||
|
|||||||
@@ -148,6 +148,10 @@ than reproduced from a declaration — because there is nothing to reproduce it
|
|||||||
([ADR 0025](../../02-DECISIONS/0025-the-design-record-is-read-not-copied.md)), not by holding a
|
([ADR 0025](../../02-DECISIONS/0025-the-design-record-is-read-not-copied.md)), not by holding a
|
||||||
copy. It is that reader; there is not a second agent for it.
|
copy. It is that reader; there is not a second agent for it.
|
||||||
|
|
||||||
|
*2026-09-30:* the reading is a module's — `records`, [35 — Reading the record](35-reading-the-record.md),
|
||||||
|
[ADR 0153](../../02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md). The
|
||||||
|
session, when built, asks it rather than reading for itself; what it adds is judgement, not text.
|
||||||
|
|
||||||
**It answers into a symptom search**, so what it knows appears beside ordinary results rather than
|
**It answers into a symptom search**, so what it knows appears beside ordinary results rather than
|
||||||
only when it is asked. **And when it cannot be reached, the search says so.** A result set that
|
only when it is asked. **And when it cannot be reached, the search says so.** A result set that
|
||||||
silently omits this material looks identical to one where nothing matched — the same rule as the
|
silently omits this material looks identical to one where nothing matched — the same rule as the
|
||||||
|
|||||||
@@ -186,7 +186,8 @@ disagrees with it.
|
|||||||
| `grants` | credentials it must create for its consumers |
|
| `grants` | credentials it must create for its consumers |
|
||||||
| `filtering` | rules beyond its own ports |
|
| `filtering` | rules beyond its own ports |
|
||||||
| `computed` | marks a module the controller generates rather than an author writing |
|
| `computed` | marks a module the controller generates rather than an author writing |
|
||||||
| `build.artifacts` | what it produces |
|
| `build.artifacts` | what it produces; an artifact's `context` may be a URL or a path on the git seat (`seat: git`), composed by the mesh that builds it |
|
||||||
|
| `names-on-purpose` | on a resource: each name it means to name — the world's federation server, a registry that built an application the mesh does not — with its reason. A definition names no installation, and a test says so ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)) |
|
||||||
|
|
||||||
**A container mounts only what the manifest declares**
|
**A container mounts only what the manifest declares**
|
||||||
([ADR 0091](../../02-DECISIONS/0091-a-mount-is-declared-three-ways.md)). A bind mount the module
|
([ADR 0091](../../02-DECISIONS/0091-a-mount-is-declared-three-ways.md)). A bind mount the module
|
||||||
@@ -234,7 +235,7 @@ counts as a copy and what as a base.
|
|||||||
| resource | is | a module may |
|
| resource | is | a module may |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `directory` | a directory with a mode and an owner | ✅ |
|
| `directory` | a directory with a mode and an owner | ✅ |
|
||||||
| `file` | literal content, with `${bound:…}` and `${secret:…}` filled in | ✅ |
|
| `file` | literal content, with `${bound:…}`, `${secret:…}`, `${dir:…}`, `${port:…}`, `${machine:…}` and `${setting:…}` filled in — the last an operator's value from the assignment's settings, refused by name when unset ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)) | ✅ |
|
||||||
| `user` | a login | ✅ |
|
| `user` | a login | ✅ |
|
||||||
| `access` | a pre-existing path it may use and must not own | ✅ |
|
| `access` | a pre-existing path it may use and must not own | ✅ |
|
||||||
| `archive` | files fetched by digest and unpacked | ✅ |
|
| `archive` | files fetched by digest and unpacked | ✅ |
|
||||||
|
|||||||
@@ -361,6 +361,13 @@ bridged. It is three things:
|
|||||||
|
|
||||||
Nothing is built of this before §10's bed passes; the MCP surface is a thin adapter over (2).
|
Nothing is built of this before §10's bed passes; the MCP surface is a thin adapter over (2).
|
||||||
|
|
||||||
|
*Built, and then made a module — 2026-09-30.* (1) and (2) exist: `operator issue` and the `mesh`
|
||||||
|
client. What (2) describes as a program on the workstation is now the recovery path; the surface an
|
||||||
|
operator uses is a module the mesh assigns to the machine, holding a credential the mesh minted —
|
||||||
|
[ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md),
|
||||||
|
[34 — The console](34-the-console.md). The tool list it asks for is no longer
|
||||||
|
`catalog_tools`, which nothing served: each runtime answers `tools` for its own module.
|
||||||
|
|
||||||
## 8. What a module sees, and what the wire does
|
## 8. What a module sees, and what the wire does
|
||||||
|
|
||||||
**The contract a module is written against does not change.** `publish` on an envelope becomes a
|
**The contract a module is written against does not change.** `publish` on an envelope becomes a
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
---
|
---
|
||||||
layer: to-be
|
layer: to-be
|
||||||
status: proposed
|
status: in-progress
|
||||||
code: []
|
code: [mesh-controller internal/catalogue]
|
||||||
updated: 2026-09-26
|
updated: 2026-09-30
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md
|
- 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md
|
||||||
|
- 02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md
|
||||||
- 02-DECISIONS/0115-one-assignment-of-a-module-per-node.md
|
- 02-DECISIONS/0115-one-assignment-of-a-module-per-node.md
|
||||||
- 02-DECISIONS/0113-the-vault-makes-every-secret.md
|
- 02-DECISIONS/0113-the-vault-makes-every-secret.md
|
||||||
- 02-DECISIONS/0114-a-shared-credential-rotates-over-two-credentials.md
|
- 02-DECISIONS/0114-a-shared-credential-rotates-over-two-credentials.md
|
||||||
@@ -171,6 +172,15 @@ make a new external key, so rotating one means an operator handing over a new va
|
|||||||
assignment, and the route provider answers. A public name already held by another assignment is
|
assignment, and the route provider answers. A public name already held by another assignment is
|
||||||
refused, like any other singular thing.
|
refused, like any other singular thing.
|
||||||
|
|
||||||
|
**Built so far, 2026-09-30** ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)):
|
||||||
|
the operator's value in its first form — `${setting:<key>}` in a file's content, from the assignment's
|
||||||
|
settings layers, refused by name when nothing set it; a module told the name its route composes
|
||||||
|
(`${bound:<route>:name}`); a build context on the git seat; and the check that no definition names an
|
||||||
|
installation, with `names-on-purpose` for the names a definition means. The host's directory in its
|
||||||
|
first form is [`${dir:<id>}`](../../04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md),
|
||||||
|
a placed directory under the node's root. Each is this design's provider in the shape the existing
|
||||||
|
placeholders have, not yet the one requirement form below; they are phase 1's first cases.
|
||||||
|
|
||||||
## How a definition reads what was resolved
|
## How a definition reads what was resolved
|
||||||
|
|
||||||
**One form, naming a requirement and a field of its contract.** A definition that needs the database's
|
**One form, naming a requirement and a field of its contract.** A definition that needs the database's
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
layer: to-be
|
layer: to-be
|
||||||
status: designed
|
status: implemented
|
||||||
code: []
|
code: [mesh-controller, mesh-tools]
|
||||||
updated: 2026-09-28
|
updated: 2026-09-30
|
||||||
decisions:
|
decisions:
|
||||||
|
- 02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md
|
||||||
- 02-DECISIONS/0132-a-seat-carries-the-tools-its-holder-must-serve.md
|
- 02-DECISIONS/0132-a-seat-carries-the-tools-its-holder-must-serve.md
|
||||||
- 02-DECISIONS/0129-a-seat-carries-the-protocol-of-its-role.md
|
- 02-DECISIONS/0129-a-seat-carries-the-protocol-of-its-role.md
|
||||||
- 02-DECISIONS/0095-the-control-plane-is-the-way-to-ask-a-module.md
|
- 02-DECISIONS/0095-the-control-plane-is-the-way-to-ask-a-module.md
|
||||||
@@ -102,6 +103,12 @@ being something a person carries and becomes something the mesh runs, on a node,
|
|||||||
An agent's authority can then be role-shaped: *the forge's tools*, rather than a list of
|
An agent's authority can then be role-shaped: *the forge's tools*, rather than a list of
|
||||||
module-specific names that changes the day the forge is replaced.
|
module-specific names that changes the day the forge is replaced.
|
||||||
|
|
||||||
|
*Decided and designed on 2026-09-30:* the module is the console —
|
||||||
|
[ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md),
|
||||||
|
[34 — The console](34-the-console.md). It builds the second half of §5 now (a module's tools are
|
||||||
|
asked of the module, through a `tools` verb every runtime answers) and lists a role's tools when the
|
||||||
|
records carry them.
|
||||||
|
|
||||||
## 7. Versioning
|
## 7. Versioning
|
||||||
|
|
||||||
A seat's tools are an interface and change like one. Additive within a version. A change that would
|
A seat's tools are an interface and change like one. Additive within a version. A change that would
|
||||||
@@ -120,6 +127,36 @@ by side until nothing is bound to the old one.
|
|||||||
- **Two nodes holding one node-scoped seat derive two addresses.** Checked by the same test as the rest
|
- **Two nodes holding one node-scoped seat derive two addresses.** Checked by the same test as the rest
|
||||||
of the subject table.
|
of the subject table.
|
||||||
|
|
||||||
|
## What is built, 2026-09-30
|
||||||
|
|
||||||
|
Under [ADR 0154](../../02-DECISIONS/0154-the-meshs-own-verbs-are-the-controller-seats-tools.md): §2's
|
||||||
|
two constraints (the protocol in the store's row, seeded additively; a verb with description and
|
||||||
|
schema, a bare name still accepted), §3 for the mesh's seats (holding refused by naming the missing
|
||||||
|
verbs), §4 (a node-scoped seat's tool carries the node as its last subject token; a user publishes
|
||||||
|
`*`), and the third family — twelve verbs on the `mesh-controller` seat, each running the command it
|
||||||
|
names in the controller's own binary. §5's first half is served rather than read: the seat's `tools`
|
||||||
|
verb answers every seat's tools from the records, because the console cannot read the store; the
|
||||||
|
console lists a role's tools beside the modules' own and resolves `<seat>.<verb>` to the seat when the
|
||||||
|
seat declares that verb. Which verbs each *other* seat serves stays a decision per seat, still untaken.
|
||||||
|
|
||||||
|
## What shipped, 2026-09-30
|
||||||
|
|
||||||
|
mesh-controller PRs 166 and 167, mesh-tools PR 21. Verified on the live mesh the same evening: the
|
||||||
|
console on a workstation lists the twelve verbs as `mesh-controller.<verb>` beside 67 module tools, and
|
||||||
|
`mesh-controller.nodes` and `mesh-controller.status` answer through it with what the commands print.
|
||||||
|
|
||||||
|
Two things shipped bent. **The grant arrived after the holder started**: the controller composes the
|
||||||
|
bus's user list and a push delivers it, so the first controller to serve its seat subscribed before
|
||||||
|
the broker's list named the grant, the server refused all twelve subscriptions, and the client never
|
||||||
|
retried — a holder now rebinds a refused subscription every thirty seconds (PR 167), and a controller
|
||||||
|
roll-out that adds a grant is followed by a push to the broker node. **A JSON verb's answer was parsed
|
||||||
|
from both output streams**, so `status --json`'s warnings hid the document as data; the answer is now
|
||||||
|
parsed from standard output alone (mesh-controller PR 168, pending). The `output` field carried it
|
||||||
|
either way.
|
||||||
|
|
||||||
|
What stays as designed and not built: which verbs any *other* seat serves, and §3 for module-declared
|
||||||
|
seats' schemas beyond the names their manifests already list.
|
||||||
|
|
||||||
## What this does not settle
|
## What this does not settle
|
||||||
|
|
||||||
- Which verbs each seat should serve. That is a decision per seat, and the reason to do it slowly: a
|
- Which verbs each seat should serve. That is a decision per seat, and the reason to do it slowly: a
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
---
|
||||||
|
layer: to-be
|
||||||
|
status: implemented
|
||||||
|
code: [mesh-catalog, mesh-tools, mesh-controller]
|
||||||
|
updated: 2026-09-30
|
||||||
|
decisions:
|
||||||
|
- 02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md
|
||||||
|
- 02-DECISIONS/0132-a-seat-carries-the-tools-its-holder-must-serve.md
|
||||||
|
- 02-DECISIONS/0095-the-control-plane-is-the-way-to-ask-a-module.md
|
||||||
|
- 02-DECISIONS/0035-one-implementation-several-surfaces.md
|
||||||
|
- 02-DECISIONS/0034-the-local-account-owns-the-mesh.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 34 — The console
|
||||||
|
|
||||||
|
**The mesh's tools, on the machine a person sits at, served by a module the mesh assigned there.**
|
||||||
|
An agent reaches them over MCP on the machine's loopback; a person reaches the same endpoint. Nothing is
|
||||||
|
installed by hand, nothing is configured with an address, and the mesh knows the surface exists because
|
||||||
|
it put it there ([ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md)).
|
||||||
|
|
||||||
|
## 1. What it is
|
||||||
|
|
||||||
|
A module, `mesh-console`, in the catalogue. Its image is the tool runtime's own — the client that
|
||||||
|
already speaks the bus as a command line and as an MCP server — started in a mode that reads the
|
||||||
|
module's credential and listens on loopback. It has no state, no provision, no seat. What it needs is
|
||||||
|
the bus, which it gets the way every module does: a credential the mesh minted for `<node>.mesh-console`,
|
||||||
|
sealed to the machine, delivered as the module's own secret.
|
||||||
|
|
||||||
|
Its manifest says three things nothing else in the catalogue says together:
|
||||||
|
|
||||||
|
- `invokes: ["*"]` — it calls every tool on the mesh, and the bus grants exactly that publish side;
|
||||||
|
- `listens` on a port `from: machine` — the filter opens nothing for it, because loopback is not outside;
|
||||||
|
- no `emits`, no `consumes`, no `tools` — it answers nothing on the bus and nobody can address it there.
|
||||||
|
|
||||||
|
## 2. What it serves, and to whom
|
||||||
|
|
||||||
|
**One endpoint, `POST /mcp` on the machine's loopback**, speaking MCP over HTTP: `initialize`,
|
||||||
|
`tools/list`, `tools/call`. An agent on the machine is pointed at it once — the address is the machine's
|
||||||
|
own and never changes — and sees every tool the mesh can say it has. A person at a terminal uses the
|
||||||
|
same endpoint through the `mesh` client, or through anything that can make an HTTP request; the client
|
||||||
|
needs no credential, because the console holds it.
|
||||||
|
|
||||||
|
**The endpoint is the machine's login.** It binds `127.0.0.1` and nothing else. Whoever can connect is
|
||||||
|
on the machine, and whoever is on the machine is the account that owns the mesh there
|
||||||
|
([ADR 0034](../../02-DECISIONS/0034-the-local-account-owns-the-mesh.md),
|
||||||
|
[ADR 0144](../../02-DECISIONS/0144-anything-on-a-machine-may-call-anything-on-it.md)). There is no
|
||||||
|
token, no login page and no second identity, on purpose: a credential a person had to carry to reach
|
||||||
|
their own machine's console would be the arrangement this replaces, moved one hop.
|
||||||
|
|
||||||
|
## 3. How it knows what the mesh can do
|
||||||
|
|
||||||
|
Design [33](33-the-tools-the-mesh-answers.md) §5 splits discovery in two: a role's tools are read from
|
||||||
|
the mesh's records, a module's own are asked of the module. The console builds the second half now and
|
||||||
|
reads the first when it exists.
|
||||||
|
|
||||||
|
**Every tool runtime answers `tools`.** The runtime that serves a module's tools also serves one verb of
|
||||||
|
its own under that module's name, `mesh.mod.<module>.tool.tools`, answering the module's tool names,
|
||||||
|
descriptions and argument schemas — the definitions from the code that answers them, and from nowhere
|
||||||
|
else. A module may not name a tool of its own `tools`; the runtime refuses the collision at load.
|
||||||
|
|
||||||
|
**The console asks the catalogue which modules the mesh holds, then asks each.** `catalog_modules`
|
||||||
|
answers the roster; one `tools` request per module, in parallel, answers the list. The bus refuses at
|
||||||
|
once a request nothing serves, so a module that is not running costs nothing and is named in the answer
|
||||||
|
as not answering, rather than silently absent — *silence and success must never look alike*. The list is
|
||||||
|
kept for a short while and refreshed, so an agent asking on every turn does not fan out on every turn.
|
||||||
|
|
||||||
|
**A tool that was not listed can still be called.** Listing is discovery; calling is the grant. An agent
|
||||||
|
that knows a tool's name asks for it by `<module>.<tool>` and the module answers or the bus says why not.
|
||||||
|
|
||||||
|
**What is missing from the list, and until when.** A role's tools and the mesh's own verbs — `status`,
|
||||||
|
`push`, `assign` — are the `mesh-controller` seat's under ADR 0132 and are not served yet; their three
|
||||||
|
prerequisites are listed in that record. When the seat serves them, the console lists them beside the
|
||||||
|
modules' own, and the person stops opening a shell for the mesh's own questions. Until then the console
|
||||||
|
says so in its handshake.
|
||||||
|
|
||||||
|
## 4. Where it runs
|
||||||
|
|
||||||
|
On whichever machines an operator sits at, by assignment. It is not on the control node by default and
|
||||||
|
does not need to be: it reaches the bus like any module, from anywhere in the mesh. A machine that is
|
||||||
|
not a node cannot have it, which is the right refusal — the mesh reaches what it declares, and a
|
||||||
|
workstation that wants the console joins first.
|
||||||
|
|
||||||
|
The person's credential and the `mesh` client (design [25](25-the-bus-on-nats.md) §7) remain the path
|
||||||
|
for a machine that is not a node, and the path to a mesh not yet far enough along to assign anything.
|
||||||
|
|
||||||
|
## 5. Removing it
|
||||||
|
|
||||||
|
Unassigning the console from a machine revokes its bus account at the next composition and stops the
|
||||||
|
container; nothing is left on the machine that could still connect. An agent pointed at the loopback
|
||||||
|
address gets a refused connection, which is the truthful answer.
|
||||||
|
|
||||||
|
## How it is checked
|
||||||
|
|
||||||
|
| Check | Defends |
|
||||||
|
|---|---|
|
||||||
|
| a module invoking one tool may publish that subject and no other tool's; `*` may publish every one; neither may publish an event or subscribe what it did not consume | ADR 0152, the grant |
|
||||||
|
| a module registering two tools answers three names to `tools`, with schemas; a module naming its own `tools` is refused at load | ADR 0152, discovery |
|
||||||
|
| against a real bus: two modules up, a third held and not running — the console lists the two and names the third as not answering | ADR 0152, silence is not success |
|
||||||
|
| a call through the console's endpoint reaches a module over the bus and the answer is the module's own, unshaped | ADR 0035, a surface decides nothing |
|
||||||
|
| on the live mesh: the console assigned to a workstation answers `tools/list` on loopback and a call to the forge returns repositories | the exit of work-order step 3 |
|
||||||
|
| the composed filter for a machine carrying the console opens no port for it | ADR 0144 |
|
||||||
|
|
||||||
|
## What shipped, 2026-09-30
|
||||||
|
|
||||||
|
Everything above, the same day: mesh-controller PR 164 (`invokes`, `module check`), mesh-tools PR 20
|
||||||
|
(`mesh serve`, the `tools` verb), mesh-catalog PR 181 (`mesh-console`). Verified on the live mesh: the
|
||||||
|
console assigned to a workstation answered `tools/list` on its loopback with 62 tools from the modules
|
||||||
|
whose runtimes had been rebuilt to answer `tools`, named 36 modules as not answering (modules that serve
|
||||||
|
no tools, and modules whose new runtime the mesh records rather than rolls out), and a `tools/call` of
|
||||||
|
the forge's `gitea_list_repos` returned repositories. An agent on that machine reaches it as an HTTP
|
||||||
|
MCP server and reports it connected. The mesh assigned the declared port unchanged, which is what a
|
||||||
|
machine with nothing else on it does; the console binds whatever it is given.
|
||||||
|
|
||||||
|
Two things shipped bent, both stated in [`00-as-is/13-the-console.md`](../00-as-is/13-the-console.md):
|
||||||
|
the person's client through the console (`--console`) exists and was exercised in the test suite, not
|
||||||
|
on the live mesh; and a module registered from the catalogue by hand recorded its source as a URL rather
|
||||||
|
than as a path on the git seat, because `--self` takes the forge path form — the rebuild-on-merge still
|
||||||
|
matched it by URL.
|
||||||
|
|
||||||
|
## What this does not settle
|
||||||
|
|
||||||
|
- Narrowing a console's grant per assignment. ADR 0046 makes it a setting; nothing reads one yet.
|
||||||
|
- The mesh's own verbs on the bus. Design 33's third family; this document only says where they appear
|
||||||
|
once they exist.
|
||||||
|
- A person's identity behind the console. The mesh sees the console's account; design 15 keeps the
|
||||||
|
question open.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md) — the decision
|
||||||
|
- [33 — The tools the mesh answers](33-the-tools-the-mesh-answers.md) — what the console lists
|
||||||
|
- [25 — The bus on NATS](25-the-bus-on-nats.md) §7 — the person's client this makes a module of
|
||||||
|
- [issue 147](../../04-ISSUES/147-the-operators-tools-still-dial-the-bus-that-was-removed/00-report.md) — the symptom
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
layer: to-be
|
||||||
|
status: implemented
|
||||||
|
code: [mesh-catalog modules/records]
|
||||||
|
updated: 2026-09-30
|
||||||
|
decisions:
|
||||||
|
- 02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md
|
||||||
|
- 02-DECISIONS/0025-the-design-record-is-read-not-copied.md
|
||||||
|
- 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 35 — Reading the record
|
||||||
|
|
||||||
|
**The design record, answered from a checkout the mesh keeps, at the commit it read.** A module,
|
||||||
|
`records`, holds a working copy of a repository of markdown — this one, for this mesh — and answers
|
||||||
|
where a phrase appears, what a document says, what a folder holds and where the copy stands. The
|
||||||
|
console lists those answers beside every other tool
|
||||||
|
([ADR 0153](../../02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md)).
|
||||||
|
|
||||||
|
## 1. What it keeps, and why that is not a copy
|
||||||
|
|
||||||
|
A git checkout, cloned from the forge that holds the `git` seat, brought up to date on every merge the
|
||||||
|
forge announces and every ten minutes besides. The bytes are the repository's; nothing is derived
|
||||||
|
from them and stored. What [ADR 0025](../../02-DECISIONS/0025-the-design-record-is-read-not-copied.md)
|
||||||
|
refused was a second store that is *searched* while the first is *edited*, drifting silently. A
|
||||||
|
checkout cannot drift; it can lag, and the lag is in every answer as the commit it was read at and
|
||||||
|
in `records_status` as when it was last brought up to date.
|
||||||
|
|
||||||
|
The checkout is reset to the origin on every sync, never merged: it is the mesh's, so a local change
|
||||||
|
is nobody's.
|
||||||
|
|
||||||
|
## 2. What it answers
|
||||||
|
|
||||||
|
| tool | answers |
|
||||||
|
|---|---|
|
||||||
|
| `records_search` | every place a phrase appears, as written and case-insensitively: document, line, the nearest heading above it; bounded, and says when it was |
|
||||||
|
| `records_read` | one document, whole, or its first part with a note when very long |
|
||||||
|
| `records_list` | what a folder holds: sub-folders and documents |
|
||||||
|
| `records_status` | repository, forge, commit and its date, last sync, document count, last error |
|
||||||
|
| `records_sync` | bring the checkout up to date now |
|
||||||
|
|
||||||
|
No ranking and no summary, on purpose: a record is found by its own words, and the reasoning is in
|
||||||
|
the document, not in the tool.
|
||||||
|
|
||||||
|
## 3. What it is told, and what it refuses to guess
|
||||||
|
|
||||||
|
Three things, none from a manifest ([ADR 0112](../../02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md)):
|
||||||
|
the directory its checkout lives in (a resource the mesh gives it), the forge's address (the `git`
|
||||||
|
provision's binding, written into a file as `scheme://host:port`), and the repository's path on the
|
||||||
|
forge (a **setting**, `{"repository": "<owner>/<name>"}`). Without the third it serves no tools and its
|
||||||
|
log says so. Public repositories only; it holds no credential.
|
||||||
|
|
||||||
|
## 4. How it is found
|
||||||
|
|
||||||
|
The console asks every module what it serves and lists `records_search` with a description that says
|
||||||
|
when to call it — *search the literal words of a symptom or a term before forming a hypothesis*. That
|
||||||
|
is 0025's second half in today's mesh: there is no store to be beside, and an agent choosing from a
|
||||||
|
tool list is the search.
|
||||||
|
|
||||||
|
## 5. Where it runs
|
||||||
|
|
||||||
|
Anywhere a node has the forge in reach; one assignment is enough, and a second on another machine is
|
||||||
|
harmless. The mesh session of design 15, when it exists, calls this rather than reading for itself.
|
||||||
|
|
||||||
|
## How it is checked
|
||||||
|
|
||||||
|
| Check | Defends |
|
||||||
|
|---|---|
|
||||||
|
| a phrase in one document of a repository the test makes comes back from that document, with the commit; a second commit on the origin is pulled and the next answer names it | ADR 0025's check, ADR 0153 |
|
||||||
|
| a path outside the checkout is refused; an empty search is refused | the reader reads the repository and nothing else |
|
||||||
|
| a sync against an unreachable origin leaves the checkout standing and says why | silence and success never look alike |
|
||||||
|
| live: through the console, `records_search` for a phrase that appears only in a design document here returns it | issue 006's closing check |
|
||||||
|
|
||||||
|
## What shipped, 2026-09-30
|
||||||
|
|
||||||
|
mesh-catalog PR 183, then PR 185. Verified on the live mesh the same evening: `records` assigned to the
|
||||||
|
control node with `{"repository": …}` as its setting, its checkout at the repository's `main` with 478
|
||||||
|
documents, its five tools listed by the console beside every other tool, and — ADR 0025's check —
|
||||||
|
`records_search` for a phrase from this document's title returned it from where it is written, with
|
||||||
|
the commit. The first live search missed: the phrase chosen from ADR 0025 straddled a line break under
|
||||||
|
emphasis, and the reader matched single lines. PR 185 matches a line together with the next and
|
||||||
|
ignores emphasis marks, which the module's test now covers; until it rolls, a phrase that wraps is one
|
||||||
|
to shorten.
|
||||||
|
|
||||||
|
The reader's `consumes` names the forge module's event rather than the `git` seat, because the seat
|
||||||
|
declares none; a merge into the repository was seen and pulled within seconds.
|
||||||
|
|
||||||
|
## What this does not settle
|
||||||
|
|
||||||
|
- Ranking or meaning. A search that understands a question is the session's job, not the reader's.
|
||||||
|
- A private repository. That is a credential the module would have to hold, and a decision about
|
||||||
|
what may read what.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [ADR 0153](../../02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md)
|
||||||
|
- [ADR 0025](../../02-DECISIONS/0025-the-design-record-is-read-not-copied.md)
|
||||||
|
- [34 — The console](34-the-console.md) — what lists it
|
||||||
|
- [issue 006](../../04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md)
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
status: located
|
status: resolved
|
||||||
opened: 2026-08-23
|
opened: 2026-08-23
|
||||||
located-in: [hal, hq]
|
located-in: [mesh-catalog modules/records, mesh-catalog modules/mesh-console]
|
||||||
fixed-by:
|
fixed-by: ADR 0153; mesh-catalog PR 183 (records), PR 185 (a phrase that wraps)
|
||||||
amended-design: 02-DECISIONS/0025-the-design-record-is-read-not-copied.md
|
amended-design: 03-DESIGN/01-to-be/35-reading-the-record.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 006 — This repository is not indexed into the knowledge base, and the claim that it is holds up a decision
|
# 006 — This repository is not indexed into the knowledge base, and the claim that it is holds up a decision
|
||||||
@@ -169,3 +169,40 @@ them into. The record stays open, and its answer is no longer "index this reposi
|
|||||||
is whatever the mesh grows as its own knowledge surface, if it grows one. Until then the honest fix
|
is whatever the mesh grows as its own knowledge surface, if it grows one. Until then the honest fix
|
||||||
is the README, which should stop claiming a property nothing provides.
|
is the README, which should stop claiming a property nothing provides.
|
||||||
|
|
||||||
|
|
||||||
|
## Where this stands, 2026-09-30
|
||||||
|
|
||||||
|
The README no longer claims a property nothing provides: it says the indexing never existed, that the
|
||||||
|
store it named is unreachable since the cut-over, and that ADR 0025's answer — read, not copied, by an
|
||||||
|
agent that consults this repository — is decided and not built. That was the honest fix the previous
|
||||||
|
note asked for, and it is done.
|
||||||
|
|
||||||
|
The record stays open on ADR 0025's build, and on nothing else. The mesh's own operator surface is now
|
||||||
|
a module ([ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md)); the
|
||||||
|
reader ADR 0025 describes is the mesh session of design 15, which would answer through that surface
|
||||||
|
like any tool. What closes this is still the check 0025 names: search for a phrase that appears only in
|
||||||
|
a design document here, and get it back.
|
||||||
|
|
||||||
|
## Built, 2026-09-30
|
||||||
|
|
||||||
|
[ADR 0153](../../02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md): the
|
||||||
|
reader is a module, `records` (mesh-catalog PR 183), keeping a checkout of this repository from the
|
||||||
|
forge and answering `records_search`, `records_read`, `records_list`, `records_status` and
|
||||||
|
`records_sync` at the commit it read; the console lists them beside every other tool, which is where
|
||||||
|
"beside everything else" lives in a mesh with no store. Design
|
||||||
|
[35 — Reading the record](../../03-DESIGN/01-to-be/35-reading-the-record.md). The module's test runs
|
||||||
|
0025's check against a repository it makes; this record closes when the same check passes through the
|
||||||
|
console on the live mesh, and says so below.
|
||||||
|
|
||||||
|
## Resolved, 2026-09-30
|
||||||
|
|
||||||
|
The check ADR 0025 names passed on the live mesh: through the console on a workstation,
|
||||||
|
`records_search` for a phrase that appears in one design document here returned that document and the
|
||||||
|
commit it was read at, from a checkout the mesh keeps and nobody copied. What this record asked on
|
||||||
|
2026-08-23 — *does the searcher find it without already suspecting it exists?* — is answered by where
|
||||||
|
the tool sits: in the same list as the forge's and the mesh's own, described as the thing to search
|
||||||
|
before forming a hypothesis. Reachable became surfacing when the surface became a list.
|
||||||
|
|
||||||
|
Open beside it, and not this record's: the mesh has no symptom-indexed memory at all since the
|
||||||
|
cut-over ([as-is 07](../../03-DESIGN/00-as-is/07-knowledge.md) says so), and the lessons of these
|
||||||
|
days are in this repository by hand.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
status: located
|
status: resolved
|
||||||
opened: 2026-09-26
|
opened: 2026-09-26
|
||||||
located-in: [mesh-controller internal/catalogue/declaration.go, mesh-catalog modules/keycloak, mesh-catalog modules/minio, mesh-catalog modules/nextcloud]
|
located-in: [mesh-controller internal/catalogue/declaration.go, mesh-catalog modules/keycloak, mesh-catalog modules/minio, mesh-catalog modules/nextcloud]
|
||||||
fixed-by:
|
fixed-by: mesh-controller PR 149 (a module is told the name it is served under), PR 169 (an operator's value, a context on the seat); mesh-catalog PR 188; ADR 0155
|
||||||
amended-design:
|
amended-design: 03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 122 — A module cannot ask for its own public name, so three manifests wrote this mesh's names into the catalogue
|
# 122 — A module cannot ask for its own public name, so three manifests wrote this mesh's names into the catalogue
|
||||||
@@ -114,3 +114,18 @@ particular to one installation, and also has nowhere to live but the definition.
|
|||||||
ADR 0112 would put it), and if so what reads it — the provisioner, or the module's own values?
|
ADR 0112 would put it), and if so what reads it — the provisioner, or the module's own values?
|
||||||
- What check would notice the next one? A definition naming a public domain is detectable in the
|
- What check would notice the next one? A definition naming a public domain is detectable in the
|
||||||
shape of the value, which is more than nothing, and less than a rule.
|
shape of the value, which is more than nothing, and less than a rule.
|
||||||
|
|
||||||
|
## Resolved, 2026-09-30
|
||||||
|
|
||||||
|
The open questions, answered in order. **A module names what it will be reached at** through the
|
||||||
|
binding of the route it contributes: `${bound:route:name}`, or `:name-<local>` for several
|
||||||
|
contributions, is the composed public name; `:internal-name` the private one (controller PR 149). The
|
||||||
|
identity provider, the object store's console and the automation tool's webhook now read it there.
|
||||||
|
**The scheme is the module's**, because it is true of the route the proxy terminates and every instance
|
||||||
|
wrote `https://` in front of the name. **An adopted resource's name is a setting** on the assignment;
|
||||||
|
so is every other operator's value — a mail domain, a site name, the address a proxy forwards from —
|
||||||
|
as `${setting:<key>}` in the file the software reads
|
||||||
|
([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)).
|
||||||
|
**The check that notices the next one** is the shape of the value, as this record guessed it would be,
|
||||||
|
and it is more than nothing: it found forty-two, and the catalogue passes it now
|
||||||
|
([issue 134](../134-a-definition-may-still-name-the-mesh/00-report.md)).
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
status: open
|
status: resolved
|
||||||
opened: 2026-09-28
|
opened: 2026-09-28
|
||||||
located-in: [mesh-catalog, mesh-controller internal/catalogue]
|
located-in: [mesh-catalog, mesh-controller internal/catalogue]
|
||||||
fixed-by:
|
fixed-by: mesh-controller PR 169 (the check, module check, the catalogue-wide test); mesh-catalog PR 188 (the catalogue that passes it); ADR 0155
|
||||||
amended-design:
|
amended-design: 03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 134 — A definition may still name the mesh, and the check that would say so does not exist
|
# 134 — A definition may still name the mesh, and the check that would say so does not exist
|
||||||
@@ -64,3 +64,17 @@ that.
|
|||||||
hostnames above are the first real cases.
|
hostnames above are the first real cases.
|
||||||
- Should a build context name a repository on the git seat rather than by URL, and if so, what does
|
- Should a build context name a repository on the git seat rather than by URL, and if so, what does
|
||||||
that mean for a context in *another* mesh's forge?
|
that mean for a context in *another* mesh's forge?
|
||||||
|
|
||||||
|
## Resolved, 2026-09-30
|
||||||
|
|
||||||
|
The check exists: `InstallationProblems`, run by `module check` and by a catalogue-wide test. Run over
|
||||||
|
the 77 definitions it found 42 values, not 15 — the by-hand count had missed a second name one
|
||||||
|
character after the first on the same line, which is the kind of thing a check is for. The three open
|
||||||
|
questions: **a domain in a `why` string does not break the rule**, prose is not judged, and the eight
|
||||||
|
were rewritten anyway because this catalogue is public; **a service's public name is the name the mesh
|
||||||
|
composes for its route**, read through the route's binding, and an operator's own value is a setting;
|
||||||
|
**a build context names a repository on the git seat**, `seat: git` with the path, and a context in
|
||||||
|
another mesh's forge stays a URL, which the check reports and `names-on-purpose` would declare. The
|
||||||
|
seven values that remain are declared with their reason — four applications built outside the mesh —
|
||||||
|
and are the list that shrinks ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)).
|
||||||
|
Registration does not refuse yet; it will when the list has been empty for a release.
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
status: located
|
status: resolved
|
||||||
opened: 2026-09-29
|
opened: 2026-09-29
|
||||||
located-in: [nothing in the mesh — the surface in use is the predecessor's, installed on the workstation]
|
located-in: [mesh-catalog modules/mesh-console, mesh-tools src/mesh.ts, mesh-controller internal/broker]
|
||||||
|
fixed-by: ADR 0152; mesh-controller PR 164 (invokes); mesh-tools PR 20 (mesh serve, the tools verb); mesh-catalog PR 181 (mesh-console)
|
||||||
|
amended-design: 03-DESIGN/01-to-be/34-the-console.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 147 — the operator's tools still dial the bus that was removed
|
# 147 — the operator's tools still dial the bus that was removed
|
||||||
@@ -73,3 +75,28 @@ outside the mesh.
|
|||||||
- It fails identically for the local machine, which rules out reachability and points at the
|
- It fails identically for the local machine, which rules out reachability and points at the
|
||||||
transport alone.
|
transport alone.
|
||||||
- The mesh's own traffic over the new bus is unaffected: nodes report, declarations apply.
|
- The mesh's own traffic over the new bus is unaffected: nodes report, declarations apply.
|
||||||
|
|
||||||
|
## Answered, 2026-09-30
|
||||||
|
|
||||||
|
The work order's question — does the mesh grow its own operator surface, or is the surface an
|
||||||
|
ordinary module — is answered by [ADR 0152](../../02-DECISIONS/0152-the-operators-surface-is-a-module-the-console.md):
|
||||||
|
**the console is a module.** `mesh-console` is assigned to the machine a person sits at, holds a
|
||||||
|
credential the mesh minted, calls tools under a grant its manifest declares (`invokes`), and serves the
|
||||||
|
mesh's tools on that machine's loopback to an agent over MCP and to a person through the same endpoint.
|
||||||
|
Designed in [34 — The console](../../03-DESIGN/01-to-be/34-the-console.md).
|
||||||
|
|
||||||
|
What that leaves, said plainly so nobody reads this record as closed on the whole of its first
|
||||||
|
paragraph: the console reaches every tool a *module* serves. The mesh's own questions — what a node
|
||||||
|
runs, what is assigned — are the `mesh-controller` seat's tools under ADR 0132 and are not on the bus
|
||||||
|
yet; for those a shell is still the way, and design 33 is where that closes.
|
||||||
|
|
||||||
|
The predecessor's program on the workstation is not replaced by the mesh; it is left where it is and
|
||||||
|
the assistant is pointed at the console beside it. The `hal` entry in the assistant's configuration
|
||||||
|
still names things that are not the mesh's.
|
||||||
|
|
||||||
|
**Verified live, 2026-09-30 evening.** The four pull requests merged; the console was registered
|
||||||
|
(checked first with `module check`), built, assigned to a workstation, issued a bus account, and pushed.
|
||||||
|
On that machine `tools/list` answered on loopback with 62 tools and named 36 modules as not answering,
|
||||||
|
and a call to the forge's `gitea_list_repos` returned repositories. The assistant on that machine now
|
||||||
|
lists the console as a connected MCP server beside the predecessor's program, which was left where it
|
||||||
|
is. As-is: [`13-the-console.md`](../../03-DESIGN/00-as-is/13-the-console.md).
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
---
|
---
|
||||||
status: open
|
status: resolved
|
||||||
opened: 2026-09-29
|
opened: 2026-09-29
|
||||||
located-in: [mesh-controller cmd/mesh-controller]
|
located-in: [mesh-controller cmd/mesh-controller, mesh-controller internal/catalogue]
|
||||||
|
fixed-by: mesh-controller PR 164 (module check)
|
||||||
|
amended-design: 03-DESIGN/01-to-be/12-a-module-repository.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 148 — a manifest outside this catalogue has no check
|
# 148 — a manifest outside this catalogue has no check
|
||||||
@@ -35,3 +37,14 @@ Nothing prevents this; it was noticed and left. ADR 0037 named it on 2026-09-01
|
|||||||
take a path from `MESH_CATALOG`, so the mechanism is already path-driven and not repository-bound.
|
take a path from `MESH_CATALOG`, so the mechanism is already path-driven and not repository-bound.
|
||||||
- `mesh-controller module add` refuses a bad manifest at registration, which is the same check far
|
- `mesh-controller module add` refuses a bad manifest at registration, which is the same check far
|
||||||
too late: by then it is in a running mesh's records.
|
too late: by then it is in a running mesh's records.
|
||||||
|
|
||||||
|
## Built, 2026-09-30
|
||||||
|
|
||||||
|
`mesh-controller module check <manifest>…` runs what registration runs — the strict parse, the
|
||||||
|
per-manifest problems, and the cross-manifest rules over every manifest given — with no store and no
|
||||||
|
mesh, prints every problem in the manifest's words, and exits non-zero on any. What it cannot judge
|
||||||
|
without a store it says: a claim on one of the mesh's own seats is judged fully only at registration
|
||||||
|
([ADR 0122](../../02-DECISIONS/0122-a-seat-is-data-a-rename-is-a-database-update.md)), and a seat
|
||||||
|
declared by a module whose manifest was not passed reads as unknown. Written into
|
||||||
|
[12 — A module repository](../../03-DESIGN/01-to-be/12-a-module-repository.md) as the section *a
|
||||||
|
manifest is checked where it is written*. The console's own manifest was the first checked with it.
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
status: resolved
|
||||||
|
opened: 2026-09-30
|
||||||
|
located-in:
|
||||||
|
- mesh-catalog modules/mailu (eight containers name Mailu's own resolver, and one of them binds a mesh name)
|
||||||
|
- mesh-catalog modules/dnsmasq (dropped the DNSSEC bit its upstreams set)
|
||||||
|
fixed-by: mesh-catalog PR 178 (mailu-admin uses the machine's resolver) and PR 179 (the machine's resolver passes the DNSSEC bit down) — 2026-09-30, the same afternoon
|
||||||
|
amended-design:
|
||||||
|
---
|
||||||
|
|
||||||
|
# 171 — A module that names its own resolver knows no mesh name
|
||||||
|
|
||||||
|
## What was observed
|
||||||
|
|
||||||
|
The afternoon [ADR 0148](../../02-DECISIONS/0148-the-meshs-names-are-resolved-not-copied-into-containers.md)
|
||||||
|
landed — no container is given the mesh's names any more; it asks the machine's resolver — the mail
|
||||||
|
system's admin container began logging, 523 times in three minutes:
|
||||||
|
|
||||||
|
```
|
||||||
|
psycopg2.OperationalError: could not translate host name "novox.internal" to address: Name does not resolve
|
||||||
|
```
|
||||||
|
|
||||||
|
Mail was accepted on every port and the web front answered; the admin and the spam filter beside it
|
||||||
|
were unhealthy, and anything that needed the database — a mailbox change through the API, the spam
|
||||||
|
filter's domain list — failed. Found by the operator asking whether mail was back, forty minutes in.
|
||||||
|
|
||||||
|
Mailu ships its own resolver, an unbound in a container, and every other Mailu container is told to
|
||||||
|
use it — the module carries `dns: [192.168.203.254]` on eight containers. That resolver recurses from the
|
||||||
|
root and knows nothing under `.internal`. Until that afternoon the admin container had the database's
|
||||||
|
name anyway, because the mesh wrote every name into every container at creation; the copy was the only
|
||||||
|
reason a container behind its own resolver could reach anything by a mesh name, and nobody knew it was
|
||||||
|
load-bearing.
|
||||||
|
|
||||||
|
**Removing the override was not enough.** Given the machine's resolver instead, the admin refused to
|
||||||
|
start: `Your DNS resolver at 127.0.0.11 isn't doing DNSSEC validation`. Mailu checks, at start, that
|
||||||
|
its resolver returns the Authenticated Data bit for a signed name. The mesh's resolver forwards to two
|
||||||
|
upstreams that validate and set the bit, and dropped it on the way down — dnsmasq does unless told
|
||||||
|
otherwise. Mailu's own unbound has no hook to forward a zone elsewhere, so it could not be taught the
|
||||||
|
mesh's names either.
|
||||||
|
|
||||||
|
## Why it matters beyond this instance
|
||||||
|
|
||||||
|
**A container with a resolver of its own has opted out of the machine's, and nothing says so.** 0148
|
||||||
|
made the machine's resolver load-bearing for every container; a `dns` on a container is a quiet
|
||||||
|
exception to that, and the exception used to be papered over by the copy the record removed. The
|
||||||
|
manifest field reads like a preference and is a decision about whether mesh names exist inside the
|
||||||
|
container.
|
||||||
|
|
||||||
|
**A resolver that forwards to validating upstreams and hides the fact is less useful than it could
|
||||||
|
be**, and the first program to check found out.
|
||||||
|
|
||||||
|
**The mesh reported nothing.** Every container ran; the failing one accepted connections; the report
|
||||||
|
was about bytes. It is [issue 145](../145-a-machine-reads-healthy-while-its-modules-cannot-reach-each-other/00-report.md)
|
||||||
|
again, and the check that would have caught it is the same unbuilt one.
|
||||||
|
|
||||||
|
## What was done
|
||||||
|
|
||||||
|
- The one Mailu container that binds a mesh name — the admin, through the database it is granted —
|
||||||
|
no longer names Mailu's resolver and uses the machine's, like every container without a `dns` of
|
||||||
|
its own (mesh-catalog PR 178). The other seven keep unbound: the spam filter needs a validating
|
||||||
|
resolver for its blocklist lookups, and none of them asks for a mesh name.
|
||||||
|
- The machine's resolver passes the DNSSEC bit down from its upstreams, `proxy-dnssec` (PR 179). It
|
||||||
|
does not validate itself; the trust is the upstream's and the path to it, as a forwarding resolver's
|
||||||
|
always was, and the configuration says so.
|
||||||
|
|
||||||
|
## What checks it
|
||||||
|
|
||||||
|
The admin container's own start-up check, which is what failed, and the mesh's status once it reads
|
||||||
|
healthy. A container-level check that a mesh name resolves from inside every declared container is
|
||||||
|
the one 110 and 145 both ask for and is not built.
|
||||||
|
|
||||||
|
## Open questions
|
||||||
|
|
||||||
|
- Should a container's `dns` be refused, or made to say what it gives up? A module that names its
|
||||||
|
own resolver and binds a mesh name is a contradiction the controller can see at composition — the
|
||||||
|
grant hands it a name its resolver will not answer.
|
||||||
|
- Should the machine's resolver validate rather than proxy? It would cost a trust anchor on every
|
||||||
|
machine and make the resolver slower to start; proxying was enough for the one program that asked.
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
status: located
|
||||||
|
opened: 2026-09-30
|
||||||
|
located-in:
|
||||||
|
- the predecessor's terminal module (still generating the operator's ssh client blocks on every workstation)
|
||||||
|
- mesh-controller internal/catalogue (the ssh-client roster, tested and not yet a catalogue module)
|
||||||
|
fixed-by:
|
||||||
|
amended-design:
|
||||||
|
---
|
||||||
|
|
||||||
|
# 172 — The ssh client block for a machine matches one spelling of its name, and the other gets the wrong user
|
||||||
|
|
||||||
|
## What was observed
|
||||||
|
|
||||||
|
On a workstation, 2026-09-30, reported by the operator. `ssh home-server` logs in; `ssh
|
||||||
|
home-server.internal` is refused with `Permission denied (publickey)`. The operator expected the
|
||||||
|
opposite, if either: the full mesh name is the one the resolver serves.
|
||||||
|
|
||||||
|
The name is not the fault. Both spellings resolve to the machine's private address — the mesh's roster
|
||||||
|
region in the hosts file carries `<node>.internal <node>` on one line, and the resolver answers
|
||||||
|
anything under the node's name. What differs is the login: the generated client configuration has a
|
||||||
|
`Host home-server` block naming the account to log in as, and `home-server.internal` matches no block,
|
||||||
|
so ssh falls back to the operator's local username, which has no account on that machine. Spelled
|
||||||
|
`account@home-server.internal` it works.
|
||||||
|
|
||||||
|
The file is the predecessor's. `~/.ssh/config.d/mesh` says in its own header that it is generated by
|
||||||
|
the predecessor's terminal module, which only ever wrote the bare name. The mesh's own ssh-client
|
||||||
|
roster — every other machine's Host block, written as a marked region of the operator's `~/.ssh/config`
|
||||||
|
with the account the mesh knows for that machine (to-be 29) — already matches both spellings, and a
|
||||||
|
controller test holds `Host marge marge.internal`. It is composed and tested in the controller and is
|
||||||
|
not a module in the catalogue, so no machine receives it; every workstation still runs the
|
||||||
|
predecessor's generator.
|
||||||
|
|
||||||
|
## Why it matters beyond this instance
|
||||||
|
|
||||||
|
**A name the mesh serves and a name a person can use are not the same set**, and the difference is
|
||||||
|
silent. The resolver, the hosts file and the certificate authority all treat `<node>.internal` as the
|
||||||
|
machine's name; the one file that decides who you log in as does not know it. A person who learns the
|
||||||
|
mesh's name from `status` or from a certificate and types it is refused with an error that says
|
||||||
|
nothing about a missing Host block.
|
||||||
|
|
||||||
|
**It is the migration story for the operator's own tooling, arriving as a symptom.** The mesh has the
|
||||||
|
right file and does not ship it. Until the ssh-client roster is a module and is assigned to the
|
||||||
|
workstations, the predecessor's generator keeps writing a file the mesh has already superseded, and
|
||||||
|
every such file is one the mesh cannot correct.
|
||||||
|
|
||||||
|
## Open questions
|
||||||
|
|
||||||
|
- Should the ssh-client roster become a catalogue module now, assigned to every workstation, and take
|
||||||
|
the predecessor's `config.d/mesh` out of the operator's `Include`? Its content is settled; what is
|
||||||
|
not is the takeover of a file in a person's home that another generator still writes.
|
||||||
|
- Should the block match a third spelling — the machine's public name, where it has one — or is that
|
||||||
|
a different key and a different account?
|
||||||
|
- What checks it? A controller test holds the two spellings; nothing checks that the file a workstation
|
||||||
|
actually has is the mesh's rather than the predecessor's.
|
||||||
@@ -87,12 +87,15 @@ rather than location** — that these documents would be indexed into the knowle
|
|||||||
symptom search returns them beside everything else. One source, many surfaces. Where the source
|
symptom search returns them beside everything else. One source, many surfaces. Where the source
|
||||||
is authored is then a separate question.
|
is authored is then a separate question.
|
||||||
|
|
||||||
**That indexing does not exist.** It was checked on 2026-08-23 and returns nothing; it appears
|
**That indexing never existed, and the store it would have indexed into is gone.** It was checked
|
||||||
never to have existed. Until it does, the objection stands unanswered and this repository is
|
on 2026-08-23 and returned nothing; the knowledge base it named was the predecessor's, and since the
|
||||||
the fourth knowledge system it was argued not to be. Recorded as
|
mesh moved to its own bus on 2026-09-28 nothing can reach it at all. The claim is recorded as
|
||||||
[`04-ISSUES/006`](04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md), and
|
[`04-ISSUES/006`](04-ISSUES/006-hq-is-not-indexed-into-the-knowledge-base/00-report.md) and answered
|
||||||
left standing here rather than quietly reworded, because a claim that held up a decision and
|
by [ADR 0025](02-DECISIONS/0025-the-design-record-is-read-not-copied.md): these documents are
|
||||||
was never checked is precisely the failure this repository exists to name.
|
**read, not copied** — an agent reads this repository and a search consults it — and that agent is
|
||||||
|
not built. So today this repository is reachable by whoever knows to open it and surfaces to nobody
|
||||||
|
else. Said here rather than quietly reworded, because a claim that held up a decision and was never
|
||||||
|
checked is precisely the failure this repository exists to name.
|
||||||
|
|
||||||
Answered separately, a repository of its own is the better home:
|
Answered separately, a repository of its own is the better home:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user