Order the records the way the system is learned
Jochen asked whether the order made sense. It did not -- it followed when things happened to be decided, which after consolidation is fictional anyway since record 5 alone folds decisions taken across a week. Concretely wrong before: the domain statement sat at 8, after five engineering rules; the constitution was scattered across 5, 12 and 17; the tiers landed at 15, 16, 21 and 22 with process records in between. Now it walks: what the mesh is (1-3), its tiers from the bottom up (4-8), what runs on them and how it gets there (9-10), how it is built (11-16), how it is checked (17-18), how we work (19-23). Two things made this safe rather than free. It is a permutation, not a compaction, so the renames go through temporary names -- otherwise two files want one slot and one is lost. And the reference rewrite is a single simultaneous pass, because almost every number moved into a slot another number was vacating; replacing one at a time would have cascaded and pointed things at the wrong record while still resolving. Verified: 284 [ADR NNNN](path) links across the repository, all with matching text and target. The ordering principle is now stated in 19 rather than left implicit -- the repository already said "the numbering is the flow" about its folders, and there was no reason for the records to be the exception.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
status: active
|
||||
initiated: 2026-08-22
|
||||
touches: [03-DESIGN/00-as-is/02-modules-and-manifests.md, 03-DESIGN/00-as-is/10-module-catalogue.md, 03-DESIGN/01-to-be/00-work-breakdown.md]
|
||||
became: [02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md]
|
||||
became: [02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md]
|
||||
---
|
||||
|
||||
# 001 — Module domain decomposition
|
||||
@@ -54,7 +54,7 @@ Tracked in [`analysis.md`](analysis.md) under "Open questions".
|
||||
## Deliberately not decided
|
||||
|
||||
Recorded so they are not mistaken for oversights. Each is open, and each comes out of
|
||||
[ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md); this effort stays
|
||||
[ADR 0001](../../02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md); this effort stays
|
||||
`active` until they are answered.
|
||||
|
||||
| Question | Status |
|
||||
@@ -64,4 +64,4 @@ Recorded so they are not mistaken for oversights. Each is open, and each comes o
|
||||
| Catalogue destination — one repository or many. | Open. Phase 4. |
|
||||
| What the shared library keeps after extraction. | Open. Phase 3. |
|
||||
| Where human agent modality is recorded — which user, on which node, a human agent acts as. | Open. Required by the model; not yet stored. |
|
||||
| Which domains the modules outside the platform core group into. | Open, from [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md), which settles the principle and deliberately not the list. |
|
||||
| Which domains the modules outside the platform core group into. | Open, from [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md), which settles the principle and deliberately not the list. |
|
||||
|
||||
@@ -237,6 +237,6 @@ returns. That mechanism is the subject of a separate ADR.
|
||||
pipeline resolves dependencies across the registry rather than the filesystem?
|
||||
4. **SDK residue** — after extraction, does `hal/sdk` keep transport (`amqp-client`), or
|
||||
does that belong to `hal/stream`? Everything imports it, which argues both ways.
|
||||
5. **Human agent modality.** ADR 0008 requires a fact the mesh does not record: which
|
||||
5. **Human agent modality.** ADR 0001 requires a fact the mesh does not record: which
|
||||
user, on which node, a human agent acts as. Where does it live — an attribute of the
|
||||
agent, or of the agent-node binding?
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
status: graduated
|
||||
initiated: 2026-08-22
|
||||
touches: [03-DESIGN/00-as-is/04-delivery.md, 03-DESIGN/00-as-is/05-runtime-and-installation.md]
|
||||
became: [03-DESIGN/01-to-be/01-end-to-end-testing.md, 02-DECISIONS/0009-the-lab.md]
|
||||
became: [03-DESIGN/01-to-be/01-end-to-end-testing.md, 02-DECISIONS/0016-the-lab.md]
|
||||
---
|
||||
|
||||
# 002 — A mesh that runs locally
|
||||
@@ -24,7 +24,7 @@ This effort establishes what already runs in a container, what is welded to the
|
||||
what it would take to close the gap. It does **not** choose an approach: the central
|
||||
question — how a containerised node executes a module service, when a module service is
|
||||
defined today as a systemd unit shelling to `docker compose` in `/services/` — is not
|
||||
answered by ADR 0008 and is recorded below rather than decided.
|
||||
answered by ADR 0001 and is recorded below rather than decided.
|
||||
|
||||
## What was established
|
||||
|
||||
|
||||
@@ -253,7 +253,7 @@ over either way.
|
||||
|
||||
## References
|
||||
|
||||
- [`02-DECISIONS/0001`](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md) — the decision this
|
||||
- [`02-DECISIONS/0001`](../../02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md) — the decision this
|
||||
phase unblocks
|
||||
- [`03-DESIGN/00-work-breakdown.md`](../../03-DESIGN/01-to-be/00-work-breakdown.md) — Phase 0 tasks
|
||||
and checkpoint
|
||||
|
||||
@@ -3,8 +3,8 @@ status: graduated
|
||||
initiated: 2026-08-22
|
||||
touches: [03-DESIGN/00-as-is/05-runtime-and-installation.md]
|
||||
became:
|
||||
- 02-DECISIONS/0016-the-node-host.md
|
||||
- 02-DECISIONS/0016-the-node-host.md
|
||||
- 02-DECISIONS/0005-the-node-host.md
|
||||
- 02-DECISIONS/0005-the-node-host.md
|
||||
- 03-DESIGN/01-to-be/05-the-node-host.md
|
||||
---
|
||||
|
||||
@@ -43,7 +43,7 @@ This effort answers the cost half. It does not choose.
|
||||
- **There is a third option neither of us named**, and it is the one that also solves Phase 0:
|
||||
run HAL's own daemons as containers, making Docker the supervisor for everything. Local and
|
||||
production then have the same shape rather than a translation layer between them.
|
||||
- **It cannot be all-or-nothing**, and ADR 0008 already says why: a human agent acts through a
|
||||
- **It cannot be all-or-nothing**, and ADR 0001 already says why: a human agent acts through a
|
||||
shell and a desktop. Those parts are on the host by definition.
|
||||
- One incidental finding: the automatic node rescue that documentation describes **does not
|
||||
exist**. No unit declares `OnFailure=`, and nothing calls `hal-rescue.sh` on a timer.
|
||||
@@ -57,14 +57,14 @@ which is why the effort sat `active` for five days after being answered. Recorde
|
||||
finding that is the point of a sweep.
|
||||
|
||||
**The third option is what the mesh adopted.** `Docker is the supervisor for everything` is
|
||||
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md): the
|
||||
[ADR 0005](../../02-DECISIONS/0005-the-node-host.md): the
|
||||
host is a plain process on the machine and everything above tier 0 is a container. The substrate
|
||||
bootstrap declares no service at all — it is package, container, action, container — so the
|
||||
44-of-44 restart policies this effort counted are the supervision, exactly as it argued.
|
||||
|
||||
**Fate-sharing was the hard part, and it is solved the way this effort predicted.** It said any
|
||||
mesh-native supervisor inherits the problem *unless it sits outside the mesh's own process
|
||||
tree*. [ADR 0016](../../02-DECISIONS/0016-the-node-host.md) puts
|
||||
tree*. [ADR 0005](../../02-DECISIONS/0005-the-node-host.md) puts
|
||||
the launcher there: it supervises the host as a child and shares no code with it, so a host that
|
||||
cannot start is still recovered.
|
||||
|
||||
|
||||
@@ -128,10 +128,10 @@ What it costs, honestly:
|
||||
|
||||
---
|
||||
|
||||
## 5. Why it cannot be all-or-nothing — and ADR 0008 already says so
|
||||
## 5. Why it cannot be all-or-nothing — and ADR 0001 already says so
|
||||
|
||||
Some of what runs under systemd today **cannot** be containerised, and the reason is
|
||||
already in the domain model. ADR 0008:
|
||||
already in the domain model. ADR 0001:
|
||||
|
||||
> a non-human agent acts through a spawned session — a human agent acts through a shell or
|
||||
> desktop
|
||||
@@ -223,7 +223,7 @@ fate-sharing reason in §3.
|
||||
## References
|
||||
|
||||
- [`002-local-mesh`](../002-local-mesh/analysis.md) — the effort this came out of
|
||||
- [`02-DECISIONS/0001`](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md) — agent modality, which
|
||||
- [`02-DECISIONS/0001`](../../02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md) — agent modality, which
|
||||
decides what cannot leave the host
|
||||
- `modules/hal/meshware/daemon/src/cerebellum.ts:815-828` — the self-restart workaround
|
||||
- `modules/hal/meshware/systemd/hal-module@.service` — the per-module Docker lifecycle
|
||||
|
||||
@@ -3,9 +3,9 @@ status: graduated
|
||||
initiated: 2026-08-22
|
||||
touches: [03-DESIGN/00-as-is/01-mesh-and-transport.md, 03-DESIGN/01-to-be/01-end-to-end-testing.md]
|
||||
became:
|
||||
- 02-DECISIONS/0009-the-lab.md
|
||||
- 02-DECISIONS/0009-the-lab.md
|
||||
- 02-DECISIONS/0009-the-lab.md
|
||||
- 02-DECISIONS/0016-the-lab.md
|
||||
- 02-DECISIONS/0016-the-lab.md
|
||||
- 02-DECISIONS/0016-the-lab.md
|
||||
- 03-DESIGN/01-to-be/02-scenario-declaration.md
|
||||
- 03-DESIGN/01-to-be/01-end-to-end-testing.md
|
||||
---
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
status: graduated
|
||||
initiated: 2026-08-23
|
||||
touches:
|
||||
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0009-modules-and-the-graph.md
|
||||
- 03-DESIGN/00-as-is/10-module-catalogue.md
|
||||
- 03-DESIGN/00-as-is/02-modules-and-manifests.md
|
||||
became:
|
||||
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0009-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0009-modules-and-the-graph.md
|
||||
---
|
||||
|
||||
# 005 — Which domains the catalogue groups into
|
||||
|
||||
[ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md) settles
|
||||
[ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md) settles
|
||||
that modules outside the platform core are grouped by domain rather than by single function,
|
||||
and deliberately does not settle the list. This effort settles the list — and, first, tests
|
||||
whether the premise survives measurement.
|
||||
@@ -25,7 +25,7 @@ together**, measured across the full history of the code repository.
|
||||
|
||||
## Why
|
||||
|
||||
The argument in ADR 0017 is that the catalogue's shape records what was installed rather than
|
||||
The argument in ADR 0022 is that the catalogue's shape records what was installed rather than
|
||||
what anything is for — that four modules constituting "how a node is reachable" have no
|
||||
relationship the mesh can see, so a change to connectivity is made four times.
|
||||
|
||||
@@ -59,12 +59,12 @@ open questions below.
|
||||
this effort — which is why it stayed open after being resolved.
|
||||
|
||||
**Whether provider modules group at all** — *no.*
|
||||
[ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md):
|
||||
[ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md):
|
||||
there is no `networking` thing to install, there are concrete modules named individually. Folders
|
||||
assert relationships; edges record them. *Provider* stops being a category at the same time.
|
||||
|
||||
**Whether "group or leave" is even the right pair of options** — *it was not*, and that is the
|
||||
useful finding. [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)
|
||||
useful finding. [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md)
|
||||
reframes it: things that change together share an **authority**, not a package. This effort's own
|
||||
measurement is what that record rests on — reachability being the *only* place modules genuinely
|
||||
co-change is why connectivity is a context and why nothing else needed one.
|
||||
|
||||
@@ -10,7 +10,7 @@ updated: 2026-08-23
|
||||
Every commit in the code repository's main branch that touches the module catalogue, reduced
|
||||
to the set of modules it touched. Platform-namespace modules are excluded — their
|
||||
decomposition is settled by
|
||||
[ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md). Modules that no
|
||||
[ADR 0001](../../02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md). Modules that no
|
||||
longer exist are excluded, because pre-rename names dominate the raw signal and describe a
|
||||
catalogue nobody works in.
|
||||
|
||||
@@ -95,14 +95,14 @@ remainder are genuine:
|
||||
| 2026-08-06 | firewall mesh-only by default, public by declaration |
|
||||
|
||||
Each is one intent — *change how a node is reachable* — landing across the proxy, the
|
||||
resolver, the firewall and the VPN together. That is exactly the shape ADR 0017 describes, and
|
||||
resolver, the firewall and the VPN together. That is exactly the shape ADR 0022 describes, and
|
||||
it is the only place in the catalogue where the measurement finds it.
|
||||
|
||||
The 2026-08-23 scoping commit is the sharpest case: it spans the reachability cluster **and**
|
||||
two providers, because "which network is this exposed on" is a reachability question asked of
|
||||
a database.
|
||||
|
||||
## What this means for ADR 0017
|
||||
## What this means for ADR 0022
|
||||
|
||||
The record's principle stands, and its scope needs narrowing. Grouping by domain is:
|
||||
|
||||
@@ -130,7 +130,7 @@ Asked directly, and stated as an opinion because it is not yet decided.
|
||||
is implementation selection, a substantially larger design with its own failure modes, and
|
||||
nothing currently asks for it.
|
||||
3. **It would hide which implementation serves a requirement** — the one place the mesh most
|
||||
needs to be explicit, and precisely the indirection ADR 0017 warns grouping causes.
|
||||
needs to be explicit, and precisely the indirection ADR 0022 warns grouping causes.
|
||||
|
||||
A provider module is already exactly one purpose: it provisions one resource type. That is a
|
||||
boundary, not an accident of installation.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
status: active
|
||||
initiated: 2026-08-23
|
||||
touches:
|
||||
- 02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md
|
||||
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md
|
||||
- 02-DECISIONS/0009-modules-and-the-graph.md
|
||||
- 03-DESIGN/00-as-is/00-overview.md
|
||||
- 03-DESIGN/01-to-be/00-work-breakdown.md
|
||||
became: []
|
||||
@@ -24,9 +24,9 @@ disk, and where today's catalogue lands.
|
||||
## Why
|
||||
|
||||
Every structural decision so far has been a **correction**: eight contexts replacing thirty-three
|
||||
modules ([ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md)), domains
|
||||
modules ([ADR 0001](../../02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md)), domains
|
||||
replacing single-function modules
|
||||
([ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)). A
|
||||
([ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md)). A
|
||||
correction inherits the frame of the thing it corrects, and two of the mesh's oldest problems
|
||||
look unsolvable from inside that frame:
|
||||
|
||||
@@ -68,7 +68,7 @@ against taste:
|
||||
circle, self-hosted. Personal cloud infrastructure.
|
||||
8. **Agents make it self-improving and self-healing.**
|
||||
9. It is **end-to-end testable on one machine**
|
||||
([ADR 0009](../../02-DECISIONS/0009-the-lab.md)).
|
||||
([ADR 0016](../../02-DECISIONS/0016-the-lab.md)).
|
||||
|
||||
## Status
|
||||
|
||||
@@ -76,7 +76,7 @@ A first skeleton exists, with four design moves that the current shape does not
|
||||
`active` because two of them are unproven and one contradicts a record that is already
|
||||
accepted.
|
||||
|
||||
**Finding worth stating up front:** [ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md)
|
||||
**Finding worth stating up front:** [ADR 0001](../../02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md)
|
||||
names nine bounded contexts and **none of them owns connectivity** — no overlay, no resolution,
|
||||
no firewall, no ingress. Requirement 4 has no home in the accepted decomposition, while
|
||||
[research 005](../005-domain-grouping/analysis.md) found reachability to be the *only* part of
|
||||
@@ -87,8 +87,8 @@ the catalogue where modules genuinely change together under one intent. The skel
|
||||
| Question | Why it is open |
|
||||
|---|---|
|
||||
| Does the record — the event log contexts integrate through — belong to the substrate or the control plane? | It is infrastructure by shape and domain by content. Placing it wrong reintroduces a circularity. |
|
||||
| One repository per tier, or per context? | Already open from ADR 0008 as "catalogue destination — one repository or many". The skeleton assumes per tier and does not settle it. |
|
||||
| ~~Does an unprivileged node earn a place in the inventory, or only a presence?~~ | **Answered 2026-08-25** by the operator: a node is a *managed machine inside the mesh*, not an unprivileged something — and a disconnected node is still a node, in a different situation. The question posed a class distinction; the answer is that there is none, and what varies is **state**. Recorded as [ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md). |
|
||||
| ~~Does absorbing overlay, filtering, packages, supervision and the container runtime make the host too large?~~ | **Answered 2026-08-25** — [`host-size.md`](host-size.md). Measured: the absorption is smaller than the machinery that already applies state, and eight of ten adapters already carry no dependency. The risk is not size but direction, and it is two modules wide. The claim survives with its scope corrected — the host carries one concern, *apply declared state on this machine*, of which the six are instances. Recorded as [ADR 0016](../../02-DECISIONS/0016-the-node-host.md), designed in [`05-the-node-host.md`](../../03-DESIGN/01-to-be/05-the-node-host.md). |
|
||||
| One repository per tier, or per context? | Already open from ADR 0001 as "catalogue destination — one repository or many". The skeleton assumes per tier and does not settle it. |
|
||||
| ~~Does an unprivileged node earn a place in the inventory, or only a presence?~~ | **Answered 2026-08-25** by the operator: a node is a *managed machine inside the mesh*, not an unprivileged something — and a disconnected node is still a node, in a different situation. The question posed a class distinction; the answer is that there is none, and what varies is **state**. Recorded as [ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md). |
|
||||
| ~~Does absorbing overlay, filtering, packages, supervision and the container runtime make the host too large?~~ | **Answered 2026-08-25** — [`host-size.md`](host-size.md). Measured: the absorption is smaller than the machinery that already applies state, and eight of ten adapters already carry no dependency. The risk is not size but direction, and it is two modules wide. The claim survives with its scope corrected — the host carries one concern, *apply declared state on this machine*, of which the six are instances. Recorded as [ADR 0005](../../02-DECISIONS/0005-the-node-host.md), designed in [`05-the-node-host.md`](../../03-DESIGN/01-to-be/05-the-node-host.md). |
|
||||
| ~~Four substrate services or five?~~ | **Answered conditionally**, which is the honest form — [`07-the-substrate.md`](../../03-DESIGN/01-to-be/07-the-substrate.md). The substrate is *what the control plane consumes and cannot grant itself*. The identity provider qualifies only if the control plane delegates authentication; if it authenticates natively it is an ordinary hosted service. The count follows from a decision not yet taken, and asserting four was asserting that decision. |
|
||||
| Does `feature` survive? | The skeleton splits it in two and argues the conflation is what makes the delivery pipeline hard to reason about. Unproven. |
|
||||
|
||||
@@ -160,8 +160,8 @@ neither option covers, and it is the most common one.
|
||||
| **Absorbed into the host** | It is not a module at all. It is part of what "managing a machine" means, and belongs in tier 0. | overlay membership, packet filtering, package management, service supervision, container runtime, filesystem management |
|
||||
| **Substrate** | The control plane cannot exist without it. Pinned, host-applied. | relational store, bus, object store, image registry |
|
||||
| **Control-plane context** | It decides something across nodes. | connectivity policy, inventory, delivery, provisioning, observability |
|
||||
| **Workload module** | The mesh hosts it. Grouped per [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md). | media library, desktop session, collaboration tooling |
|
||||
| **Leaves the repository** | A standalone application, per [ADR 0006](../../02-DECISIONS/0006-applications-live-in-their-own-repository.md). | the applications identified in research 005 |
|
||||
| **Workload module** | The mesh hosts it. Grouped per [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md). | media library, desktop session, collaboration tooling |
|
||||
| **Leaves the repository** | A standalone application, per [ADR 0015](../../02-DECISIONS/0015-applications-live-in-their-own-repository.md). | the applications identified in research 005 |
|
||||
|
||||
**The first fate is the finding.** Research 005 measured the reachability cluster — proxy,
|
||||
resolver, firewall, overlay — as the only place in the catalogue where modules genuinely change
|
||||
@@ -256,7 +256,7 @@ mesh-surfaces/ TIER 3
|
||||
mesh-catalog/ TIER 4
|
||||
<domain>/<module>/ layout as above
|
||||
|
||||
mesh-lab/ mesh-sdk/ hq/ (company-scoped — ADR 0011)
|
||||
mesh-lab/ mesh-sdk/ hq/ (company-scoped — ADR 0019)
|
||||
```
|
||||
|
||||
## What this does not settle
|
||||
|
||||
@@ -80,12 +80,12 @@ mesh-surfaces/ TIER 3 — thin; no logic lives here
|
||||
cli/ the shell-facing interface
|
||||
|
||||
mesh-catalog/ TIER 4 — what the mesh hosts
|
||||
<domain>/ grouped per ADR 0017, list per research 005
|
||||
<domain>/ grouped per ADR 0022, list per research 005
|
||||
|
||||
mesh-lab/ the whole mesh, disposable, on one machine
|
||||
mesh-sdk/ contracts shared across tiers — types, not behaviour
|
||||
|
||||
hq/ company-scoped, not a mesh repository — ADR 0011
|
||||
hq/ company-scoped, not a mesh repository — ADR 0019
|
||||
```
|
||||
|
||||
## The dependency rule
|
||||
@@ -97,7 +97,7 @@ a second surface would have to reimplement.
|
||||
This is the whole of the bootstrap answer, and per this repository's own rule it must say how
|
||||
it is checked: a dependency-direction lint in the build, failing on an upward import. A tier
|
||||
rule enforced by intention is the same as no tier rule — that is
|
||||
[ADR 0023](../../02-DECISIONS/0023-delivery.md) applied to architecture.
|
||||
[ADR 0010](../../02-DECISIONS/0010-delivery.md) applied to architecture.
|
||||
|
||||
## Move 1 — the substrate is applied, not delivered
|
||||
|
||||
@@ -144,7 +144,7 @@ assumption that every node is equivalent — already false, and today handled by
|
||||
|
||||
## Move 3 — connectivity becomes a context
|
||||
|
||||
[ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md) names nine contexts
|
||||
[ADR 0001](../../02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md) names nine contexts
|
||||
and none of them owns the overlay, the resolver, the firewall or the ingress. `config` owns
|
||||
PKI, which is the closest thing, and it is not close.
|
||||
|
||||
@@ -162,8 +162,8 @@ So the evidence and the gap point the same way. `connectivity` owns:
|
||||
- certificates for both name spaces
|
||||
|
||||
This is an addition to an accepted record, so it is a decision, not a drafting choice. It
|
||||
belongs in a new record that extends ADR 0008 the way
|
||||
[ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md) does —
|
||||
belongs in a new record that extends ADR 0001 the way
|
||||
[ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md) does —
|
||||
not written here.
|
||||
|
||||
### Where the networking actually lives
|
||||
@@ -195,7 +195,7 @@ The invitation was to check whether the concept survives. It does not, in one pi
|
||||
|
||||
Today a **feature** means both *a thing built once* and *a thing selected per node*, and the
|
||||
delivery pipeline is hard to reason about precisely because those have different cardinality
|
||||
and one word ([ADR 0023](../../02-DECISIONS/0023-delivery.md)
|
||||
and one word ([ADR 0010](../../02-DECISIONS/0010-delivery.md)
|
||||
is the pipeline half of the same confusion).
|
||||
|
||||
Split it:
|
||||
@@ -230,7 +230,7 @@ the fact that it runs its own development on them is dogfooding, not architectur
|
||||
## What agents are, structurally
|
||||
|
||||
Self-improvement and self-healing are not a tier. Agents are participants
|
||||
([ADR 0007](../../02-DECISIONS/0007-agents-are-persistent-employees.md)) that hold identity in
|
||||
([ADR 0003](../../02-DECISIONS/0003-agents-are-persistent-employees.md)) that hold identity in
|
||||
tier 2, act through tier 3 like any other caller, and run as workloads in tier 4.
|
||||
|
||||
This matters for one reason: **an agent must not have a privileged path**. Anything an agent
|
||||
@@ -241,7 +241,7 @@ no human checkpoint.
|
||||
|
||||
## How this is tested
|
||||
|
||||
The lab ([ADR 0009](../../02-DECISIONS/0009-the-lab.md)) raises the
|
||||
The lab ([ADR 0016](../../02-DECISIONS/0016-the-lab.md)) raises the
|
||||
tree above on one machine: virtual machines as nodes, a real overlay between them, the real
|
||||
substrate bundle, the real control plane, the real delivery path.
|
||||
|
||||
@@ -254,7 +254,7 @@ being the one thing nobody exercises until it breaks.
|
||||
|
||||
- Where the record lives. It is infrastructure by shape and domain by content, and putting it
|
||||
in the substrate risks recreating a circularity in the one place the design just removed one.
|
||||
- Whether tier 2's contexts are one repository or several. Open from ADR 0008 already.
|
||||
- Whether tier 2's contexts are one repository or several. Open from ADR 0001 already.
|
||||
- Whether an `edge` node is in the inventory or merely present — which decides whether "node"
|
||||
is one concept or two.
|
||||
- The migration. Nothing here says how today's mesh becomes this, and the skeleton is worth
|
||||
@@ -266,13 +266,13 @@ The tier-0 binary was first called `mesh-agent`, because "node agent" is the ref
|
||||
else in the industry. That is wrong here, and wrong in the specific way
|
||||
[`how-we-build.md`](../../00-META/how-we-build.md) §4 exists to catch: **Agent** is a
|
||||
first-class concept in this mesh — a participant, some of whom are human, holding identity and
|
||||
memory ([ADR 0007](../../02-DECISIONS/0007-agents-are-persistent-employees.md)). One document
|
||||
memory ([ADR 0003](../../02-DECISIONS/0003-agents-are-persistent-employees.md)). One document
|
||||
carried both meanings.
|
||||
|
||||
It is the same failure as the anatomy naming in the current runtime: an evocative domain word
|
||||
pointing at infrastructure.
|
||||
|
||||
[ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md) supplies the fix in
|
||||
[ADR 0001](../../02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md) supplies the fix in
|
||||
its own title — *the mesh brokers capabilities; nodes host; agents think.* Three verbs, three
|
||||
components: the control plane **brokers** (`mesh-control`), the tier-0 binary **hosts**
|
||||
(`mesh-host`), the participant **thinks** (`agents`, untouched).
|
||||
|
||||
@@ -3,7 +3,7 @@ status: active
|
||||
initiated: 2026-08-23
|
||||
touches:
|
||||
- 03-DESIGN/00-as-is/03-provisioning.md
|
||||
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0009-modules-and-the-graph.md
|
||||
- 01-RESEARCH/006-mesh-from-scratch/code-skeleton.md
|
||||
became: []
|
||||
---
|
||||
@@ -14,7 +14,7 @@ became: []
|
||||
|
||||
Provisioning is the mechanism the whole mesh rests on: a module declares what it needs, and the
|
||||
mesh makes it exist, generates the credential, records the grant, and puts the values where the
|
||||
module will read them. [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)
|
||||
module will read them. [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md)
|
||||
calls it the mesh's core concern rather than its plumbing.
|
||||
|
||||
[Research 006](../006-mesh-from-scratch/code-skeleton.md) then asks it to carry **more**: the
|
||||
|
||||
@@ -3,12 +3,12 @@ status: graduated
|
||||
initiated: 2026-08-23
|
||||
touches:
|
||||
- 03-DESIGN/00-as-is/04-delivery.md
|
||||
- 02-DECISIONS/0023-delivery.md
|
||||
- 02-DECISIONS/0023-delivery.md
|
||||
- 02-DECISIONS/0010-delivery.md
|
||||
- 02-DECISIONS/0010-delivery.md
|
||||
- 01-RESEARCH/006-mesh-from-scratch/code-skeleton.md
|
||||
became:
|
||||
- 02-DECISIONS/0023-delivery.md
|
||||
- 02-DECISIONS/0023-delivery.md
|
||||
- 02-DECISIONS/0010-delivery.md
|
||||
- 02-DECISIONS/0010-delivery.md
|
||||
---
|
||||
|
||||
# 008 — The coordinator: a change checked in becomes a deployed state
|
||||
@@ -49,14 +49,14 @@ working across the transition to self-hosted providers.
|
||||
because the first was honest about what it did not fix.
|
||||
|
||||
**Does the coordinator dispatch stages, or converge nodes on a declaration?** — *Converge.*
|
||||
[ADR 0023](../../02-DECISIONS/0023-delivery.md): a pipeline ends when the
|
||||
[ADR 0010](../../02-DECISIONS/0010-delivery.md): a pipeline ends when the
|
||||
declaration is updated, and the host applies it and reads back — so the reporter is the applier.
|
||||
|
||||
**Does the three-silo split survive?** — *Yes, with the third redefined.* The cardinality
|
||||
observation holds; the third silo is not a stage any more.
|
||||
|
||||
**How does a change become a pipeline, reliably?** — *It does not become a pipeline at all.*
|
||||
[ADR 0023](../../02-DECISIONS/0023-delivery.md) applies 0058's
|
||||
[ADR 0010](../../02-DECISIONS/0010-delivery.md) applies 0058's
|
||||
move one level up: the control plane holds what source exists and what has been built, and builds
|
||||
the difference. **An event makes it fast; nothing makes it necessary.** The failures this effort
|
||||
catalogued — a truncated commit list, a broken path match — become latency rather than silence.
|
||||
@@ -83,7 +83,7 @@ load-bearing question first.
|
||||
|
||||
## What is NOT closed by this
|
||||
|
||||
[ADR 0023](../../02-DECISIONS/0023-delivery.md) names four costs
|
||||
[ADR 0010](../../02-DECISIONS/0010-delivery.md) names four costs
|
||||
and one of them is a real risk rather than a trade: **a reconciler that cannot reach its target
|
||||
retries forever, and without something that notices, the failure is silence** — which is the
|
||||
fault this effort exists to catalogue, reintroduced in a new place. That belongs to observability
|
||||
@@ -96,6 +96,6 @@ and it is not designed.
|
||||
| What is a **deployed state**, and how does the mesh know it is in one? | Everything follows from this. If a stage reports transport, "deployed" is a claim nobody checked. A desired-state model with reconciliation gives a different answer from a job-completion model. |
|
||||
| Does the coordinator dispatch **stages**, or converge nodes on a **declaration**? | The current model is a state machine over stages. The alternative is that a node is told what should be true and reports what is. The second makes drift visible; the first cannot see it. |
|
||||
| How does a change **become** a pipeline, reliably? | Detection has failed for reasons unrelated to the change, silently. |
|
||||
| What produces a **verdict**, and what is it a verdict about? | Ties to the lab ([ADR 0009](../../02-DECISIONS/0009-the-lab.md)) and to a module carrying its own assertions. |
|
||||
| What produces a **verdict**, and what is it a verdict about? | Ties to the lab ([ADR 0016](../../02-DECISIONS/0016-the-lab.md)) and to a module carrying its own assertions. |
|
||||
| How does delivery work **before self-hosting**, and across the transition? | From research 006: source and artifacts start external and are re-bound to internal providers. The coordinator has to be indifferent to which. |
|
||||
| Does the **three-silo** split survive the artifact/part split? | [ADR 0023](../../02-DECISIONS/0023-delivery.md) is cardinality-driven, and research 006 renames the thing the cardinality is about. |
|
||||
| Does the **three-silo** split survive the artifact/part split? | [ADR 0010](../../02-DECISIONS/0010-delivery.md) is cardinality-driven, and research 006 renames the thing the cardinality is about. |
|
||||
|
||||
@@ -4,7 +4,7 @@ initiated: 2026-08-23
|
||||
touches:
|
||||
- 01-RESEARCH/006-mesh-from-scratch/code-skeleton.md
|
||||
- 03-DESIGN/01-to-be/01-end-to-end-testing.md
|
||||
- 02-DECISIONS/0009-the-lab.md
|
||||
- 02-DECISIONS/0016-the-lab.md
|
||||
- 03-DESIGN/00-as-is/00-overview.md
|
||||
became: []
|
||||
---
|
||||
@@ -28,7 +28,7 @@ Recorded because incremental is the reflex answer and it is wrong in this case.
|
||||
requirements — none of these can half-apply. Running both models at once means the old one's
|
||||
assumptions keep constraining the new one, which is how a migration becomes permanent.
|
||||
- **Nothing external depends on it.** No users outside the operator, no service level to hold.
|
||||
- **The lab exists precisely for this** ([ADR 0009](../../02-DECISIONS/0009-the-lab.md)).
|
||||
- **The lab exists precisely for this** ([ADR 0016](../../02-DECISIONS/0016-the-lab.md)).
|
||||
A big-bang that has been rehearsed end to end, repeatedly, on identical machines is not the
|
||||
same risk as one performed for the first time on the real mesh. This is also why the lab is
|
||||
phase 0 rather than a verification step later: the new mesh is *developed* inside it, so by
|
||||
@@ -70,7 +70,7 @@ than a discovery.
|
||||
|
||||
| Phase | What | Done when |
|
||||
|---|---|---|
|
||||
| **0** | **Build the lab's bootstrap scenario** ([ADR 0009](../../02-DECISIONS/0009-the-lab.md)) — virtual machines, a network, a way to place a binary, snapshot and reset. No forge, no coordinator, no pipeline. | A machine can be raised from nothing, reset, and raised again, repeatably. |
|
||||
| **0** | **Build the lab's bootstrap scenario** ([ADR 0016](../../02-DECISIONS/0016-the-lab.md)) — virtual machines, a network, a way to place a binary, snapshot and reset. No forge, no coordinator, no pipeline. | A machine can be raised from nothing, reset, and raised again, repeatably. |
|
||||
| A | Build tier 0, **inside the lab**. The host's interface first — it carries the skeleton's biggest unproven claim. | A bare machine becomes a managed node with no mesh present. |
|
||||
| B | Build tier 1 and 2. The bootstrap scenario grows into the full one by addition — the same machines, with more placed inside them. | The lab raises a full mesh from nothing, repeatedly, from pinned external artifacts. |
|
||||
| C | Enough of tier 3 to operate it. | The mesh can be driven without direct database access. |
|
||||
|
||||
@@ -3,8 +3,8 @@ status: active
|
||||
initiated: 2026-08-24
|
||||
touches:
|
||||
- 03-DESIGN/01-to-be/03-scenario-lifecycle.md
|
||||
- 02-DECISIONS/0009-the-lab.md
|
||||
- 02-DECISIONS/0009-the-lab.md
|
||||
- 02-DECISIONS/0016-the-lab.md
|
||||
- 02-DECISIONS/0016-the-lab.md
|
||||
became: []
|
||||
---
|
||||
|
||||
@@ -23,7 +23,7 @@ Measured on a workstation, 2026-08-24. Numbers in [`measurements.md`](measuremen
|
||||
|
||||
## Why it matters
|
||||
|
||||
[ADR 0009](../../02-DECISIONS/0009-the-lab.md) makes the
|
||||
[ADR 0016](../../02-DECISIONS/0016-the-lab.md) makes the
|
||||
bootstrap scenario the inner development loop for tiers 0 and 1 — the argument being that
|
||||
raising a node from nothing stops being the least-exercised path and becomes the most-exercised
|
||||
one. **That argument is only true if raising and resetting are cheap.** A loop that costs
|
||||
|
||||
@@ -13,7 +13,7 @@ image.
|
||||
|
||||
| Fact | Value | Consequence |
|
||||
|---|---|---|
|
||||
| Hardware virtualisation | present | virtual machines run at native speed; the choice in [ADR 0009](../../02-DECISIONS/0009-the-lab.md) is not paying an emulation penalty |
|
||||
| Hardware virtualisation | present | virtual machines run at native speed; the choice in [ADR 0016](../../02-DECISIONS/0016-the-lab.md) is not paying an emulation penalty |
|
||||
| Storage drivers the daemon offers | **`dir` only** | no copy-on-write, therefore no cheap snapshot |
|
||||
| Host filesystems | ext4 throughout | nothing copy-on-write to put a pool on |
|
||||
| btrfs kernel module | **available** | the kernel can do it |
|
||||
@@ -72,7 +72,7 @@ worst, before any of the mesh's own work begins.
|
||||
|
||||
**This is too slow for an inner loop**, and the reason is not the design.
|
||||
|
||||
[ADR 0009](../../02-DECISIONS/0009-the-lab.md) argues that
|
||||
[ADR 0016](../../02-DECISIONS/0016-the-lab.md) argues that
|
||||
making the bootstrap path the inner development loop turns the least-exercised code in the
|
||||
system into the most-exercised. That argument holds only while resetting is cheap. At a minute
|
||||
and a half a cycle, with occasional multi-minute stalls, the loop is one a person works around
|
||||
@@ -120,7 +120,7 @@ A four-machine reset-and-rerun cycle, the operation the inner loop repeats most:
|
||||
| **cycle** | **~90 s, unbounded at worst** | **~15 s, dominated by boot** |
|
||||
|
||||
At fifteen seconds, dominated by a boot that cannot be avoided, the inner loop is viable and
|
||||
[ADR 0009](../../02-DECISIONS/0009-the-lab.md)'s argument holds.
|
||||
[ADR 0016](../../02-DECISIONS/0016-the-lab.md)'s argument holds.
|
||||
At ninety it did not.
|
||||
|
||||
### One honest counter-observation
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
status: graduated
|
||||
initiated: 2026-08-25
|
||||
became:
|
||||
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0020-a-context-owns-its-store.md
|
||||
- 02-DECISIONS/0009-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0008-a-context-owns-its-store.md
|
||||
- 03-DESIGN/01-to-be/06-the-control-plane.md
|
||||
- 03-DESIGN/01-to-be/07-the-substrate.md
|
||||
touches:
|
||||
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
|
||||
- 02-DECISIONS/0009-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0009-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0004-a-node-and-how-it-joins.md
|
||||
- 03-DESIGN/00-as-is/02-modules-and-manifests.md
|
||||
- 03-DESIGN/00-as-is/10-module-catalogue.md
|
||||
- 04-ISSUES/007-an-installed-package-is-not-a-capability/00-report.md
|
||||
@@ -21,9 +21,9 @@ touches:
|
||||
> *instantiation*, and both are **runtime** edges — they answer *what does this need in order to
|
||||
> run*. Delivery needs a different question answered — *what has to be rebuilt when this changes*
|
||||
> — and that is a **build** edge, fixed inside an artifact rather than negotiated when it runs.
|
||||
> Recorded by [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md), which also
|
||||
> Recorded by [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md), which also
|
||||
> notes what this effort's three entities turn out to be good for
|
||||
> ([ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)).
|
||||
> ([ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md)).
|
||||
|
||||
## What is being investigated
|
||||
|
||||
@@ -74,12 +74,12 @@ What survives is narrower: three declarations that do not exist (`excludes`, a r
|
||||
capability, an interface with adapters), and two defects worth fixing whatever else is
|
||||
concluded — `provider:` is a dependency edge that is not read as one, which makes the closure
|
||||
for a working mesh come out without a database; and the resolver continues past a cycle and
|
||||
past a missing dependency, contrary to ADR 0008.
|
||||
past a missing dependency, contrary to ADR 0001.
|
||||
|
||||
[ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md) proposes
|
||||
[ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md) proposes
|
||||
grouping modules by domain. [Research 005](../005-domain-grouping/analysis.md) measured that
|
||||
proposal and found its evidence holds in exactly one place — reachability — which
|
||||
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md) has since absorbed
|
||||
[ADR 0005](../../02-DECISIONS/0005-the-node-host.md) has since absorbed
|
||||
into the host. The measured case for domain grouping has therefore been consumed by a decision
|
||||
taken for unrelated reasons, and what remains is fifty modules that co-change with nothing.
|
||||
|
||||
@@ -102,7 +102,7 @@ and abandoned in favour of one concept with facets, for a reason worth keeping:
|
||||
Filing decisions that follow from nothing are the disease research 005 measured. A second
|
||||
taxonomy would reproduce it.
|
||||
|
||||
So [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md) survives, and the question
|
||||
So [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md) survives, and the question
|
||||
becomes what a module must be able to **declare**.
|
||||
|
||||
## The shape being investigated
|
||||
@@ -111,7 +111,7 @@ Five declarations, of which two exist today.
|
||||
|
||||
| Declaration | Today | Notes |
|
||||
|---|---|---|
|
||||
| **requires a resource** — a database, a bucket | yes | provisioning, [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md) |
|
||||
| **requires a resource** — a database, a bucket | yes | provisioning, [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md) |
|
||||
| **provides a resource** | yes | as above |
|
||||
| **requires another module** | **no** | the dependency edge — the graph's substance |
|
||||
| **excludes another module** | **no** | installing A makes B unavailable |
|
||||
@@ -174,12 +174,12 @@ Struck-through rows are answered, with where. The rest are live.
|
||||
| ~~What does the graph **delete**?~~ | For the existing system: nothing, it is already there ([`analysis.md`](analysis.md)). For the design: the module/resource distinction, the interface as a kind of thing, capability checking as a separate mechanism, domain grouping, and — the first clear deletion — **grant kinds**, once a module may only be granted what it exclusively owns ([`worked-provider.md`](worked-provider.md)). |
|
||||
| ~~Is an interface a module, or a name?~~ | A **name**, and only where providers are genuinely substitutable. The adapter is what creates one; without an adapter there is a **tag**, which describes and does not bind ([`proposal.md`](proposal.md)). |
|
||||
| ~~Where do domain modules fit?~~ | They do not. There is core infrastructure — concrete modules named individually, not flavourable, nothing standing in front of them. |
|
||||
| ~~What happens to domain grouping?~~ | Superseded. Folders assert relationships; edges record them. What grouping was for is a tag and a query. [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md) is `proposed` and should be superseded rather than narrowed. |
|
||||
| ~~What happens to domain grouping?~~ | Superseded. Folders assert relationships; edges record them. What grouping was for is a tag and a query. [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md) is `proposed` and should be superseded rather than narrowed. |
|
||||
| ~~Is there one kind of edge?~~ | **No — two.** *Presence*, where a thing must exist, and *instantiation*, where a provider makes something for a consumer and hands back credentials. Instantiation implies presence, not the reverse. |
|
||||
| ~~When two modules provide one name, who chooses?~~ | Neither the consumer naming a node nor the consumer not caring. The consumer declares the **scope of its own need** — shared across its instances, or one each — the mesh binds, and the binding is written down and sticky. Where it is written follows the scope. |
|
||||
| ~~Can several modules share one database?~~ | **No.** A module is granted only what it exclusively owns — no shared writes and no read role on another's store, because reading couples you to its layout just as firmly. |
|
||||
| ~~What about a dashboard reading a dozen stores?~~ | **The rule is about contexts, not processes.** The mesh's own board reading the mesh's own store is the mesh showing its own data — not a boundary crossing. Everything inside a context reads its store freely; what is forbidden is a *different* context reading it. An earlier answer here was wrong. |
|
||||
| ~~Can every registry consumer be served another way?~~ | **Largely dissolves.** Of eighteen direct consumers, the owner keeps its database, node appliers are already stopped by ADR 0016, and the bulk are **foreign tenants** — thirteen tables across three contexts — who need to move out rather than read differently. |
|
||||
| ~~Can every registry consumer be served another way?~~ | **Largely dissolves.** Of eighteen direct consumers, the owner keeps its database, node appliers are already stopped by ADR 0005, and the bulk are **foreign tenants** — thirteen tables across three contexts — who need to move out rather than read differently. |
|
||||
|
||||
### Live
|
||||
|
||||
@@ -190,10 +190,10 @@ Struck-through rows are answered, with where. The rest are live.
|
||||
| What does a provider hand back? | Credentials and an address for a store; a command for a terminal. Same relation, different shape crossing it. |
|
||||
| Is provisioning one mechanism or two? | The mesh's own registry is provisioned **before there is a mesh**, so provisioning is part of the bootstrap and part of what the carried bundle expresses. At bootstrap the store is local; afterwards it is on another node. Same operation, both sides of a tier boundary. |
|
||||
| Is a tool surface one relation with two audiences, or two? | 56 of 126 modules carry tools — more than carry a service — and what consumes them is an **agent**, not a module. |
|
||||
| ~~Do the remaining cross-context reads want an interface or events?~~ | **Derived, not chosen.** Neither is SQL — that only ever runs against your own store. ADR 0015 makes disconnection ordinary, so anything that must work while disconnected cannot use a request and needs a local copy: a subscription. Anything where a stale answer is worse than none cannot use a subscription. |
|
||||
| ~~Do the remaining cross-context reads want an interface or events?~~ | **Derived, not chosen.** Neither is SQL — that only ever runs against your own store. ADR 0004 makes disconnection ordinary, so anything that must work while disconnected cannot use a request and needs a local copy: a subscription. Anything where a stale answer is worse than none cannot use a subscription. |
|
||||
| What does a consumer do about events it missed while disconnected? | Replay from a point, ask once for a full picture and resume, or rebuild. The question every projection has, and unanswered here. |
|
||||
| What happens to a grant when its consumer is removed? | Dropping is data loss; keeping is a leak. ADR 0016's removal rule does not obviously carry, because the thing lives inside another module's state. |
|
||||
| Is a declaration composed per node, from what that node reported? | Some configuration follows the hardware. Either the host fills a blank — deciding, against ADR 0016 — or the control plane composes from the node's inventory first. |
|
||||
| What happens to a grant when its consumer is removed? | Dropping is data loss; keeping is a leak. ADR 0005's removal rule does not obviously carry, because the thing lives inside another module's state. |
|
||||
| Is a declaration composed per node, from what that node reported? | Some configuration follows the hardware. Either the host fills a blank — deciding, against ADR 0005 — or the control plane composes from the node's inventory first. |
|
||||
| Would `excludes` and capability requirements actually be used? | Zero manifests declare either, which is equally consistent with *nobody needs them* and *nobody can express them*. |
|
||||
| What does an exclusion mean for something already installed? | Refuse the install, or surface the conflict and let it be decided. |
|
||||
| Are tiers a view of the graph, or a constraint on it? | If a tier is a computed level the word is a convenience. If *a tier may depend only on tiers below it* is to be enforced, it is a constraint and must be stated as one. |
|
||||
|
||||
@@ -36,7 +36,7 @@ another module's provision is treated as an implicit edge to that module**, so a
|
||||
not have to declare the same relationship twice.
|
||||
|
||||
So *ordering by the graph* — which
|
||||
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md) says
|
||||
[ADR 0005](../../02-DECISIONS/0005-the-node-host.md) says
|
||||
the control plane will do — is not a thing to build. It is a thing to call.
|
||||
|
||||
## Finding 3 — the most important edges in the mesh are invisible
|
||||
@@ -82,7 +82,7 @@ means.
|
||||
## Finding 4 — the resolver continues past faults it should stop on
|
||||
|
||||
Two behaviours, both contrary to
|
||||
[ADR 0023](../../02-DECISIONS/0023-delivery.md):
|
||||
[ADR 0010](../../02-DECISIONS/0010-delivery.md):
|
||||
|
||||
- **A cycle warns and falls back to input order.** A cycle means no correct order exists; the
|
||||
resolver proceeds with an arbitrary one and logs a line.
|
||||
@@ -91,7 +91,7 @@ Two behaviours, both contrary to
|
||||
|
||||
Neither has fired in the current catalogue — there are no cycles and nothing dangling — which
|
||||
is why nobody has noticed. They are latent, and they are in the component that
|
||||
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md) makes
|
||||
[ADR 0005](../../02-DECISIONS/0005-the-node-host.md) makes
|
||||
responsible for the ordering a host will apply without question.
|
||||
|
||||
## Finding 5 — placement is decided in the catalogue
|
||||
|
||||
@@ -13,7 +13,7 @@ it is simply up. *A relational store, a message broker, an object store, a dashb
|
||||
|
||||
**2 — A system package with configuration.** Not a container. Installed into the machine,
|
||||
configured through files, run by the service manager. *A firewall, a resolver, an overlay.*
|
||||
Note: [ADR 0016](../../02-DECISIONS/0016-the-node-host.md) says applying
|
||||
Note: [ADR 0005](../../02-DECISIONS/0005-the-node-host.md) says applying
|
||||
these is the host's job — so what the module contributes is the *deciding*, not the doing.
|
||||
|
||||
**3 — An application a person launches.** Installed on a node, started by a human, running only
|
||||
@@ -35,7 +35,7 @@ provider behind an assistant interface.*
|
||||
|
||||
**9 — A standalone application in its own repository.** Same shape as any of the above; the
|
||||
difference is only where its source lives
|
||||
([ADR 0006](../../02-DECISIONS/0006-applications-live-in-their-own-repository.md)). Worth
|
||||
([ADR 0015](../../02-DECISIONS/0015-applications-live-in-their-own-repository.md)). Worth
|
||||
listing because a schema that assumes a monorepo path would exclude it.
|
||||
|
||||
## The cases that break a naive schema
|
||||
|
||||
@@ -55,7 +55,7 @@ registry. **Tier 2, delivery.**
|
||||
|
||||
**Resources — desired state on a machine.** `configs`, `service`, `systemd`, `vhost`, `tools`.
|
||||
Applied, converged, idempotent — which is exactly what
|
||||
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md)
|
||||
[ADR 0005](../../02-DECISIONS/0005-the-node-host.md)
|
||||
already describes and what the host already does. **Tier 0.**
|
||||
|
||||
**Actions — run once, against something that is not this machine.** `migrations`, `seeds`,
|
||||
|
||||
@@ -132,7 +132,7 @@ The question the effort opened with, answered for the design rather than for wha
|
||||
to install. There is **core infrastructure**, which is a set of concrete modules named
|
||||
individually — a firewall, a store, a resolver — with no flavour and no grouping module
|
||||
standing in front of them.
|
||||
- **Domain grouping as structure** ([ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)).
|
||||
- **Domain grouping as structure** ([ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md)).
|
||||
Folders assert relationships; edges record them. What grouping was for — finding things,
|
||||
seeing what belongs together — is a **tag** and a *query* over the graph, neither of which
|
||||
anybody has to keep true by hand.
|
||||
|
||||
@@ -149,7 +149,7 @@ Steps 2 and 3 happen **before there is a mesh to do them**. So provisioning is n
|
||||
control-plane service that consumers use; it is part of the bootstrap, and part of what the
|
||||
carried bundle has to be able to express.
|
||||
|
||||
**Which strains what a declaration is.** [ADR 0016](../../02-DECISIONS/0016-the-node-host.md)
|
||||
**Which strains what a declaration is.** [ADR 0005](../../02-DECISIONS/0005-the-node-host.md)
|
||||
has the host applying *declared state on this machine*. A database inside a running store is not
|
||||
a file or a unit — and at bootstrap it is, at least, local: the store is on the same machine as
|
||||
the host applying the bundle.
|
||||
@@ -158,7 +158,7 @@ Later it is not. A consumer on one node provisioned from a store on another is t
|
||||
case, and reaching it is not the host's job.
|
||||
|
||||
**Resolved as two mechanisms, which is the answer rather than a compromise**
|
||||
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)). The host
|
||||
([ADR 0005](../../02-DECISIONS/0005-the-node-host.md)). The host
|
||||
runs bootstrap actions locally from the bundle; the control plane provisions across the mesh
|
||||
afterwards. Different actors, different scopes, different trust paths — so there is no single
|
||||
operation with a tier boundary running through it.
|
||||
@@ -279,7 +279,7 @@ of them is work.
|
||||
| Group | What happens under the rule |
|
||||
|---|---|
|
||||
| **The owner and its machinery** — the mesh module, the SDK, the environment and configuration synchronisers, secrets | Nothing. It owns the database. |
|
||||
| **Node appliers** — the overlay, the shell daemon, the resolver | **Already resolved.** [ADR 0016](../../02-DECISIONS/0016-the-node-host.md) stops the host querying the mesh database, decided for tier reasons with nothing to do with this. |
|
||||
| **Node appliers** — the overlay, the shell daemon, the resolver | **Already resolved.** [ADR 0005](../../02-DECISIONS/0005-the-node-host.md) stops the host querying the mesh database, decided for tier reasons with nothing to do with this. |
|
||||
| **Foreign tenants** — the work engine (10 tables), the knowledge base (2), pipeline logs (1) | They need **their own database**. They are not reading the registry; they are storing their own data in it. |
|
||||
| **Genuine cross-context reads** — the work engine reads `nodes`; two others read a handful | The only ones needing an interface or events. |
|
||||
|
||||
@@ -299,7 +299,7 @@ estimate. **The rule holds.**
|
||||
The remaining cross-context reads need one or the other. **Neither is SQL** — under exclusive
|
||||
ownership a module runs SQL against its own database and nothing else, whatever transport a
|
||||
query might travel over. Both options are the mesh's own channel, and both ride the broker
|
||||
([ADR 0001](../../02-DECISIONS/0001-nodes-communicate-over-a-broker.md)), so the transport is
|
||||
([ADR 0002](../../02-DECISIONS/0002-nodes-communicate-over-a-broker.md)), so the transport is
|
||||
not the distinction.
|
||||
|
||||
**The distinction is where the answer lives when you need it.**
|
||||
@@ -312,7 +312,7 @@ not the distinction.
|
||||
| when the other side is down | you cannot answer | you answer from your copy |
|
||||
| what you must handle | a round trip that can fail | events you missed while you were down |
|
||||
|
||||
**What decides is not taste.** [ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)
|
||||
**What decides is not taste.** [ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)
|
||||
makes disconnection an ordinary situation rather than an exception. So:
|
||||
|
||||
> **Anything that must keep working while disconnected cannot use a request** — there is nobody
|
||||
@@ -354,14 +354,14 @@ proves it cannot be a global rule.
|
||||
|
||||
**What happens to a grant when the consumer is removed?** The game is uninstalled. Its database
|
||||
still exists, holding its data. Dropping it silently is data loss; keeping it forever is a leak.
|
||||
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md) says
|
||||
[ADR 0005](../../02-DECISIONS/0005-the-node-host.md) says
|
||||
the host removes what it applied and no longer declares — but this is not on the host, it is
|
||||
inside another module's state, and the same reasoning does not obviously carry.
|
||||
|
||||
**Where does node-derived configuration come from?** (3) The control plane composes a
|
||||
declaration, and cannot know this machine's memory. Either the host fills in a blank the
|
||||
declaration leaves — which makes the host decide something, against
|
||||
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md) — or the control
|
||||
[ADR 0005](../../02-DECISIONS/0005-the-node-host.md) — or the control
|
||||
plane reads the node's inventory first and composes with it. The second is consistent and means
|
||||
a declaration is composed *per node from what the node reported*, which is a stronger claim than
|
||||
anything recorded so far.
|
||||
@@ -419,9 +419,9 @@ But two things differ *between* them, and both matter more than the similarity.
|
||||
|
||||
### The broker cannot be managed over the broker
|
||||
|
||||
[ADR 0001](../../02-DECISIONS/0001-nodes-communicate-over-a-broker.md) makes the broker the
|
||||
[ADR 0002](../../02-DECISIONS/0002-nodes-communicate-over-a-broker.md) makes the broker the
|
||||
channel every node takes work from, and
|
||||
[ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md) makes it the security
|
||||
[ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md) makes it the security
|
||||
boundary — everything a node applies arrives through it.
|
||||
|
||||
So the module providing the broker is also **the way modules are managed**. A declaration cannot
|
||||
@@ -430,7 +430,7 @@ reconfigured. Nothing else in the catalogue has that property; the store is cons
|
||||
control plane but is not how the control plane *reaches* anything.
|
||||
|
||||
This is exactly what the carried bundle exists for
|
||||
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)): the broker is raised from
|
||||
([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)): the broker is raised from
|
||||
what the host carries, before there is a channel, because there is no other way to raise it.
|
||||
Recorded here because it is a constraint on *one module*, not a general rule, and a schema with
|
||||
no way to say so hides it.
|
||||
@@ -439,7 +439,7 @@ no way to say so hides it.
|
||||
|
||||
The broker is one per mesh — a single point of failure and a single point of trust, by decision
|
||||
rather than by accident. The store cannot be: a node that must keep working while disconnected
|
||||
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)) cannot depend on a database
|
||||
([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)) cannot depend on a database
|
||||
somewhere else.
|
||||
|
||||
Same nine properties, opposite answers. Which settles something the cases file left open: **how
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
status: active
|
||||
initiated: 2026-08-26
|
||||
touches:
|
||||
- 02-DECISIONS/0016-the-node-host.md
|
||||
- 02-DECISIONS/0002-managed-files-are-generated-never-edited.md
|
||||
- 02-DECISIONS/0005-the-node-host.md
|
||||
- 02-DECISIONS/0011-managed-files-are-generated-never-edited.md
|
||||
- 03-DESIGN/01-to-be/05-the-node-host.md
|
||||
- 03-DESIGN/00-as-is/05-runtime-and-installation.md
|
||||
- 01-RESEARCH/011-the-module-graph/00-overview.md
|
||||
@@ -34,7 +34,7 @@ carry everything in the bundle, download at apply time, or have something push t
|
||||
first. Downloading fails on the first node, which cannot fetch the image registry from the image
|
||||
registry it is trying to start.
|
||||
|
||||
> **Qualified by [ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md).** The
|
||||
> **Qualified by [ADR 0006](../../02-DECISIONS/0006-the-substrate-and-the-control-plane.md).** The
|
||||
> reframing below still holds for what a *tailored installer* contains — the missing pieces for a
|
||||
> given machine. It does **not** have to hold for container images: the installer fetches those
|
||||
> by digest, because a real machine has a network and the sealed case is the lab.
|
||||
@@ -71,13 +71,13 @@ records adoption of a pre-existing machine's configuration as the original mecha
|
||||
legacy and explicitly out of scope for the lab. It returns here for a different reason than it
|
||||
was dropped for, which is a thing to notice rather than to gloss.
|
||||
|
||||
**It creates a state that does not exist today.** [ADR 0002](../../02-DECISIONS/0002-managed-files-are-generated-never-edited.md)
|
||||
**It creates a state that does not exist today.** [ADR 0011](../../02-DECISIONS/0011-managed-files-are-generated-never-edited.md)
|
||||
has managed files generated and never edited; adoption needs a one-time import before that rule
|
||||
starts applying. Three states, and the middle one is new:
|
||||
|
||||
> unmanaged → **adopted once** → generated
|
||||
|
||||
**And it crosses a boundary just drawn.** [ADR 0016](../../02-DECISIONS/0016-the-node-host.md)
|
||||
**And it crosses a boundary just drawn.** [ADR 0005](../../02-DECISIONS/0005-the-node-host.md)
|
||||
says the host never touches what it did not create — the rule that stops a converger deleting
|
||||
what the mesh never put there. Adoption is the deliberate act of taking ownership of exactly
|
||||
that. The rule needs a companion rather than an exception: *never, unless adoption made it the
|
||||
|
||||
Reference in New Issue
Block a user