Renumber the records 1 to 23
The consolidation left a sparse sequence -- 1, 4, 6, 7, 9, 10, 12, 15, 16, 18, 19, 25, 34, 35, 36, 37, 40, 42, 44, 45, 48, 49, 58 -- where the gaps were only the archaeology of what used to be there. Renumbered contiguously. Renames run in ascending order, so every target number is already free and no two files ever collide. The reference rewrite is one simultaneous pass rather than a sequence of replacements. Numbers moved into slots other numbers were vacating -- the node host went 37 to 16 while the lab went 16 to 9 -- so replacing one at a time would have cascaded and silently pointed things at the wrong record. Seven plain-text references survived the merges as prose rather than links, naming records that no longer existed: the enrolment token, the link boundary, what a declaration is, reachability, the repository structure. Each mapped to the consolidated record that now holds it. Verified rather than assumed: every [ADR NNNN](path) link now has matching text and target, checked across the whole repository, and the checker passes. Frontmatter `consolidates:` lists dropped -- they named records that are gone, and each consolidated record already says in prose what it absorbed.
This commit is contained in:
+3
-3
@@ -34,7 +34,7 @@ comparing it against the code rather than by anyone noticing:
|
|||||||
|
|
||||||
- It described the pipeline as having a separate builder process and a build stage that
|
- It described the pipeline as having a separate builder process and a build stage that
|
||||||
packages. Neither was true after 2026-08-04; the documents stayed stale until 2026-08-06
|
packages. Neither was true after 2026-08-04; the documents stayed stale until 2026-08-06
|
||||||
([ADR 0058](../02-DECISIONS/0058-delivery.md)).
|
([ADR 0023](../02-DECISIONS/0023-delivery.md)).
|
||||||
- It listed the mesh as spanning a fixed number of named machines, which is exactly the
|
- It listed the mesh as spanning a fixed number of named machines, which is exactly the
|
||||||
content this repository cannot carry.
|
content this repository cannot carry.
|
||||||
|
|
||||||
@@ -44,10 +44,10 @@ symlinks at all — the rule is not merely "only the installer may link", and a
|
|||||||
elevating linking to a principle points the opposite way from where this is going.
|
elevating linking to a principle points the opposite way from where this is going.
|
||||||
|
|
||||||
What exists today is that the installer owns and reconciles every link
|
What exists today is that the installer owns and reconciles every link
|
||||||
([ADR 0018](../02-DECISIONS/0018-the-mesh-creates-no-symlinks.md)) — an as-is fact, recorded in
|
([ADR 0010](../02-DECISIONS/0010-the-mesh-creates-no-symlinks.md)) — an as-is fact, recorded in
|
||||||
[`03-DESIGN/00-as-is/05-runtime-and-installation.md`](../03-DESIGN/00-as-is/05-runtime-and-installation.md).
|
[`03-DESIGN/00-as-is/05-runtime-and-installation.md`](../03-DESIGN/00-as-is/05-runtime-and-installation.md).
|
||||||
Centralising who may link narrowed the incident class; it did not close it. The intent is to
|
Centralising who may link narrowed the incident class; it did not close it. The intent is to
|
||||||
remove the mechanism, recorded as [ADR 0018](../02-DECISIONS/0018-the-mesh-creates-no-symlinks.md).
|
remove the mechanism, recorded as [ADR 0010](../02-DECISIONS/0010-the-mesh-creates-no-symlinks.md).
|
||||||
|
|
||||||
A founding document contradicting the direction of travel is precisely the failure this folder
|
A founding document contradicting the direction of travel is precisely the failure this folder
|
||||||
exists to prevent.
|
exists to prevent.
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ indistinguishable from one that cannot.
|
|||||||
| `links` | every relative link resolves | — (run ad hoc during authoring; now permanent) |
|
| `links` | every relative link resolves | — (run ad hoc during authoring; now permanent) |
|
||||||
| `rests-on` | `decisions:` and `extends:` name records that exist and are **accepted** | the class behind both incidents |
|
| `rests-on` | `decisions:` and `extends:` name records that exist and are **accepted** | the class behind both incidents |
|
||||||
| `live-citation` | a governing document citing a **superseded** record names its replacement in the same paragraph | `01-to-be/README.md` citing ADR 0017 as live guidance |
|
| `live-citation` | a governing document citing a **superseded** record names its replacement in the same paragraph | `01-to-be/README.md` citing ADR 0017 as live guidance |
|
||||||
| `supersession` | if A says it was superseded by B, B says it supersedes A | ADR 0018 never declared that it superseded 0011 |
|
| `supersession` | if A says it was superseded by B, B says it supersedes A | ADR 0010 never declared that it superseded 0011 |
|
||||||
| `numbering` | the number in the filename is the number in the heading | — |
|
| `numbering` | the number in the filename is the number in the heading | — |
|
||||||
|
|
||||||
## What is deliberately not checked
|
## What is deliberately not checked
|
||||||
@@ -31,7 +31,7 @@ indistinguishable from one that cannot.
|
|||||||
having it.
|
having it.
|
||||||
- **`03-DESIGN/00-as-is/` may rest on a superseded record.** It describes what runs, and what
|
- **`03-DESIGN/00-as-is/` may rest on a superseded record.** It describes what runs, and what
|
||||||
runs was built under whatever was decided at the time
|
runs was built under whatever was decided at the time
|
||||||
([ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md):
|
([ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md):
|
||||||
*as-is describing a superseded decision is exactly what as-is is for*).
|
*as-is describing a superseded decision is exactly what as-is is for*).
|
||||||
- **Whether a citation's prose is still true.** Only whether the record it points at is live.
|
- **Whether a citation's prose is still true.** Only whether the record it points at is live.
|
||||||
A document can cite an accepted record and describe it wrongly, and nothing here notices.
|
A document can cite an accepted record and describe it wrongly, and nothing here notices.
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ status: canonical
|
|||||||
updated: 2026-08-23
|
updated: 2026-08-23
|
||||||
derives: knowledge-base constitution page
|
derives: knowledge-base constitution page
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0009-the-mesh-is-governed-by-a-constitution.md
|
- 02-DECISIONS/0005-the-mesh-is-governed-by-a-constitution.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# How we build
|
# How we build
|
||||||
@@ -37,13 +37,13 @@ incident behind it is not written down, and the fix is to write it down, not to
|
|||||||
| Rule | What it means |
|
| Rule | What it means |
|
||||||
|---|---|
|
|---|---|
|
||||||
| **Never write to a production database directly** | No insert, update, delete or schema statement executed against production by hand. Schema changes go through numbered migrations; data changes go through application code or the module's own capabilities. Raw statements skip every side effect the proper path has — events, audit, cache invalidation, fan-out. |
|
| **Never write to a production database directly** | No insert, update, delete or schema statement executed against production by hand. Schema changes go through numbered migrations; data changes go through application code or the module's own capabilities. Raw statements skip every side effect the proper path has — events, audit, cache invalidation, fan-out. |
|
||||||
| **Every schema change is a migration** | Numbered, in the module's own language, compiled with it. Both a baseline for a fresh installation *and* an incremental migration for installations that already exist. If code references a column, the migration creating it must exist. [ADR 0006](../02-DECISIONS/0006-schema-changes-are-numbered-migrations.md) |
|
| **Every schema change is a migration** | Numbered, in the module's own language, compiled with it. Both a baseline for a fresh installation *and* an incremental migration for installations that already exist. If code references a column, the migration creating it must exist. [ADR 0003](../02-DECISIONS/0003-schema-changes-are-numbered-migrations.md) |
|
||||||
| **Never bypass the pipeline** | No manual database edit, no manual restart as a workaround. Fix the cause and deploy. A workaround that works is a workaround that is never removed, and the next person cannot tell the node from its declaration. |
|
| **Never bypass the pipeline** | No manual database edit, no manual restart as a workaround. Fix the cause and deploy. A workaround that works is a workaround that is never removed, and the next person cannot tell the node from its declaration. |
|
||||||
| **Never create a symlink** | A hand-made link caused production data loss through container volume resolution, and the judgement needed to make a safe exception is exactly the judgement unavailable at the moment it matters. **The mesh creates none at all** ([ADR 0018](../02-DECISIONS/0018-the-mesh-creates-no-symlinks.md), which supersedes [ADR 0018](../02-DECISIONS/0018-the-mesh-creates-no-symlinks.md)). The links the installer still reconciles are a migration, not a permission. |
|
| **Never create a symlink** | A hand-made link caused production data loss through container volume resolution, and the judgement needed to make a safe exception is exactly the judgement unavailable at the moment it matters. **The mesh creates none at all** ([ADR 0010](../02-DECISIONS/0010-the-mesh-creates-no-symlinks.md), which supersedes [ADR 0010](../02-DECISIONS/0010-the-mesh-creates-no-symlinks.md)). The links the installer still reconciles are a migration, not a permission. |
|
||||||
| **Never push directly to the main branch** | Branch, push, review, merge. Every merge is a human checkpoint, without exception — **including in this repository**. A documentation repository is not a lower tier of care; a decision record lands the same way a service does. |
|
| **Never push directly to the main branch** | Branch, push, review, merge. Every merge is a human checkpoint, without exception — **including in this repository**. A documentation repository is not a lower tier of care; a decision record lands the same way a service does. |
|
||||||
| **One change per pull request, and never merge unapproved work** | Unrelated improvements bundled together cannot be reviewed or reverted separately. And the checkpoint is **a person deciding, not a person clicking** — work may be merged by whoever wrote it once a human has explicitly approved *that merge*, and never on a standing permission, an instruction to do the work, silence, or the author's own judgement that it is ready. [ADR 0042](../02-DECISIONS/0042-approval-is-the-checkpoint.md) |
|
| **One change per pull request, and never merge unapproved work** | Unrelated improvements bundled together cannot be reviewed or reverted separately. And the checkpoint is **a person deciding, not a person clicking** — work may be merged by whoever wrote it once a human has explicitly approved *that merge*, and never on a standing permission, an instruction to do the work, silence, or the author's own judgement that it is ready. [ADR 0018](../02-DECISIONS/0018-approval-is-the-checkpoint.md) |
|
||||||
| **Never open a pull request unprompted** | A permissions list saying it is allowed is not a request. |
|
| **Never open a pull request unprompted** | A permissions list saying it is allowed is not a request. |
|
||||||
| **A failed step fails the job** | A sequence that continues past a failure does the next thing in the wrong place. Gate each step on the last. [ADR 0058](../02-DECISIONS/0058-delivery.md), and §5. |
|
| **A failed step fails the job** | A sequence that continues past a failure does the next thing in the wrong place. Gate each step on the last. [ADR 0023](../02-DECISIONS/0023-delivery.md), and §5. |
|
||||||
|
|
||||||
### A failed step must stop the steps after it — how it was earned
|
### A failed step must stop the steps after it — how it was earned
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ reported failure, nothing stopped, and the damage happened somewhere nobody was
|
|||||||
- **Every runtime variable is declared.** A variable the module reads and the manifest does not
|
- **Every runtime variable is declared.** A variable the module reads and the manifest does not
|
||||||
declare is invisible to the mesh: it will not be generated, injected, or audited.
|
declare is invisible to the mesh: it will not be generated, injected, or audited.
|
||||||
- **Provisioned credentials arrive through declared requirements**, never hardcoded in code,
|
- **Provisioned credentials arrive through declared requirements**, never hardcoded in code,
|
||||||
compose files or scripts. [ADR 0044](../02-DECISIONS/0044-modules-and-the-graph.md)
|
compose files or scripts. [ADR 0019](../02-DECISIONS/0019-modules-and-the-graph.md)
|
||||||
- **Never install a package by hand.** A package is declared in the manifest and arrives the
|
- **Never install a package by hand.** A package is declared in the manifest and arrives the
|
||||||
way every other package does. A hand-installed package is invisible to the mesh: it is not
|
way every other package does. A hand-installed package is invisible to the mesh: it is not
|
||||||
declared, not reproduced on the next node, and not present after a rebuild — and the node
|
declared, not reproduced on the next node, and not present after a rebuild — and the node
|
||||||
@@ -82,7 +82,7 @@ reported failure, nothing stopped, and the damage happened somewhere nobody was
|
|||||||
- **Every standalone application gets its own repository**, with a manifest at its root,
|
- **Every standalone application gets its own repository**, with a manifest at its root,
|
||||||
registered as a build source. Creating an application directory in the monorepo is a
|
registered as a build source. Creating an application directory in the monorepo is a
|
||||||
convention violation and reviewers reject it.
|
convention violation and reviewers reject it.
|
||||||
[ADR 0010](../02-DECISIONS/0010-applications-live-in-their-own-repository.md)
|
[ADR 0006](../02-DECISIONS/0006-applications-live-in-their-own-repository.md)
|
||||||
|
|
||||||
### Migrations
|
### Migrations
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ reported failure, nothing stopped, and the damage happened somewhere nobody was
|
|||||||
surface is regenerated from the mesh database; a local edit survives one synchronisation and is
|
surface is regenerated from the mesh database; a local edit survives one synchronisation and is
|
||||||
then silently overwritten, bringing back whatever it fixed. Use the mesh operation that owns
|
then silently overwritten, bringing back whatever it fixed. Use the mesh operation that owns
|
||||||
the value. If unsure whether a file is managed, ask the tooling — the answer is not visible
|
the value. If unsure whether a file is managed, ask the tooling — the answer is not visible
|
||||||
from the file. [ADR 0004](../02-DECISIONS/0004-managed-files-are-generated-never-edited.md)
|
from the file. [ADR 0002](../02-DECISIONS/0002-managed-files-are-generated-never-edited.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ for them. Do not merge them into one module: they are delivered to different nod
|
|||||||
that must be assigned where half of it is unwanted is not a boundary either.
|
that must be assigned where half of it is unwanted is not a boundary either.
|
||||||
|
|
||||||
Coherence is a context. Delivery is a module. Relationships are edges, not folders.
|
Coherence is a context. Delivery is a module. Relationships are edges, not folders.
|
||||||
[ADR 0044](../02-DECISIONS/0044-modules-and-the-graph.md)
|
[ADR 0019](../02-DECISIONS/0019-modules-and-the-graph.md)
|
||||||
|
|
||||||
### Contexts integrate through the record, never through a shared schema
|
### Contexts integrate through the record, never through a shared schema
|
||||||
|
|
||||||
@@ -226,7 +226,7 @@ No drive-by edits. Every change traces to a recorded decision.
|
|||||||
a design meeting with at least two node operators — which has never been met and cannot be, as
|
a design meeting with at least two node operators — which has never been met and cannot be, as
|
||||||
there is one operator. A rule that cannot be satisfied is not a high standard; it is a rule
|
there is one operator. A rule that cannot be satisfied is not a high standard; it is a rule
|
||||||
everything silently violates. Recorded here as resolved in favour of what is achievable, and
|
everything silently violates. Recorded here as resolved in favour of what is achievable, and
|
||||||
what has in fact been practised ([ADR 0040](../02-DECISIONS/0040-the-constitution-absorbs-what-is-enforced.md)).
|
what has in fact been practised ([ADR 0017](../02-DECISIONS/0017-the-constitution-absorbs-what-is-enforced.md)).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -243,7 +243,7 @@ process. Absence of an override means these rules apply unmodified.
|
|||||||
## 8. Code quality
|
## 8. Code quality
|
||||||
|
|
||||||
*Absorbed 2026-08-26 from the enforced page, which carried these rules while this document did
|
*Absorbed 2026-08-26 from the enforced page, which carried these rules while this document did
|
||||||
not — [ADR 0040](../02-DECISIONS/0040-the-constitution-absorbs-what-is-enforced.md).*
|
not — [ADR 0017](../02-DECISIONS/0017-the-constitution-absorbs-what-is-enforced.md).*
|
||||||
|
|
||||||
**These rules are recorded because they are enforced, not because this repository earned them.**
|
**These rules are recorded because they are enforced, not because this repository earned them.**
|
||||||
Every other rule here states the incident or measurement behind it. These state nothing,
|
Every other rule here states the incident or measurement behind it. These state nothing,
|
||||||
@@ -272,7 +272,7 @@ Data access, business logic and the interface layer are separate.
|
|||||||
|
|
||||||
*Scope: the mesh's services and surfaces. Tier 0 is a statically linked binary that must depend
|
*Scope: the mesh's services and surfaces. Tier 0 is a statically linked binary that must depend
|
||||||
on nothing installed first, and is written in Go —
|
on nothing installed first, and is written in Go —
|
||||||
[ADR 0037](../02-DECISIONS/0037-the-node-host.md).*
|
[ADR 0016](../02-DECISIONS/0016-the-node-host.md).*
|
||||||
|
|
||||||
- TypeScript throughout; no new untyped JavaScript.
|
- TypeScript throughout; no new untyped JavaScript.
|
||||||
- Strict, with no implicit `any` and no unchecked index access.
|
- Strict, with no implicit `any` and no unchecked index access.
|
||||||
|
|||||||
+7
-7
@@ -15,26 +15,26 @@ and a forge address is an operational detail (see [`README`](../README.md)).
|
|||||||
| Repository | Owns |
|
| Repository | Owns |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `hal` | The monorepo — the node runtime, the module catalogue, the delivery machinery, and the bootstrap scripts. Every core module lives here. |
|
| `hal` | The monorepo — the node runtime, the module catalogue, the delivery machinery, and the bootstrap scripts. Every core module lives here. |
|
||||||
| `hq` | This repository, under the company organisation — mission, research, design, decisions, issue diagnosis. Company-scoped ([ADR 0019](../02-DECISIONS/0019-how-this-repository-works.md)); the mesh is its first product. The source of truth for *why*. Carries no implementation. |
|
| `hq` | This repository, under the company organisation — mission, research, design, decisions, issue diagnosis. Company-scoped ([ADR 0011](../02-DECISIONS/0011-how-this-repository-works.md)); the mesh is its first product. The source of truth for *why*. Carries no implementation. |
|
||||||
| *(one per application)* | Every standalone application, site or side-project gets its own repository, with `module.yml` at the root. Registered with the mesh as a build source; built and deployed by the same pipeline as anything in the monorepo. |
|
| *(one per application)* | Every standalone application, site or side-project gets its own repository, with `module.yml` at the root. Registered with the mesh as a build source; built and deployed by the same pipeline as anything in the monorepo. |
|
||||||
|
|
||||||
## What the mesh becomes
|
## What the mesh becomes
|
||||||
|
|
||||||
[ADR 0019](../02-DECISIONS/0019-how-this-repository-works.md) records the repositories the
|
[ADR 0011](../02-DECISIONS/0011-how-this-repository-works.md) records the repositories the
|
||||||
monorepo decomposes into. **`mesh-lab` and `mesh-host` exist so far** — the lab is built first
|
monorepo decomposes into. **`mesh-lab` and `mesh-host` exist so far** — the lab is built first
|
||||||
([ADR 0016](../02-DECISIONS/0016-the-lab.md)); the rest are the
|
([ADR 0009](../02-DECISIONS/0009-the-lab.md)); the rest are the
|
||||||
target, not the present.
|
target, not the present.
|
||||||
|
|
||||||
| Repository | Tier | Holds |
|
| Repository | Tier | Holds |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `mesh-host` | 0 | **exists.** The node host — one statically linked binary, requiring nothing present ([ADR 0037](../02-DECISIONS/0037-the-node-host.md)) |
|
| `mesh-host` | 0 | **exists.** The node host — one statically linked binary, requiring nothing present ([ADR 0016](../02-DECISIONS/0016-the-node-host.md)) |
|
||||||
| `mesh-substrate` | 1 | the four pinned services, as declarations |
|
| `mesh-substrate` | 1 | the four pinned services, as declarations |
|
||||||
| `mesh-control` | 2 | the control plane and its contexts |
|
| `mesh-control` | 2 | the control plane and its contexts |
|
||||||
| `mesh-surfaces` | 3 | tools, web, cli |
|
| `mesh-surfaces` | 3 | tools, web, cli |
|
||||||
| `mesh-sdk` | — | contracts shared across tiers |
|
| `mesh-sdk` | — | contracts shared across tiers |
|
||||||
| `mesh-lab` | — | **exists.** The lab — scenario lifecycle, networking, placement. Ships to nobody; runs on a workstation. |
|
| `mesh-lab` | — | **exists.** The lab — scenario lifecycle, networking, placement. Ships to nobody; runs on a workstation. |
|
||||||
|
|
||||||
Tier 4's shape is open, and deliberately so: see ADR 0030 and
|
Tier 4's shape is open, and deliberately so: see ADR 0011 and
|
||||||
[research 005](../01-RESEARCH/005-domain-grouping/00-overview.md).
|
[research 005](../01-RESEARCH/005-domain-grouping/00-overview.md).
|
||||||
|
|
||||||
## What lives where inside the monorepo
|
## What lives where inside the monorepo
|
||||||
@@ -53,7 +53,7 @@ Named by role, because the layout is itself part of the as-is design — see
|
|||||||
## Why applications do not live in the monorepo
|
## Why applications do not live in the monorepo
|
||||||
|
|
||||||
A standalone application in the monorepo is a convention violation, and reviewers reject it.
|
A standalone application in the monorepo is a convention violation, and reviewers reject it.
|
||||||
The reasoning is recorded in [`02-DECISIONS/0010`](../02-DECISIONS/0010-applications-live-in-their-own-repository.md):
|
The reasoning is recorded in [`02-DECISIONS/0010`](../02-DECISIONS/0006-applications-live-in-their-own-repository.md):
|
||||||
the mesh installs, provisions for, and ships an application through exactly the same machinery
|
the mesh installs, provisions for, and ships an application through exactly the same machinery
|
||||||
whether or not its source sits beside the mesh's own — so co-location buys nothing and costs
|
whether or not its source sits beside the mesh's own — so co-location buys nothing and costs
|
||||||
the monorepo's review cadence.
|
the monorepo's review cadence.
|
||||||
@@ -64,7 +64,7 @@ Each module is a standalone package that consumes its dependencies from the priv
|
|||||||
not from a sibling directory. The workspace was removed after it caused build-versus-development
|
not from a sibling directory. The workspace was removed after it caused build-versus-development
|
||||||
divergence — a workspace member importing another resolved to local unbuilt source in the
|
divergence — a workspace member importing another resolved to local unbuilt source in the
|
||||||
pipeline and to a published version in development. Recorded in
|
pipeline and to a published version in development. Recorded in
|
||||||
[`02-DECISIONS/0007`](../02-DECISIONS/0007-no-npm-workspace.md).
|
[`02-DECISIONS/0007`](../02-DECISIONS/0004-no-npm-workspace.md).
|
||||||
|
|
||||||
Consequence, and it is a real one: a cross-package change is two steps — publish, then consume
|
Consequence, and it is a real one: a cross-package change is two steps — publish, then consume
|
||||||
— and a repository-wide `npm install` does not exist.
|
— and a repository-wide `npm install` does not exist.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
status: active
|
status: active
|
||||||
initiated: 2026-08-22
|
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]
|
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/0015-mesh-brokers-nodes-host-agents-think.md]
|
became: [02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md]
|
||||||
---
|
---
|
||||||
|
|
||||||
# 001 — Module domain decomposition
|
# 001 — Module domain decomposition
|
||||||
@@ -54,7 +54,7 @@ Tracked in [`analysis.md`](analysis.md) under "Open questions".
|
|||||||
## Deliberately not decided
|
## Deliberately not decided
|
||||||
|
|
||||||
Recorded so they are not mistaken for oversights. Each is open, and each comes out of
|
Recorded so they are not mistaken for oversights. Each is open, and each comes out of
|
||||||
[ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md); this effort stays
|
[ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md); this effort stays
|
||||||
`active` until they are answered.
|
`active` until they are answered.
|
||||||
|
|
||||||
| Question | Status |
|
| 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. |
|
| Catalogue destination — one repository or many. | Open. Phase 4. |
|
||||||
| What the shared library keeps after extraction. | Open. Phase 3. |
|
| 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. |
|
| 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 0044](../../02-DECISIONS/0044-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 0019](../../02-DECISIONS/0019-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?
|
pipeline resolves dependencies across the registry rather than the filesystem?
|
||||||
4. **SDK residue** — after extraction, does `hal/sdk` keep transport (`amqp-client`), or
|
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.
|
does that belong to `hal/stream`? Everything imports it, which argues both ways.
|
||||||
5. **Human agent modality.** ADR 0015 requires a fact the mesh does not record: which
|
5. **Human agent modality.** ADR 0008 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
|
user, on which node, a human agent acts as. Where does it live — an attribute of the
|
||||||
agent, or of the agent-node binding?
|
agent, or of the agent-node binding?
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
status: graduated
|
status: graduated
|
||||||
initiated: 2026-08-22
|
initiated: 2026-08-22
|
||||||
touches: [03-DESIGN/00-as-is/04-delivery.md, 03-DESIGN/00-as-is/05-runtime-and-installation.md]
|
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/0016-the-lab.md]
|
became: [03-DESIGN/01-to-be/01-end-to-end-testing.md, 02-DECISIONS/0009-the-lab.md]
|
||||||
---
|
---
|
||||||
|
|
||||||
# 002 — A mesh that runs locally
|
# 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
|
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
|
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
|
defined today as a systemd unit shelling to `docker compose` in `/services/` — is not
|
||||||
answered by ADR 0015 and is recorded below rather than decided.
|
answered by ADR 0008 and is recorded below rather than decided.
|
||||||
|
|
||||||
## What was established
|
## What was established
|
||||||
|
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ over either way.
|
|||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- [`02-DECISIONS/0001`](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md) — the decision this
|
- [`02-DECISIONS/0001`](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md) — the decision this
|
||||||
phase unblocks
|
phase unblocks
|
||||||
- [`03-DESIGN/00-work-breakdown.md`](../../03-DESIGN/01-to-be/00-work-breakdown.md) — Phase 0 tasks
|
- [`03-DESIGN/00-work-breakdown.md`](../../03-DESIGN/01-to-be/00-work-breakdown.md) — Phase 0 tasks
|
||||||
and checkpoint
|
and checkpoint
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ status: graduated
|
|||||||
initiated: 2026-08-22
|
initiated: 2026-08-22
|
||||||
touches: [03-DESIGN/00-as-is/05-runtime-and-installation.md]
|
touches: [03-DESIGN/00-as-is/05-runtime-and-installation.md]
|
||||||
became:
|
became:
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 03-DESIGN/01-to-be/05-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:
|
- **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
|
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.
|
production then have the same shape rather than a translation layer between them.
|
||||||
- **It cannot be all-or-nothing**, and ADR 0015 already says why: a human agent acts through a
|
- **It cannot be all-or-nothing**, and ADR 0008 already says why: a human agent acts through a
|
||||||
shell and a desktop. Those parts are on the host by definition.
|
shell and a desktop. Those parts are on the host by definition.
|
||||||
- One incidental finding: the automatic node rescue that documentation describes **does not
|
- 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.
|
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.
|
finding that is the point of a sweep.
|
||||||
|
|
||||||
**The third option is what the mesh adopted.** `Docker is the supervisor for everything` is
|
**The third option is what the mesh adopted.** `Docker is the supervisor for everything` is
|
||||||
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md): the
|
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md): the
|
||||||
host is a plain process on the machine and everything above tier 0 is a container. The substrate
|
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
|
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.
|
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
|
**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
|
mesh-native supervisor inherits the problem *unless it sits outside the mesh's own process
|
||||||
tree*. [ADR 0037](../../02-DECISIONS/0037-the-node-host.md) puts
|
tree*. [ADR 0016](../../02-DECISIONS/0016-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
|
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.
|
cannot start is still recovered.
|
||||||
|
|
||||||
|
|||||||
@@ -128,10 +128,10 @@ What it costs, honestly:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. Why it cannot be all-or-nothing — and ADR 0015 already says so
|
## 5. Why it cannot be all-or-nothing — and ADR 0008 already says so
|
||||||
|
|
||||||
Some of what runs under systemd today **cannot** be containerised, and the reason is
|
Some of what runs under systemd today **cannot** be containerised, and the reason is
|
||||||
already in the domain model. ADR 0015:
|
already in the domain model. ADR 0008:
|
||||||
|
|
||||||
> a non-human agent acts through a spawned session — a human agent acts through a shell or
|
> a non-human agent acts through a spawned session — a human agent acts through a shell or
|
||||||
> desktop
|
> desktop
|
||||||
@@ -223,7 +223,7 @@ fate-sharing reason in §3.
|
|||||||
## References
|
## References
|
||||||
|
|
||||||
- [`002-local-mesh`](../002-local-mesh/analysis.md) — the effort this came out of
|
- [`002-local-mesh`](../002-local-mesh/analysis.md) — the effort this came out of
|
||||||
- [`02-DECISIONS/0001`](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md) — agent modality, which
|
- [`02-DECISIONS/0001`](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md) — agent modality, which
|
||||||
decides what cannot leave the host
|
decides what cannot leave the host
|
||||||
- `modules/hal/meshware/daemon/src/cerebellum.ts:815-828` — the self-restart workaround
|
- `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
|
- `modules/hal/meshware/systemd/hal-module@.service` — the per-module Docker lifecycle
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ status: graduated
|
|||||||
initiated: 2026-08-22
|
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]
|
touches: [03-DESIGN/00-as-is/01-mesh-and-transport.md, 03-DESIGN/01-to-be/01-end-to-end-testing.md]
|
||||||
became:
|
became:
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 03-DESIGN/01-to-be/02-scenario-declaration.md
|
- 03-DESIGN/01-to-be/02-scenario-declaration.md
|
||||||
- 03-DESIGN/01-to-be/01-end-to-end-testing.md
|
- 03-DESIGN/01-to-be/01-end-to-end-testing.md
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -2,17 +2,17 @@
|
|||||||
status: graduated
|
status: graduated
|
||||||
initiated: 2026-08-23
|
initiated: 2026-08-23
|
||||||
touches:
|
touches:
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 03-DESIGN/00-as-is/10-module-catalogue.md
|
- 03-DESIGN/00-as-is/10-module-catalogue.md
|
||||||
- 03-DESIGN/00-as-is/02-modules-and-manifests.md
|
- 03-DESIGN/00-as-is/02-modules-and-manifests.md
|
||||||
became:
|
became:
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 005 — Which domains the catalogue groups into
|
# 005 — Which domains the catalogue groups into
|
||||||
|
|
||||||
[ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md) settles
|
[ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md) settles
|
||||||
that modules outside the platform core are grouped by domain rather than by single function,
|
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
|
and deliberately does not settle the list. This effort settles the list — and, first, tests
|
||||||
whether the premise survives measurement.
|
whether the premise survives measurement.
|
||||||
@@ -59,12 +59,12 @@ open questions below.
|
|||||||
this effort — which is why it stayed open after being resolved.
|
this effort — which is why it stayed open after being resolved.
|
||||||
|
|
||||||
**Whether provider modules group at all** — *no.*
|
**Whether provider modules group at all** — *no.*
|
||||||
[ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md):
|
[ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md):
|
||||||
there is no `networking` thing to install, there are concrete modules named individually. Folders
|
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.
|
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
|
**Whether "group or leave" is even the right pair of options** — *it was not*, and that is the
|
||||||
useful finding. [ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md)
|
useful finding. [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)
|
||||||
reframes it: things that change together share an **authority**, not a package. This effort's own
|
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
|
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.
|
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
|
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
|
to the set of modules it touched. Platform-namespace modules are excluded — their
|
||||||
decomposition is settled by
|
decomposition is settled by
|
||||||
[ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md). Modules that no
|
[ADR 0008](../../02-DECISIONS/0008-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
|
longer exist are excluded, because pre-rename names dominate the raw signal and describe a
|
||||||
catalogue nobody works in.
|
catalogue nobody works in.
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
status: active
|
status: active
|
||||||
initiated: 2026-08-23
|
initiated: 2026-08-23
|
||||||
touches:
|
touches:
|
||||||
- 02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md
|
- 02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 03-DESIGN/00-as-is/00-overview.md
|
- 03-DESIGN/00-as-is/00-overview.md
|
||||||
- 03-DESIGN/01-to-be/00-work-breakdown.md
|
- 03-DESIGN/01-to-be/00-work-breakdown.md
|
||||||
became: []
|
became: []
|
||||||
@@ -24,9 +24,9 @@ disk, and where today's catalogue lands.
|
|||||||
## Why
|
## Why
|
||||||
|
|
||||||
Every structural decision so far has been a **correction**: eight contexts replacing thirty-three
|
Every structural decision so far has been a **correction**: eight contexts replacing thirty-three
|
||||||
modules ([ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md)), domains
|
modules ([ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md)), domains
|
||||||
replacing single-function modules
|
replacing single-function modules
|
||||||
([ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md)). A
|
([ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)). A
|
||||||
correction inherits the frame of the thing it corrects, and two of the mesh's oldest problems
|
correction inherits the frame of the thing it corrects, and two of the mesh's oldest problems
|
||||||
look unsolvable from inside that frame:
|
look unsolvable from inside that frame:
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ against taste:
|
|||||||
circle, self-hosted. Personal cloud infrastructure.
|
circle, self-hosted. Personal cloud infrastructure.
|
||||||
8. **Agents make it self-improving and self-healing.**
|
8. **Agents make it self-improving and self-healing.**
|
||||||
9. It is **end-to-end testable on one machine**
|
9. It is **end-to-end testable on one machine**
|
||||||
([ADR 0016](../../02-DECISIONS/0016-the-lab.md)).
|
([ADR 0009](../../02-DECISIONS/0009-the-lab.md)).
|
||||||
|
|
||||||
## Status
|
## 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
|
`active` because two of them are unproven and one contradicts a record that is already
|
||||||
accepted.
|
accepted.
|
||||||
|
|
||||||
**Finding worth stating up front:** [ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md)
|
**Finding worth stating up front:** [ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md)
|
||||||
names nine bounded contexts and **none of them owns connectivity** — no overlay, no resolution,
|
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
|
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
|
[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 |
|
| 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. |
|
| 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 0015 as "catalogue destination — one repository or many". The skeleton assumes per tier and does not settle it. |
|
| 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 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md). |
|
| ~~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 0037](../../02-DECISIONS/0037-the-node-host.md), designed in [`05-the-node-host.md`](../../03-DESIGN/01-to-be/05-the-node-host.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). |
|
||||||
| ~~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. |
|
| ~~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. |
|
| 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 |
|
| **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 |
|
| **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 |
|
| **Control-plane context** | It decides something across nodes. | connectivity policy, inventory, delivery, provisioning, observability |
|
||||||
| **Workload module** | The mesh hosts it. Grouped per [ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md). | media library, desktop session, collaboration tooling |
|
| **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 0010](../../02-DECISIONS/0010-applications-live-in-their-own-repository.md). | the applications identified in research 005 |
|
| **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 |
|
||||||
|
|
||||||
**The first fate is the finding.** Research 005 measured the reachability cluster — proxy,
|
**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
|
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
|
mesh-catalog/ TIER 4
|
||||||
<domain>/<module>/ layout as above
|
<domain>/<module>/ layout as above
|
||||||
|
|
||||||
mesh-lab/ mesh-sdk/ hq/ (company-scoped — ADR 0028)
|
mesh-lab/ mesh-sdk/ hq/ (company-scoped — ADR 0011)
|
||||||
```
|
```
|
||||||
|
|
||||||
## What this does not settle
|
## What this does not settle
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ mesh-catalog/ TIER 4 — what the mesh hosts
|
|||||||
mesh-lab/ the whole mesh, disposable, on one machine
|
mesh-lab/ the whole mesh, disposable, on one machine
|
||||||
mesh-sdk/ contracts shared across tiers — types, not behaviour
|
mesh-sdk/ contracts shared across tiers — types, not behaviour
|
||||||
|
|
||||||
hq/ company-scoped, not a mesh repository — ADR 0028
|
hq/ company-scoped, not a mesh repository — ADR 0011
|
||||||
```
|
```
|
||||||
|
|
||||||
## The dependency rule
|
## 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
|
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
|
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
|
rule enforced by intention is the same as no tier rule — that is
|
||||||
[ADR 0058](../../02-DECISIONS/0058-delivery.md) applied to architecture.
|
[ADR 0023](../../02-DECISIONS/0023-delivery.md) applied to architecture.
|
||||||
|
|
||||||
## Move 1 — the substrate is applied, not delivered
|
## 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
|
## Move 3 — connectivity becomes a context
|
||||||
|
|
||||||
[ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md) names nine contexts
|
[ADR 0008](../../02-DECISIONS/0008-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
|
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.
|
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
|
- certificates for both name spaces
|
||||||
|
|
||||||
This is an addition to an accepted record, so it is a decision, not a drafting choice. It
|
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 0015 the way
|
belongs in a new record that extends ADR 0008 the way
|
||||||
[ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md) does —
|
[ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md) does —
|
||||||
not written here.
|
not written here.
|
||||||
|
|
||||||
### Where the networking actually lives
|
### 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
|
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
|
delivery pipeline is hard to reason about precisely because those have different cardinality
|
||||||
and one word ([ADR 0058](../../02-DECISIONS/0058-delivery.md)
|
and one word ([ADR 0023](../../02-DECISIONS/0023-delivery.md)
|
||||||
is the pipeline half of the same confusion).
|
is the pipeline half of the same confusion).
|
||||||
|
|
||||||
Split it:
|
Split it:
|
||||||
@@ -230,7 +230,7 @@ the fact that it runs its own development on them is dogfooding, not architectur
|
|||||||
## What agents are, structurally
|
## What agents are, structurally
|
||||||
|
|
||||||
Self-improvement and self-healing are not a tier. Agents are participants
|
Self-improvement and self-healing are not a tier. Agents are participants
|
||||||
([ADR 0012](../../02-DECISIONS/0012-agents-are-persistent-employees.md)) that hold identity in
|
([ADR 0007](../../02-DECISIONS/0007-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.
|
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
|
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
|
## How this is tested
|
||||||
|
|
||||||
The lab ([ADR 0016](../../02-DECISIONS/0016-the-lab.md)) raises the
|
The lab ([ADR 0009](../../02-DECISIONS/0009-the-lab.md)) raises the
|
||||||
tree above on one machine: virtual machines as nodes, a real overlay between them, the real
|
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.
|
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
|
- 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.
|
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 0015 already.
|
- Whether tier 2's contexts are one repository or several. Open from ADR 0008 already.
|
||||||
- Whether an `edge` node is in the inventory or merely present — which decides whether "node"
|
- Whether an `edge` node is in the inventory or merely present — which decides whether "node"
|
||||||
is one concept or two.
|
is one concept or two.
|
||||||
- The migration. Nothing here says how today's mesh becomes this, and the skeleton is worth
|
- 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
|
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
|
[`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
|
first-class concept in this mesh — a participant, some of whom are human, holding identity and
|
||||||
memory ([ADR 0012](../../02-DECISIONS/0012-agents-are-persistent-employees.md)). One document
|
memory ([ADR 0007](../../02-DECISIONS/0007-agents-are-persistent-employees.md)). One document
|
||||||
carried both meanings.
|
carried both meanings.
|
||||||
|
|
||||||
It is the same failure as the anatomy naming in the current runtime: an evocative domain word
|
It is the same failure as the anatomy naming in the current runtime: an evocative domain word
|
||||||
pointing at infrastructure.
|
pointing at infrastructure.
|
||||||
|
|
||||||
[ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md) supplies the fix in
|
[ADR 0008](../../02-DECISIONS/0008-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
|
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**
|
components: the control plane **brokers** (`mesh-control`), the tier-0 binary **hosts**
|
||||||
(`mesh-host`), the participant **thinks** (`agents`, untouched).
|
(`mesh-host`), the participant **thinks** (`agents`, untouched).
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ status: active
|
|||||||
initiated: 2026-08-23
|
initiated: 2026-08-23
|
||||||
touches:
|
touches:
|
||||||
- 03-DESIGN/00-as-is/03-provisioning.md
|
- 03-DESIGN/00-as-is/03-provisioning.md
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 01-RESEARCH/006-mesh-from-scratch/code-skeleton.md
|
- 01-RESEARCH/006-mesh-from-scratch/code-skeleton.md
|
||||||
became: []
|
became: []
|
||||||
---
|
---
|
||||||
@@ -14,7 +14,7 @@ became: []
|
|||||||
|
|
||||||
Provisioning is the mechanism the whole mesh rests on: a module declares what it needs, and the
|
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
|
mesh makes it exist, generates the credential, records the grant, and puts the values where the
|
||||||
module will read them. [ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md)
|
module will read them. [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)
|
||||||
calls it the mesh's core concern rather than its plumbing.
|
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
|
[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
|
initiated: 2026-08-23
|
||||||
touches:
|
touches:
|
||||||
- 03-DESIGN/00-as-is/04-delivery.md
|
- 03-DESIGN/00-as-is/04-delivery.md
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
- 01-RESEARCH/006-mesh-from-scratch/code-skeleton.md
|
- 01-RESEARCH/006-mesh-from-scratch/code-skeleton.md
|
||||||
became:
|
became:
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 008 — The coordinator: a change checked in becomes a deployed state
|
# 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.
|
because the first was honest about what it did not fix.
|
||||||
|
|
||||||
**Does the coordinator dispatch stages, or converge nodes on a declaration?** — *Converge.*
|
**Does the coordinator dispatch stages, or converge nodes on a declaration?** — *Converge.*
|
||||||
[ADR 0058](../../02-DECISIONS/0058-delivery.md): a pipeline ends when the
|
[ADR 0023](../../02-DECISIONS/0023-delivery.md): a pipeline ends when the
|
||||||
declaration is updated, and the host applies it and reads back — so the reporter is the applier.
|
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
|
**Does the three-silo split survive?** — *Yes, with the third redefined.* The cardinality
|
||||||
observation holds; the third silo is not a stage any more.
|
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.*
|
**How does a change become a pipeline, reliably?** — *It does not become a pipeline at all.*
|
||||||
[ADR 0058](../../02-DECISIONS/0058-delivery.md) applies 0058's
|
[ADR 0023](../../02-DECISIONS/0023-delivery.md) applies 0058's
|
||||||
move one level up: the control plane holds what source exists and what has been built, and builds
|
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
|
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.
|
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
|
## What is NOT closed by this
|
||||||
|
|
||||||
[ADR 0058](../../02-DECISIONS/0058-delivery.md) names four costs
|
[ADR 0023](../../02-DECISIONS/0023-delivery.md) names four costs
|
||||||
and one of them is a real risk rather than a trade: **a reconciler that cannot reach its target
|
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
|
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
|
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. |
|
| 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. |
|
| 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. |
|
| 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 0016](../../02-DECISIONS/0016-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 0009](../../02-DECISIONS/0009-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. |
|
| 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 0058](../../02-DECISIONS/0058-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 0023](../../02-DECISIONS/0023-delivery.md) is cardinality-driven, and research 006 renames the thing the cardinality is about. |
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ initiated: 2026-08-23
|
|||||||
touches:
|
touches:
|
||||||
- 01-RESEARCH/006-mesh-from-scratch/code-skeleton.md
|
- 01-RESEARCH/006-mesh-from-scratch/code-skeleton.md
|
||||||
- 03-DESIGN/01-to-be/01-end-to-end-testing.md
|
- 03-DESIGN/01-to-be/01-end-to-end-testing.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 03-DESIGN/00-as-is/00-overview.md
|
- 03-DESIGN/00-as-is/00-overview.md
|
||||||
became: []
|
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
|
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.
|
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.
|
- **Nothing external depends on it.** No users outside the operator, no service level to hold.
|
||||||
- **The lab exists precisely for this** ([ADR 0016](../../02-DECISIONS/0016-the-lab.md)).
|
- **The lab exists precisely for this** ([ADR 0009](../../02-DECISIONS/0009-the-lab.md)).
|
||||||
A big-bang that has been rehearsed end to end, repeatedly, on identical machines is not the
|
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
|
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
|
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 |
|
| Phase | What | Done when |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **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. |
|
| **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. |
|
||||||
| 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. |
|
| 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. |
|
| 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. |
|
| 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
|
initiated: 2026-08-24
|
||||||
touches:
|
touches:
|
||||||
- 03-DESIGN/01-to-be/03-scenario-lifecycle.md
|
- 03-DESIGN/01-to-be/03-scenario-lifecycle.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
became: []
|
became: []
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ Measured on a workstation, 2026-08-24. Numbers in [`measurements.md`](measuremen
|
|||||||
|
|
||||||
## Why it matters
|
## Why it matters
|
||||||
|
|
||||||
[ADR 0016](../../02-DECISIONS/0016-the-lab.md) makes the
|
[ADR 0009](../../02-DECISIONS/0009-the-lab.md) makes the
|
||||||
bootstrap scenario the inner development loop for tiers 0 and 1 — the argument being that
|
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
|
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
|
one. **That argument is only true if raising and resetting are cheap.** A loop that costs
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ image.
|
|||||||
|
|
||||||
| Fact | Value | Consequence |
|
| Fact | Value | Consequence |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 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 |
|
| 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 |
|
||||||
| Storage drivers the daemon offers | **`dir` only** | no copy-on-write, therefore no cheap snapshot |
|
| 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 |
|
| Host filesystems | ext4 throughout | nothing copy-on-write to put a pool on |
|
||||||
| btrfs kernel module | **available** | the kernel can do it |
|
| 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.
|
**This is too slow for an inner loop**, and the reason is not the design.
|
||||||
|
|
||||||
[ADR 0016](../../02-DECISIONS/0016-the-lab.md) argues that
|
[ADR 0009](../../02-DECISIONS/0009-the-lab.md) argues that
|
||||||
making the bootstrap path the inner development loop turns the least-exercised code in the
|
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
|
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
|
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** |
|
| **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
|
At fifteen seconds, dominated by a boot that cannot be avoided, the inner loop is viable and
|
||||||
[ADR 0016](../../02-DECISIONS/0016-the-lab.md)'s argument holds.
|
[ADR 0009](../../02-DECISIONS/0009-the-lab.md)'s argument holds.
|
||||||
At ninety it did not.
|
At ninety it did not.
|
||||||
|
|
||||||
### One honest counter-observation
|
### One honest counter-observation
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
status: graduated
|
status: graduated
|
||||||
initiated: 2026-08-25
|
initiated: 2026-08-25
|
||||||
became:
|
became:
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0045-a-context-owns-its-store.md
|
- 02-DECISIONS/0020-a-context-owns-its-store.md
|
||||||
- 03-DESIGN/01-to-be/06-the-control-plane.md
|
- 03-DESIGN/01-to-be/06-the-control-plane.md
|
||||||
- 03-DESIGN/01-to-be/07-the-substrate.md
|
- 03-DESIGN/01-to-be/07-the-substrate.md
|
||||||
touches:
|
touches:
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0036-a-node-and-how-it-joins.md
|
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
|
||||||
- 03-DESIGN/00-as-is/02-modules-and-manifests.md
|
- 03-DESIGN/00-as-is/02-modules-and-manifests.md
|
||||||
- 03-DESIGN/00-as-is/10-module-catalogue.md
|
- 03-DESIGN/00-as-is/10-module-catalogue.md
|
||||||
- 04-ISSUES/007-an-installed-package-is-not-a-capability/00-report.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
|
> *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*
|
> 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.
|
> — and that is a **build** edge, fixed inside an artifact rather than negotiated when it runs.
|
||||||
> Recorded by [ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md), which also
|
> Recorded by [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md), which also
|
||||||
> notes what this effort's three entities turn out to be good for
|
> notes what this effort's three entities turn out to be good for
|
||||||
> ([ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md)).
|
> ([ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)).
|
||||||
|
|
||||||
## What is being investigated
|
## What is being investigated
|
||||||
|
|
||||||
@@ -76,10 +76,10 @@ concluded — `provider:` is a dependency edge that is not read as one, which ma
|
|||||||
for a working mesh come out without a database; and the resolver continues past a cycle and
|
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 0008.
|
||||||
|
|
||||||
[ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md) proposes
|
[ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md) proposes
|
||||||
grouping modules by domain. [Research 005](../005-domain-grouping/analysis.md) measured that
|
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
|
proposal and found its evidence holds in exactly one place — reachability — which
|
||||||
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md) has since absorbed
|
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md) has since absorbed
|
||||||
into the host. The measured case for domain grouping has therefore been consumed by a decision
|
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.
|
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
|
Filing decisions that follow from nothing are the disease research 005 measured. A second
|
||||||
taxonomy would reproduce it.
|
taxonomy would reproduce it.
|
||||||
|
|
||||||
So [ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md) survives, and the question
|
So [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md) survives, and the question
|
||||||
becomes what a module must be able to **declare**.
|
becomes what a module must be able to **declare**.
|
||||||
|
|
||||||
## The shape being investigated
|
## The shape being investigated
|
||||||
@@ -111,7 +111,7 @@ Five declarations, of which two exist today.
|
|||||||
|
|
||||||
| Declaration | Today | Notes |
|
| Declaration | Today | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **requires a resource** — a database, a bucket | yes | provisioning, [ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md) |
|
| **requires a resource** — a database, a bucket | yes | provisioning, [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md) |
|
||||||
| **provides a resource** | yes | as above |
|
| **provides a resource** | yes | as above |
|
||||||
| **requires another module** | **no** | the dependency edge — the graph's substance |
|
| **requires another module** | **no** | the dependency edge — the graph's substance |
|
||||||
| **excludes another module** | **no** | installing A makes B unavailable |
|
| **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)). |
|
| ~~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)). |
|
| ~~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. |
|
| ~~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 0044](../../02-DECISIONS/0044-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 0019](../../02-DECISIONS/0019-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. |
|
| ~~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. |
|
| ~~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. |
|
| ~~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. |
|
| ~~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 0037, 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 0016, and the bulk are **foreign tenants** — thirteen tables across three contexts — who need to move out rather than read differently. |
|
||||||
|
|
||||||
### Live
|
### 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. |
|
| 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 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. |
|
| 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 0036 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 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. |
|
||||||
| 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 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 0043's removal rule does not obviously carry, because the thing lives inside another module's state. |
|
| 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 0037 — or the control plane composes from the node's inventory first. |
|
| 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. |
|
||||||
| 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*. |
|
| 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. |
|
| 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. |
|
| 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.
|
not have to declare the same relationship twice.
|
||||||
|
|
||||||
So *ordering by the graph* — which
|
So *ordering by the graph* — which
|
||||||
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md) says
|
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md) says
|
||||||
the control plane will do — is not a thing to build. It is a thing to call.
|
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
|
## 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
|
## Finding 4 — the resolver continues past faults it should stop on
|
||||||
|
|
||||||
Two behaviours, both contrary to
|
Two behaviours, both contrary to
|
||||||
[ADR 0058](../../02-DECISIONS/0058-delivery.md):
|
[ADR 0023](../../02-DECISIONS/0023-delivery.md):
|
||||||
|
|
||||||
- **A cycle warns and falls back to input order.** A cycle means no correct order exists; the
|
- **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.
|
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
|
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
|
is why nobody has noticed. They are latent, and they are in the component that
|
||||||
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md) makes
|
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md) makes
|
||||||
responsible for the ordering a host will apply without question.
|
responsible for the ordering a host will apply without question.
|
||||||
|
|
||||||
## Finding 5 — placement is decided in the catalogue
|
## 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,
|
**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.*
|
configured through files, run by the service manager. *A firewall, a resolver, an overlay.*
|
||||||
Note: [ADR 0037](../../02-DECISIONS/0037-the-node-host.md) says applying
|
Note: [ADR 0016](../../02-DECISIONS/0016-the-node-host.md) says applying
|
||||||
these is the host's job — so what the module contributes is the *deciding*, not the doing.
|
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
|
**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
|
**9 — A standalone application in its own repository.** Same shape as any of the above; the
|
||||||
difference is only where its source lives
|
difference is only where its source lives
|
||||||
([ADR 0010](../../02-DECISIONS/0010-applications-live-in-their-own-repository.md)). Worth
|
([ADR 0006](../../02-DECISIONS/0006-applications-live-in-their-own-repository.md)). Worth
|
||||||
listing because a schema that assumes a monorepo path would exclude it.
|
listing because a schema that assumes a monorepo path would exclude it.
|
||||||
|
|
||||||
## The cases that break a naive schema
|
## 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`.
|
**Resources — desired state on a machine.** `configs`, `service`, `systemd`, `vhost`, `tools`.
|
||||||
Applied, converged, idempotent — which is exactly what
|
Applied, converged, idempotent — which is exactly what
|
||||||
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md)
|
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md)
|
||||||
already describes and what the host already does. **Tier 0.**
|
already describes and what the host already does. **Tier 0.**
|
||||||
|
|
||||||
**Actions — run once, against something that is not this machine.** `migrations`, `seeds`,
|
**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
|
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
|
individually — a firewall, a store, a resolver — with no flavour and no grouping module
|
||||||
standing in front of them.
|
standing in front of them.
|
||||||
- **Domain grouping as structure** ([ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md)).
|
- **Domain grouping as structure** ([ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)).
|
||||||
Folders assert relationships; edges record them. What grouping was for — finding things,
|
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
|
seeing what belongs together — is a **tag** and a *query* over the graph, neither of which
|
||||||
anybody has to keep true by hand.
|
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
|
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.
|
carried bundle has to be able to express.
|
||||||
|
|
||||||
**Which strains what a declaration is.** [ADR 0037](../../02-DECISIONS/0037-the-node-host.md)
|
**Which strains what a declaration is.** [ADR 0016](../../02-DECISIONS/0016-the-node-host.md)
|
||||||
has the host applying *declared state on this machine*. A database inside a running store is not
|
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
|
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.
|
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.
|
case, and reaching it is not the host's job.
|
||||||
|
|
||||||
**Resolved as two mechanisms, which is the answer rather than a compromise**
|
**Resolved as two mechanisms, which is the answer rather than a compromise**
|
||||||
([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)). The host
|
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)). The host
|
||||||
runs bootstrap actions locally from the bundle; the control plane provisions across the mesh
|
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
|
afterwards. Different actors, different scopes, different trust paths — so there is no single
|
||||||
operation with a tier boundary running through it.
|
operation with a tier boundary running through it.
|
||||||
@@ -279,7 +279,7 @@ of them is work.
|
|||||||
| Group | What happens under the rule |
|
| 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. |
|
| **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 0037](../../02-DECISIONS/0037-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 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. |
|
||||||
| **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. |
|
| **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. |
|
| **Genuine cross-context reads** — the work engine reads `nodes`; two others read a handful | The only ones needing an interface or events. |
|
||||||
|
|
||||||
@@ -312,7 +312,7 @@ not the distinction.
|
|||||||
| when the other side is down | you cannot answer | you answer from your copy |
|
| 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 you must handle | a round trip that can fail | events you missed while you were down |
|
||||||
|
|
||||||
**What decides is not taste.** [ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)
|
**What decides is not taste.** [ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)
|
||||||
makes disconnection an ordinary situation rather than an exception. So:
|
makes disconnection an ordinary situation rather than an exception. So:
|
||||||
|
|
||||||
> **Anything that must keep working while disconnected cannot use a request** — there is nobody
|
> **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
|
**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.
|
still exists, holding its data. Dropping it silently is data loss; keeping it forever is a leak.
|
||||||
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md) says
|
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md) says
|
||||||
the host removes what it applied and no longer declares — but this is not on the host, it is
|
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.
|
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
|
**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, and cannot know this machine's memory. Either the host fills in a blank the
|
||||||
declaration leaves — which makes the host decide something, against
|
declaration leaves — which makes the host decide something, against
|
||||||
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md) — or the control
|
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md) — or the control
|
||||||
plane reads the node's inventory first and composes with it. The second is consistent and means
|
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
|
a declaration is composed *per node from what the node reported*, which is a stronger claim than
|
||||||
anything recorded so far.
|
anything recorded so far.
|
||||||
@@ -421,7 +421,7 @@ But two things differ *between* them, and both matter more than the similarity.
|
|||||||
|
|
||||||
[ADR 0001](../../02-DECISIONS/0001-nodes-communicate-over-a-broker.md) makes the broker the
|
[ADR 0001](../../02-DECISIONS/0001-nodes-communicate-over-a-broker.md) makes the broker the
|
||||||
channel every node takes work from, and
|
channel every node takes work from, and
|
||||||
[ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md) makes it the security
|
[ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md) makes it the security
|
||||||
boundary — everything a node applies arrives through it.
|
boundary — everything a node applies arrives through it.
|
||||||
|
|
||||||
So the module providing the broker is also **the way modules are managed**. A declaration cannot
|
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.
|
control plane but is not how the control plane *reaches* anything.
|
||||||
|
|
||||||
This is exactly what the carried bundle exists for
|
This is exactly what the carried bundle exists for
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)): the broker is raised from
|
([ADR 0015](../../02-DECISIONS/0015-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.
|
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
|
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.
|
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
|
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
|
rather than by accident. The store cannot be: a node that must keep working while disconnected
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)) cannot depend on a database
|
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)) cannot depend on a database
|
||||||
somewhere else.
|
somewhere else.
|
||||||
|
|
||||||
Same nine properties, opposite answers. Which settles something the cases file left open: **how
|
Same nine properties, opposite answers. Which settles something the cases file left open: **how
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
status: active
|
status: active
|
||||||
initiated: 2026-08-26
|
initiated: 2026-08-26
|
||||||
touches:
|
touches:
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0004-managed-files-are-generated-never-edited.md
|
- 02-DECISIONS/0002-managed-files-are-generated-never-edited.md
|
||||||
- 03-DESIGN/01-to-be/05-the-node-host.md
|
- 03-DESIGN/01-to-be/05-the-node-host.md
|
||||||
- 03-DESIGN/00-as-is/05-runtime-and-installation.md
|
- 03-DESIGN/00-as-is/05-runtime-and-installation.md
|
||||||
- 01-RESEARCH/011-the-module-graph/00-overview.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
|
first. Downloading fails on the first node, which cannot fetch the image registry from the image
|
||||||
registry it is trying to start.
|
registry it is trying to start.
|
||||||
|
|
||||||
> **Qualified by [ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md).** The
|
> **Qualified by [ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md).** The
|
||||||
> reframing below still holds for what a *tailored installer* contains — the missing pieces for a
|
> 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
|
> 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.
|
> 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
|
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.
|
was dropped for, which is a thing to notice rather than to gloss.
|
||||||
|
|
||||||
**It creates a state that does not exist today.** [ADR 0004](../../02-DECISIONS/0004-managed-files-are-generated-never-edited.md)
|
**It creates a state that does not exist today.** [ADR 0002](../../02-DECISIONS/0002-managed-files-are-generated-never-edited.md)
|
||||||
has managed files generated and never edited; adoption needs a one-time import before that rule
|
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:
|
starts applying. Three states, and the middle one is new:
|
||||||
|
|
||||||
> unmanaged → **adopted once** → generated
|
> unmanaged → **adopted once** → generated
|
||||||
|
|
||||||
**And it crosses a boundary just drawn.** [ADR 0037](../../02-DECISIONS/0037-the-node-host.md)
|
**And it crosses a boundary just drawn.** [ADR 0016](../../02-DECISIONS/0016-the-node-host.md)
|
||||||
says the host never touches what it did not create — the rule that stops a converger deleting
|
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
|
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
|
that. The rule needs a companion rather than an exception: *never, unless adoption made it the
|
||||||
|
|||||||
+2
-2
@@ -5,13 +5,13 @@ deciders: jochen
|
|||||||
reconstructed: true
|
reconstructed: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 4. Managed files are generated onto nodes and never edited there
|
# 2. Managed files are generated onto nodes and never edited there
|
||||||
|
|
||||||
> Reconstructed after the fact from the evidence cited below.
|
> Reconstructed after the fact from the evidence cited below.
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
[ADR 0048](0048-the-substrate-and-the-control-plane.md) put every binding in the mesh
|
[ADR 0021](0021-the-substrate-and-the-control-plane.md) put every binding in the mesh
|
||||||
database. But the things that consume those bindings — environment files, service
|
database. But the things that consume those bindings — environment files, service
|
||||||
definitions, daemon configuration, firewall rules — are files on a node's disk, because that
|
definitions, daemon configuration, firewall rules — are files on a node's disk, because that
|
||||||
is what the software reading them requires.
|
is what the software reading them requires.
|
||||||
+1
-1
@@ -5,7 +5,7 @@ deciders: jochen
|
|||||||
reconstructed: true
|
reconstructed: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 6. Schema and state changes are numbered migrations, in the same language as the code
|
# 3. Schema and state changes are numbered migrations, in the same language as the code
|
||||||
|
|
||||||
> Reconstructed after the fact from the evidence cited below.
|
> Reconstructed after the fact from the evidence cited below.
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@ deciders: jochen
|
|||||||
reconstructed: true
|
reconstructed: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 7. No workspace — each module is a standalone package consuming published dependencies
|
# 4. No workspace — each module is a standalone package consuming published dependencies
|
||||||
|
|
||||||
> Reconstructed after the fact from the evidence cited below.
|
> Reconstructed after the fact from the evidence cited below.
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ its dependencies' freshly published versions.
|
|||||||
- Development and the pipeline resolve imports identically. The divergence is gone by
|
- Development and the pipeline resolve imports identically. The divergence is gone by
|
||||||
construction rather than by discipline.
|
construction rather than by discipline.
|
||||||
- A module in its own repository is not a special case. It builds exactly as a module in the
|
- A module in its own repository is not a special case. It builds exactly as a module in the
|
||||||
monorepo does — which is what makes [ADR 0010](0010-applications-live-in-their-own-repository.md)
|
monorepo does — which is what makes [ADR 0006](0006-applications-live-in-their-own-repository.md)
|
||||||
cheap.
|
cheap.
|
||||||
- A cross-package change costs a publish-and-consume round trip. This is the real price, paid
|
- A cross-package change costs a publish-and-consume round trip. This is the real price, paid
|
||||||
on every shared-library change.
|
on every shared-library change.
|
||||||
+1
-1
@@ -5,7 +5,7 @@ deciders: jochen
|
|||||||
reconstructed: true
|
reconstructed: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 9. The mesh is governed by a constitution, injected where work is decided
|
# 5. The mesh is governed by a constitution, injected where work is decided
|
||||||
|
|
||||||
> Reconstructed after the fact from the evidence cited below.
|
> Reconstructed after the fact from the evidence cited below.
|
||||||
|
|
||||||
+3
-3
@@ -5,7 +5,7 @@ deciders: jochen
|
|||||||
reconstructed: true
|
reconstructed: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 10. Applications live in their own repository; the monorepo is for the mesh
|
# 6. Applications live in their own repository; the monorepo is for the mesh
|
||||||
|
|
||||||
> Reconstructed after the fact from the evidence cited below.
|
> Reconstructed after the fact from the evidence cited below.
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ reject it.
|
|||||||
- An application's cadence is its own. It is not reviewed as mesh code and does not queue
|
- An application's cadence is its own. It is not reviewed as mesh code and does not queue
|
||||||
behind mesh work.
|
behind mesh work.
|
||||||
- The separation is safe **only because** the pipeline and provisioning are identical either
|
- The separation is safe **only because** the pipeline and provisioning are identical either
|
||||||
side of it — which [ADR 0007](0007-no-npm-workspace.md) is what makes true. Without
|
side of it — which [ADR 0004](0004-no-npm-workspace.md) is what makes true. Without
|
||||||
standalone packages this decision would fork the build.
|
standalone packages this decision would fork the build.
|
||||||
- The monorepo stops being an inventory of the installation, which is a precondition for
|
- The monorepo stops being an inventory of the installation, which is a precondition for
|
||||||
publishing anything about it.
|
publishing anything about it.
|
||||||
@@ -59,6 +59,6 @@ reject it.
|
|||||||
|
|
||||||
- The rule is stated in the governed constitution page authored 2026-07-10, §3, as a
|
- The rule is stated in the governed constitution page authored 2026-07-10, §3, as a
|
||||||
convention violation reviewers must reject.
|
convention violation reviewers must reject.
|
||||||
- [ADR 0015](0015-mesh-brokers-nodes-host-agents-think.md) decision 4 extends this from *new*
|
- [ADR 0008](0008-mesh-brokers-nodes-host-agents-think.md) decision 4 extends this from *new*
|
||||||
applications to the modules already in the monorepo.
|
applications to the modules already in the monorepo.
|
||||||
- Knowledge base: `troubleshooting/unregistered-module-source`.
|
- Knowledge base: `troubleshooting/unregistered-module-source`.
|
||||||
+2
-2
@@ -5,7 +5,7 @@ deciders: jochen
|
|||||||
reconstructed: true
|
reconstructed: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 12. An agent is a persistent employee, not an instance of a pool
|
# 7. An agent is a persistent employee, not an instance of a pool
|
||||||
|
|
||||||
> Reconstructed after the fact from the evidence cited below.
|
> Reconstructed after the fact from the evidence cited below.
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ itself is an agent of a kind exempt from the hiring lifecycle.
|
|||||||
or another agent is hired — both deliberate acts.
|
or another agent is hired — both deliberate acts.
|
||||||
- The transition was not free. Lifecycle columns had to reach every query that selects an
|
- The transition was not free. Lifecycle columns had to reach every query that selects an
|
||||||
agent, and the ones that were missed failed at the moment of hiring rather than at startup.
|
agent, and the ones that were missed failed at the moment of hiring rather than at startup.
|
||||||
- This is the decision [ADR 0015](0015-mesh-brokers-nodes-host-agents-think.md) generalises:
|
- This is the decision [ADR 0008](0008-mesh-brokers-nodes-host-agents-think.md) generalises:
|
||||||
one kind of participant, differing only in modality.
|
one kind of participant, differing only in modality.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
+2
-2
@@ -5,7 +5,7 @@ deciders: jochen
|
|||||||
reconstructed: false
|
reconstructed: false
|
||||||
---
|
---
|
||||||
|
|
||||||
# 15. The mesh brokers capabilities; nodes host; agents think
|
# 8. The mesh brokers capabilities; nodes host; agents think
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@@ -182,7 +182,7 @@ existing pipeline. Nothing here requires a flag day, and nothing here is cheap.
|
|||||||
- `modules/hal/sdk/src/feature-handlers/index.ts` — `FEATURE_HANDLERS`, the fixed handler
|
- `modules/hal/sdk/src/feature-handlers/index.ts` — `FEATURE_HANDLERS`, the fixed handler
|
||||||
array that makes a feature a singleton per module
|
array that makes a feature a singleton per module
|
||||||
- `modules/postgres/tools/index.ts` — the adoption path that rotates a shared credential
|
- `modules/postgres/tools/index.ts` — the adoption path that rotates a shared credential
|
||||||
- Mediahuis `papa-hq`, ADR 0009 *Composable, independently-shippable modules* — the
|
- Mediahuis `papa-hq`, ADR 0005 *Composable, independently-shippable modules* — the
|
||||||
constraints that make a unit independently shippable, applicable unchanged to features
|
constraints that make a unit independently shippable, applicable unchanged to features
|
||||||
- impire.io / soulstream — *the record* as integration substrate, personas over services,
|
- impire.io / soulstream — *the record* as integration substrate, personas over services,
|
||||||
and "cheap awareness and expensive thinking"
|
and "cheap awareness and expensive thinking"
|
||||||
@@ -3,10 +3,9 @@ status: accepted
|
|||||||
date: 2026-08-28
|
date: 2026-08-28
|
||||||
deciders: jochen
|
deciders: jochen
|
||||||
reconstructed: false
|
reconstructed: false
|
||||||
consolidates: [0029, 0031, 0032, 0033]
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 16. The lab
|
# 9. The lab
|
||||||
|
|
||||||
*Consolidated 2026-08-28 from five records. The lab is one design and was split across five
|
*Consolidated 2026-08-28 from five records. The lab is one design and was split across five
|
||||||
decisions taken over three days; the reasoning is kept, the fragmentation is not.*
|
decisions taken over three days; the reasoning is kept, the fragmentation is not.*
|
||||||
+8
-8
@@ -5,11 +5,11 @@ deciders: jochen
|
|||||||
reconstructed: false
|
reconstructed: false
|
||||||
---
|
---
|
||||||
|
|
||||||
# 18. The mesh creates no symlinks — a derived file is a copy
|
# 10. The mesh creates no symlinks — a derived file is a copy
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
[ADR 0018](0018-the-mesh-creates-no-symlinks.md) responded to production data loss — a hand-made
|
[ADR 0010](0010-the-mesh-creates-no-symlinks.md) responded to production data loss — a hand-made
|
||||||
link, resolved through a container engine's volume handling, pointing a mount somewhere it
|
link, resolved through a container engine's volume handling, pointing a mount somewhere it
|
||||||
should not have — by centralising linking in the installer and forbidding it everywhere else.
|
should not have — by centralising linking in the installer and forbidding it everywhere else.
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ Two things have changed since, and together they remove the argument that kept i
|
|||||||
silently while the catalogue moves on, so a link was the cheap way to guarantee the running
|
silently while the catalogue moves on, so a link was the cheap way to guarantee the running
|
||||||
node reads a current definition. That argument assumes the node's copy is unmanaged.
|
node reads a current definition. That argument assumes the node's copy is unmanaged.
|
||||||
|
|
||||||
**It is not.** [ADR 0004](0004-managed-files-are-generated-never-edited.md) established that
|
**It is not.** [ADR 0002](0002-managed-files-are-generated-never-edited.md) established that
|
||||||
everything on a node's disk is derived from the mesh and regenerated when its inputs change,
|
everything on a node's disk is derived from the mesh and regenerated when its inputs change,
|
||||||
and the installer already **reconciles** links rather than assuming them — repointing stale
|
and the installer already **reconciles** links rather than assuming them — repointing stale
|
||||||
ones, adopting real files it finds where a link belongs. Reconciling content is the same
|
ones, adopting real files it finds where a link belongs. Reconciling content is the same
|
||||||
@@ -33,7 +33,7 @@ operation as reconciling a pointer, plus a comparison.
|
|||||||
So the mesh already has the machinery that makes a copy safe, and is using a link to solve a
|
So the mesh already has the machinery that makes a copy safe, and is using a link to solve a
|
||||||
problem that machinery solves better. Worse, a link is conceptually the wrong shape: it makes
|
problem that machinery solves better. Worse, a link is conceptually the wrong shape: it makes
|
||||||
the node's runtime state a *pointer into source*, which is the one thing
|
the node's runtime state a *pointer into source*, which is the one thing
|
||||||
[ADR 0048](0048-the-substrate-and-the-control-plane.md) and ADR 0004 exist to prevent.
|
[ADR 0021](0021-the-substrate-and-the-control-plane.md) and ADR 0002 exist to prevent.
|
||||||
State is derived onto nodes; it does not reach back.
|
State is derived onto nodes; it does not reach back.
|
||||||
|
|
||||||
## Considered options
|
## Considered options
|
||||||
@@ -53,7 +53,7 @@ State is derived onto nodes; it does not reach back.
|
|||||||
|
|
||||||
**The mesh creates no symlinks.** A file a node needs is placed on that node as a real file,
|
**The mesh creates no symlinks.** A file a node needs is placed on that node as a real file,
|
||||||
derived from the mesh and reconciled by the installer like every other managed file
|
derived from the mesh and reconciled by the installer like every other managed file
|
||||||
([ADR 0004](0004-managed-files-are-generated-never-edited.md)).
|
([ADR 0002](0002-managed-files-are-generated-never-edited.md)).
|
||||||
|
|
||||||
The prohibition in ADR 0011 stands and widens: it ceases to be "only the installer may link"
|
The prohibition in ADR 0011 stands and widens: it ceases to be "only the installer may link"
|
||||||
and becomes "nothing links, the installer included".
|
and becomes "nothing links, the installer included".
|
||||||
@@ -72,7 +72,7 @@ the rule exists at all, and the incident behind it is the reason anyone believes
|
|||||||
cost, and it is the whole cost: today a link cannot be stale, and a copy can. The answer has
|
cost, and it is the whole cost: today a link cannot be stale, and a copy can. The answer has
|
||||||
to be detection — the installer comparing what is on disk against what the mesh says should
|
to be detection — the installer comparing what is on disk against what the mesh says should
|
||||||
be — and it must be loud, because a silently stale definition is exactly the failure shape
|
be — and it must be loud, because a silently stale definition is exactly the failure shape
|
||||||
this mesh keeps producing ([ADR 0058](0058-delivery.md)).
|
this mesh keeps producing ([ADR 0023](0023-delivery.md)).
|
||||||
- Reconciliation gets more expensive: comparing content rather than checking a pointer's
|
- Reconciliation gets more expensive: comparing content rather than checking a pointer's
|
||||||
target, on every module, on every node.
|
target, on every module, on every node.
|
||||||
- Disk usage rises, trivially, and is not a consideration.
|
- Disk usage rises, trivially, and is not a consideration.
|
||||||
@@ -93,8 +93,8 @@ Until those are answered this record stays `proposed`, and ADR 0011 remains the
|
|||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- [ADR 0018](0018-the-mesh-creates-no-symlinks.md) — the incident, and the rule this widens.
|
- [ADR 0010](0010-the-mesh-creates-no-symlinks.md) — the incident, and the rule this widens.
|
||||||
- [ADR 0004](0004-managed-files-are-generated-never-edited.md) — the machinery that makes a
|
- [ADR 0002](0002-managed-files-are-generated-never-edited.md) — the machinery that makes a
|
||||||
copy safe.
|
copy safe.
|
||||||
- [`03-DESIGN/00-as-is/05-runtime-and-installation.md`](../03-DESIGN/00-as-is/05-runtime-and-installation.md)
|
- [`03-DESIGN/00-as-is/05-runtime-and-installation.md`](../03-DESIGN/00-as-is/05-runtime-and-installation.md)
|
||||||
— what the installer does today, including reconciliation and adoption.
|
— what the installer does today, including reconciliation and adoption.
|
||||||
+2
-3
@@ -3,10 +3,9 @@ status: accepted
|
|||||||
date: 2026-08-28
|
date: 2026-08-28
|
||||||
deciders: jochen
|
deciders: jochen
|
||||||
reconstructed: false
|
reconstructed: false
|
||||||
consolidates: [0020, 0021, 0022, 0023, 0024, 0026, 0027, 0028, 0030]
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 19. How this repository works
|
# 11. How this repository works
|
||||||
|
|
||||||
*Consolidated 2026-08-28 from ten records that were one decision seen from ten angles. The
|
*Consolidated 2026-08-28 from ten records that were one decision seen from ten angles. The
|
||||||
reasoning is kept; the fragmentation is not.*
|
reasoning is kept; the fragmentation is not.*
|
||||||
@@ -37,7 +36,7 @@ company-scoped one does not. So this is `hq` and the mesh's are `mesh-*`.
|
|||||||
| `novox/mesh-substrate` | 1 | the pinned tier-1 services, as declarations |
|
| `novox/mesh-substrate` | 1 | the pinned tier-1 services, as declarations |
|
||||||
| `novox/mesh-control` | 2 | the control plane and its contexts |
|
| `novox/mesh-control` | 2 | the control plane and its contexts |
|
||||||
| `novox/mesh-surfaces` | 3 | tools, web, cli — thin, no logic |
|
| `novox/mesh-surfaces` | 3 | tools, web, cli — thin, no logic |
|
||||||
| `novox/mesh-sdk` | — | the mesh's own domain ([ADR 0044](0044-modules-and-the-graph.md)) |
|
| `novox/mesh-sdk` | — | the mesh's own domain ([ADR 0019](0019-modules-and-the-graph.md)) |
|
||||||
| `novox/mesh-lab` | — | the lab: scenario lifecycle, networking, placement |
|
| `novox/mesh-lab` | — | the lab: scenario lifecycle, networking, placement |
|
||||||
| `novox/hq` | — | this one |
|
| `novox/hq` | — | this one |
|
||||||
|
|
||||||
+5
-5
@@ -5,11 +5,11 @@ deciders: jochen
|
|||||||
reconstructed: false
|
reconstructed: false
|
||||||
---
|
---
|
||||||
|
|
||||||
# 25. HQ is the source of the mesh constitution
|
# 12. HQ is the source of the mesh constitution
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
[ADR 0009](0009-the-mesh-is-governed-by-a-constitution.md) established a canonical rule set,
|
[ADR 0005](0005-the-mesh-is-governed-by-a-constitution.md) established a canonical rule set,
|
||||||
injected into every eligible design session and checked before output is accepted. It lives in
|
injected into every eligible design session and checked before output is accepted. It lives in
|
||||||
the knowledge base, where the orchestrator reads it.
|
the knowledge base, where the orchestrator reads it.
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ directly.
|
|||||||
Publishing is a playbook step, not a manual act, and it ends with **reading the page back and
|
Publishing is a playbook step, not a manual act, and it ends with **reading the page back and
|
||||||
verifying the change is present**. A publish that reported success and did nothing is exactly
|
verifying the change is present**. A publish that reported success and did nothing is exactly
|
||||||
the failure class this mesh keeps producing
|
the failure class this mesh keeps producing
|
||||||
([ADR 0058](0058-delivery.md)).
|
([ADR 0023](0023-delivery.md)).
|
||||||
|
|
||||||
Section numbering is stable, because the orchestrator and the review fragments cite sections by
|
Section numbering is stable, because the orchestrator and the review fragments cite sections by
|
||||||
number.
|
number.
|
||||||
@@ -47,7 +47,7 @@ number.
|
|||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
- One source, many surfaces — the same argument HQ's separation already rests on
|
- One source, many surfaces — the same argument HQ's separation already rests on
|
||||||
([ADR 0019](0019-how-this-repository-works.md)), applied to the rules themselves.
|
([ADR 0011](0011-how-this-repository-works.md)), applied to the rules themselves.
|
||||||
- Each rule keeps the incident that earned it, in a place that is reviewed as a diff.
|
- Each rule keeps the incident that earned it, in a place that is reviewed as a diff.
|
||||||
- An edit to the derived page survives until the next sync and then vanishes. The playbook says
|
- An edit to the derived page survives until the next sync and then vanishes. The playbook says
|
||||||
so, and nothing mechanically prevents it.
|
so, and nothing mechanically prevents it.
|
||||||
@@ -63,5 +63,5 @@ number.
|
|||||||
|
|
||||||
- [`00-META/process/05-constitution-sync.md`](../00-META/process/05-constitution-sync.md) —
|
- [`00-META/process/05-constitution-sync.md`](../00-META/process/05-constitution-sync.md) —
|
||||||
the sync, including the read-back.
|
the sync, including the read-back.
|
||||||
- [ADR 0009](0009-the-mesh-is-governed-by-a-constitution.md) — the governed page and why it
|
- [ADR 0005](0005-the-mesh-is-governed-by-a-constitution.md) — the governed page and why it
|
||||||
exists.
|
exists.
|
||||||
+1
-1
@@ -5,7 +5,7 @@ deciders: jochen
|
|||||||
reconstructed: false
|
reconstructed: false
|
||||||
---
|
---
|
||||||
|
|
||||||
# 34. A test defends a decision
|
# 13. A test defends a decision
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
+5
-5
@@ -3,16 +3,16 @@ status: accepted
|
|||||||
date: 2026-08-24
|
date: 2026-08-24
|
||||||
deciders: jochen
|
deciders: jochen
|
||||||
reconstructed: false
|
reconstructed: false
|
||||||
extends: 0034-a-test-defends-a-decision.md
|
extends: 0013-a-test-defends-a-decision.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 35. A picture of a system is read from the system, never from what asked for it
|
# 14. A picture of a system is read from the system, never from what asked for it
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
A scenario declaration is a file. A raised scenario is a set of machines, links and rulesets.
|
A scenario declaration is a file. A raised scenario is a set of machines, links and rulesets.
|
||||||
The two are supposed to correspond, and the entire value of the lab rests on noticing when
|
The two are supposed to correspond, and the entire value of the lab rests on noticing when
|
||||||
they do not — [ADR 0034](0034-a-test-defends-a-decision.md) says a claim nothing checks is a
|
they do not — [ADR 0013](0013-a-test-defends-a-decision.md) says a claim nothing checks is a
|
||||||
claim that will quietly stop being true.
|
claim that will quietly stop being true.
|
||||||
|
|
||||||
Drawing a scenario makes that concrete, and forces a choice that looks cosmetic and is not.
|
Drawing a scenario makes that concrete, and forces a choice that looks cosmetic and is not.
|
||||||
@@ -91,8 +91,8 @@ difference read off directly.
|
|||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- [ADR 0034](0034-a-test-defends-a-decision.md) — a claim nothing checks stops being true.
|
- [ADR 0013](0013-a-test-defends-a-decision.md) — a claim nothing checks stops being true.
|
||||||
- [ADR 0016](0016-the-lab.md) — why the lab must not supply what the
|
- [ADR 0009](0009-the-lab.md) — why the lab must not supply what the
|
||||||
mesh is responsible for; the same instinct, applied to facts rather than to configuration.
|
mesh is responsible for; the same instinct, applied to facts rather than to configuration.
|
||||||
- [04-ISSUES/003](../04-ISSUES/003-firewall-scope-is-read-by-no-code/00-report.md) — the fault in production
|
- [04-ISSUES/003](../04-ISSUES/003-firewall-scope-is-read-by-no-code/00-report.md) — the fault in production
|
||||||
form.
|
form.
|
||||||
+1
-2
@@ -3,10 +3,9 @@ status: accepted
|
|||||||
date: 2026-08-28
|
date: 2026-08-28
|
||||||
deciders: jochen
|
deciders: jochen
|
||||||
reconstructed: false
|
reconstructed: false
|
||||||
consolidates: [0038, 0039, 0051]
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 36. A node, and how it joins
|
# 15. A node, and how it joins
|
||||||
|
|
||||||
*Consolidated 2026-08-28 from four records.*
|
*Consolidated 2026-08-28 from four records.*
|
||||||
|
|
||||||
@@ -3,10 +3,9 @@ status: accepted
|
|||||||
date: 2026-08-28
|
date: 2026-08-28
|
||||||
deciders: jochen
|
deciders: jochen
|
||||||
reconstructed: false
|
reconstructed: false
|
||||||
consolidates: [0041, 0043, 0047, 0057, 0060, 0061, 0062]
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 37. The node host
|
# 16. The node host
|
||||||
|
|
||||||
*Consolidated 2026-08-28 from eight records. Tier 0 is one component and was decided over a
|
*Consolidated 2026-08-28 from eight records. Tier 0 is one component and was decided over a
|
||||||
week; the reasoning is kept, the fragmentation is not.*
|
week; the reasoning is kept, the fragmentation is not.*
|
||||||
+6
-6
@@ -5,11 +5,11 @@ deciders: jochen
|
|||||||
reconstructed: false
|
reconstructed: false
|
||||||
---
|
---
|
||||||
|
|
||||||
# 40. The constitution absorbs what is already enforced
|
# 17. The constitution absorbs what is already enforced
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
[ADR 0025](0025-hq-is-the-source-of-the-constitution.md) makes this repository the source
|
[ADR 0012](0012-hq-is-the-source-of-the-constitution.md) makes this repository the source
|
||||||
and the knowledge base a derived copy, and playbook
|
and the knowledge base a derived copy, and playbook
|
||||||
[05](../00-META/process/05-constitution-sync.md) publishes the copy whenever a rule changes.
|
[05](../00-META/process/05-constitution-sync.md) publishes the copy whenever a rule changes.
|
||||||
|
|
||||||
@@ -97,8 +97,8 @@ trusting the second success message either.
|
|||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- [ADR 0025](0025-hq-is-the-source-of-the-constitution.md) — source and copy.
|
- [ADR 0012](0012-hq-is-the-source-of-the-constitution.md) — source and copy.
|
||||||
- [ADR 0009](0009-the-mesh-is-governed-by-a-constitution.md) — why the copy is injected at all.
|
- [ADR 0005](0005-the-mesh-is-governed-by-a-constitution.md) — why the copy is injected at all.
|
||||||
- [Playbook 05](../00-META/process/05-constitution-sync.md) — the sync this record interrupts.
|
- [Playbook 05](../00-META/process/05-constitution-sync.md) — the sync this record interrupts.
|
||||||
- [ADR 0034](0034-a-test-defends-a-decision.md), [ADR 0035](0035-a-picture-is-read-from-what-runs.md),
|
- [ADR 0013](0013-a-test-defends-a-decision.md), [ADR 0014](0014-a-picture-is-read-from-what-runs.md),
|
||||||
[ADR 0018](0018-the-mesh-creates-no-symlinks.md) — the three rules whose sync surfaced this.
|
[ADR 0010](0010-the-mesh-creates-no-symlinks.md) — the three rules whose sync surfaced this.
|
||||||
+2
-2
@@ -5,7 +5,7 @@ deciders: jochen
|
|||||||
reconstructed: false
|
reconstructed: false
|
||||||
---
|
---
|
||||||
|
|
||||||
# 42. The approval is the checkpoint, not the second pair of hands
|
# 18. The approval is the checkpoint, not the second pair of hands
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@@ -73,5 +73,5 @@ the previous sync reported success and changed nothing.
|
|||||||
## References
|
## References
|
||||||
|
|
||||||
- [`how-we-build.md`](../00-META/how-we-build.md) §2 — the rule, now carrying this.
|
- [`how-we-build.md`](../00-META/how-we-build.md) §2 — the rule, now carrying this.
|
||||||
- [ADR 0058](0058-delivery.md) — the standard a checkpoint is held to: a
|
- [ADR 0023](0023-delivery.md) — the standard a checkpoint is held to: a
|
||||||
step that reports success without doing anything is the fault, not the shortcut.
|
step that reports success without doing anything is the fault, not the shortcut.
|
||||||
+1
-2
@@ -3,10 +3,9 @@ status: accepted
|
|||||||
date: 2026-08-28
|
date: 2026-08-28
|
||||||
deciders: jochen
|
deciders: jochen
|
||||||
reconstructed: false
|
reconstructed: false
|
||||||
consolidates: [0002, 0005, 0017, 0054, 0064, 0065]
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 44. Modules and the graph
|
# 19. Modules and the graph
|
||||||
|
|
||||||
*Consolidated 2026-08-28 from six records.*
|
*Consolidated 2026-08-28 from six records.*
|
||||||
|
|
||||||
+6
-6
@@ -3,14 +3,14 @@ status: accepted
|
|||||||
date: 2026-08-26
|
date: 2026-08-26
|
||||||
deciders: jochen
|
deciders: jochen
|
||||||
reconstructed: false
|
reconstructed: false
|
||||||
extends: 0044-modules-and-the-graph.md
|
extends: 0019-modules-and-the-graph.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 45. A context owns its store, exclusively
|
# 20. A context owns its store, exclusively
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
[ADR 0044](0044-modules-and-the-graph.md) settles what a module
|
[ADR 0019](0019-modules-and-the-graph.md) settles what a module
|
||||||
declares. This settles what a grant may be, and it is the half that **removes** things.
|
declares. This settles what a grant may be, and it is the half that **removes** things.
|
||||||
|
|
||||||
`how-we-build` §4 already says *contexts integrate through the record, never through a shared
|
`how-we-build` §4 already says *contexts integrate through the record, never through a shared
|
||||||
@@ -45,7 +45,7 @@ modules is the mesh showing its own data, not a boundary crossing. What is forbi
|
|||||||
|
|
||||||
### Asking or subscribing is derived, not chosen
|
### Asking or subscribing is derived, not chosen
|
||||||
|
|
||||||
[ADR 0036](0036-a-node-and-how-it-joins.md) makes disconnection an ordinary situation. So:
|
[ADR 0015](0015-a-node-and-how-it-joins.md) makes disconnection an ordinary situation. So:
|
||||||
|
|
||||||
- **Anything that must keep working while disconnected cannot ask** — there is nobody to ask. It
|
- **Anything that must keep working while disconnected cannot ask** — there is nobody to ask. It
|
||||||
keeps a local copy, which means subscribing.
|
keeps a local copy, which means subscribing.
|
||||||
@@ -79,7 +79,7 @@ The first clear list of what the design deletes rather than adds:
|
|||||||
- **Three contexts must move out of the registry database**, taking thirteen tables with them.
|
- **Three contexts must move out of the registry database**, taking thirteen tables with them.
|
||||||
Their dependency on the registry then shrinks to almost nothing — one of them needs a single
|
Their dependency on the registry then shrinks to almost nothing — one of them needs a single
|
||||||
table.
|
table.
|
||||||
- **The node appliers were already handled.** [ADR 0037](0037-the-node-host.md)
|
- **The node appliers were already handled.** [ADR 0016](0016-the-node-host.md)
|
||||||
stopped the host querying the mesh database for tier reasons unrelated to this, and it removes
|
stopped the host querying the mesh database for tier reasons unrelated to this, and it removes
|
||||||
most of the remaining direct readers as a side effect.
|
most of the remaining direct readers as a side effect.
|
||||||
- **What a consumer does about events missed while disconnected is not decided** — replay from a
|
- **What a consumer does about events missed while disconnected is not decided** — replay from a
|
||||||
@@ -90,4 +90,4 @@ The first clear list of what the design deletes rather than adds:
|
|||||||
- [`how-we-build.md`](../00-META/how-we-build.md) §4 — the rule this makes enforceable.
|
- [`how-we-build.md`](../00-META/how-we-build.md) §4 — the rule this makes enforceable.
|
||||||
- [Research 011](../01-RESEARCH/011-the-module-graph/worked-provider.md) — the count, the worked
|
- [Research 011](../01-RESEARCH/011-the-module-graph/worked-provider.md) — the count, the worked
|
||||||
provider, and the dashboard case.
|
provider, and the dashboard case.
|
||||||
- [ADR 0036](0036-a-node-and-how-it-joins.md) — why asking or subscribing is derived.
|
- [ADR 0015](0015-a-node-and-how-it-joins.md) — why asking or subscribing is derived.
|
||||||
+1
-2
@@ -3,10 +3,9 @@ status: accepted
|
|||||||
date: 2026-08-28
|
date: 2026-08-28
|
||||||
deciders: jochen
|
deciders: jochen
|
||||||
reconstructed: false
|
reconstructed: false
|
||||||
consolidates: [0003, 0046, 0053, 0055, 0056]
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 48. The substrate and the control plane
|
# 21. The substrate and the control plane
|
||||||
|
|
||||||
*Consolidated 2026-08-28 from six records.*
|
*Consolidated 2026-08-28 from six records.*
|
||||||
|
|
||||||
@@ -3,10 +3,9 @@ status: accepted
|
|||||||
date: 2026-08-28
|
date: 2026-08-28
|
||||||
deciders: jochen
|
deciders: jochen
|
||||||
reconstructed: false
|
reconstructed: false
|
||||||
consolidates: [0050, 0052]
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 49. Connectivity
|
# 22. Connectivity
|
||||||
|
|
||||||
*Consolidated 2026-08-28 from three records. Overlay, resolution, exposure, filtering and
|
*Consolidated 2026-08-28 from three records. Overlay, resolution, exposure, filtering and
|
||||||
certificates are one design.*
|
certificates are one design.*
|
||||||
@@ -3,10 +3,9 @@ status: accepted
|
|||||||
date: 2026-08-28
|
date: 2026-08-28
|
||||||
deciders: jochen
|
deciders: jochen
|
||||||
reconstructed: false
|
reconstructed: false
|
||||||
consolidates: [0008, 0013, 0014, 0063]
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 58. Delivery
|
# 23. Delivery
|
||||||
|
|
||||||
*Consolidated 2026-08-28 from five records.*
|
*Consolidated 2026-08-28 from five records.*
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ The records run in the order the decisions were taken, oldest first.
|
|||||||
|
|
||||||
**Every decision is a record.** There is no ledger and no index file — if a decision is worth
|
**Every decision is a record.** There is no ledger and no index file — if a decision is worth
|
||||||
recording it is worth a record, and if it is not worth a record it is not recorded
|
recording it is worth a record, and if it is not worth a record it is not recorded
|
||||||
([ADR 0019](0019-how-this-repository-works.md)). A "decision" small enough to be one line is
|
([ADR 0011](0011-how-this-repository-works.md)). A "decision" small enough to be one line is
|
||||||
almost always a **rule**, and a rule belongs in
|
almost always a **rule**, and a rule belongs in
|
||||||
[`00-META/how-we-build.md`](../00-META/how-we-build.md), where it is enforced and keeps the
|
[`00-META/how-we-build.md`](../00-META/how-we-build.md), where it is enforced and keeps the
|
||||||
incident that earned it.
|
incident that earned it.
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ code: [hal]
|
|||||||
updated: 2026-08-23
|
updated: 2026-08-23
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0001-nodes-communicate-over-a-broker.md
|
- 02-DECISIONS/0001-nodes-communicate-over-a-broker.md
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0048-the-substrate-and-the-control-plane.md
|
- 02-DECISIONS/0021-the-substrate-and-the-control-plane.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# The mesh as it stands
|
# The mesh as it stands
|
||||||
@@ -28,11 +28,11 @@ onto it and can be regenerated.
|
|||||||
containerised service is a module. A set of capabilities with no service behind them is a
|
containerised service is a module. A set of capabilities with no service behind them is a
|
||||||
module. A bare marker whose whole content is that a node has it is a module. The mesh's own
|
module. A bare marker whose whole content is that a node has it is a module. The mesh's own
|
||||||
components are modules on exactly the same terms as everything else it carries
|
components are modules on exactly the same terms as everything else it carries
|
||||||
([ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md)).
|
([ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)).
|
||||||
|
|
||||||
**An agent** is a participant. Some agents are human. What differs is modality — how the agent
|
**An agent** is a participant. Some agents are human. What differs is modality — how the agent
|
||||||
acts — and not category: both hold identity, both act, both accumulate memory
|
acts — and not category: both hold identity, both act, both accumulate memory
|
||||||
([ADR 0012](../../02-DECISIONS/0012-agents-are-persistent-employees.md)).
|
([ADR 0007](../../02-DECISIONS/0007-agents-are-persistent-employees.md)).
|
||||||
|
|
||||||
## Where truth lives
|
## Where truth lives
|
||||||
|
|
||||||
@@ -40,10 +40,10 @@ The repository defines **what exists**: the modules, what each declares, how eac
|
|||||||
|
|
||||||
The mesh database defines **what runs where**: which node is assigned which module, at which
|
The mesh database defines **what runs where**: which node is assigned which module, at which
|
||||||
selection, with which overrides, plus the settings every node reads. No node-to-module mapping
|
selection, with which overrides, plus the settings every node reads. No node-to-module mapping
|
||||||
is ever committed ([ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md)).
|
is ever committed ([ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)).
|
||||||
|
|
||||||
Everything on a node's disk is **derived** from those two, and is regenerated rather than
|
Everything on a node's disk is **derived** from those two, and is regenerated rather than
|
||||||
edited ([ADR 0004](../../02-DECISIONS/0004-managed-files-are-generated-never-edited.md)). A node that
|
edited ([ADR 0002](../../02-DECISIONS/0002-managed-files-are-generated-never-edited.md)). A node that
|
||||||
loses its database keeps running from a local cache, which is deliberate and has the obvious
|
loses its database keeps running from a local cache, which is deliberate and has the obvious
|
||||||
cost: the cache carries no indication of its own age.
|
cost: the cache carries no indication of its own age.
|
||||||
|
|
||||||
@@ -65,9 +65,9 @@ goes to where the capability is.
|
|||||||
A push to the forge is the only trigger. What follows is three silos with deliberately
|
A push to the forge is the only trigger. What follows is three silos with deliberately
|
||||||
different cardinality: compile once, package and upload once, then install-configure-start-
|
different cardinality: compile once, package and upload once, then install-configure-start-
|
||||||
verify **on every assigned node**
|
verify **on every assigned node**
|
||||||
([ADR 0058](../../02-DECISIONS/0058-delivery.md)). What travels between
|
([ADR 0023](../../02-DECISIONS/0023-delivery.md)). What travels between
|
||||||
build and node is a self-contained build output, so a deploy is extract-and-run and touches no
|
build and node is a self-contained build output, so a deploy is extract-and-run and touches no
|
||||||
network ([ADR 0058](../../02-DECISIONS/0058-delivery.md)).
|
network ([ADR 0023](../../02-DECISIONS/0023-delivery.md)).
|
||||||
|
|
||||||
Modules are resolved into dependency levels and a level completes before the next begins, so a
|
Modules are resolved into dependency levels and a level completes before the next begins, so a
|
||||||
module always builds against its dependencies as they were just published.
|
module always builds against its dependencies as they were just published.
|
||||||
@@ -78,7 +78,7 @@ A module declares what it **provides** and what it **requires**. The mesh satisf
|
|||||||
requirement: it creates the resource, generates the credential, records the grant, and writes
|
requirement: it creates the resource, generates the credential, records the grant, and writes
|
||||||
the values where the module will read them. The module never learns which node its database
|
the values where the module will read them. The module never learns which node its database
|
||||||
lives on, and nobody ever writes a credential by hand
|
lives on, and nobody ever writes a credential by hand
|
||||||
([ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md)).
|
([ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)).
|
||||||
|
|
||||||
This is the property the mesh's whole shape rests on, and it is why provisioning is treated as
|
This is the property the mesh's whole shape rests on, and it is why provisioning is treated as
|
||||||
a core concern rather than as plumbing.
|
a core concern rather than as plumbing.
|
||||||
@@ -92,7 +92,7 @@ named for a feature the module does not declare, a stage that reported it had di
|
|||||||
message rather than that the effect happened, a package that 404ed from every mirror while the
|
message rather than that the effect happened, a package that 404ed from every mirror while the
|
||||||
job went green.
|
job went green.
|
||||||
|
|
||||||
[ADR 0058](../../02-DECISIONS/0058-delivery.md) is the response, and it is applied
|
[ADR 0023](../../02-DECISIONS/0023-delivery.md) is the response, and it is applied
|
||||||
instance by instance rather than enforced by a mechanism. New instances are still being found.
|
instance by instance rather than enforced by a mechanism. New instances are still being found.
|
||||||
That is an as-is fact, not a criticism: it is the single most useful thing to know about this
|
That is an as-is fact, not a criticism: it is the single most useful thing to know about this
|
||||||
system before changing it.
|
system before changing it.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ code: [hal]
|
|||||||
updated: 2026-08-23
|
updated: 2026-08-23
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0001-nodes-communicate-over-a-broker.md
|
- 02-DECISIONS/0001-nodes-communicate-over-a-broker.md
|
||||||
- 02-DECISIONS/0048-the-substrate-and-the-control-plane.md
|
- 02-DECISIONS/0021-the-substrate-and-the-control-plane.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# The mesh and its transport
|
# The mesh and its transport
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ status: implemented
|
|||||||
code: [hal]
|
code: [hal]
|
||||||
updated: 2026-08-23
|
updated: 2026-08-23
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0006-schema-changes-are-numbered-migrations.md
|
- 02-DECISIONS/0003-schema-changes-are-numbered-migrations.md
|
||||||
- 02-DECISIONS/0007-no-npm-workspace.md
|
- 02-DECISIONS/0004-no-npm-workspace.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# Modules, manifests and features
|
# Modules, manifests and features
|
||||||
@@ -81,7 +81,7 @@ recorded in the knowledge base; both presented as "the change did not apply" wit
|
|||||||
## Dependencies between modules
|
## Dependencies between modules
|
||||||
|
|
||||||
Modules depend on each other, above all on the shared library they all build against. There is
|
Modules depend on each other, above all on the shared library they all build against. There is
|
||||||
**no workspace** ([ADR 0007](../../02-DECISIONS/0007-no-npm-workspace.md)): each module is a standalone
|
**no workspace** ([ADR 0004](../../02-DECISIONS/0004-no-npm-workspace.md)): each module is a standalone
|
||||||
package consuming published dependencies, including the mesh's own.
|
package consuming published dependencies, including the mesh's own.
|
||||||
|
|
||||||
The pipeline resolves modules into dependency **levels** and completes a level before starting
|
The pipeline resolves modules into dependency **levels** and completes a level before starting
|
||||||
@@ -96,7 +96,7 @@ since (see
|
|||||||
|
|
||||||
A module that owns state owns its migrations: numbered, written in the module's own language,
|
A module that owns state owns its migrations: numbered, written in the module's own language,
|
||||||
compiled with it, frozen once they have run anywhere, and idempotent so that re-running is safe
|
compiled with it, frozen once they have run anywhere, and idempotent so that re-running is safe
|
||||||
([ADR 0006](../../02-DECISIONS/0006-schema-changes-are-numbered-migrations.md)).
|
([ADR 0003](../../02-DECISIONS/0003-schema-changes-are-numbered-migrations.md)).
|
||||||
|
|
||||||
Two kinds exist and the distinction matters: migrations against the module's **own** local
|
Two kinds exist and the distinction matters: migrations against the module's **own** local
|
||||||
state, and migrations against a **provisioned** resource, which run on the node that consumes
|
state, and migrations against a **provisioned** resource, which run on the node that consumes
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ status: implemented
|
|||||||
code: [hal]
|
code: [hal]
|
||||||
updated: 2026-08-23
|
updated: 2026-08-23
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0004-managed-files-are-generated-never-edited.md
|
- 02-DECISIONS/0002-managed-files-are-generated-never-edited.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# Provisioning
|
# Provisioning
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ status: implemented
|
|||||||
code: [hal]
|
code: [hal]
|
||||||
updated: 2026-08-23
|
updated: 2026-08-23
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# Delivery — from a push to a running node
|
# Delivery — from a push to a running node
|
||||||
@@ -31,7 +31,7 @@ merge that created no pipeline, and nothing said so**.
|
|||||||
## Three silos
|
## Three silos
|
||||||
|
|
||||||
Cardinality is the whole point, and the three differ
|
Cardinality is the whole point, and the three differ
|
||||||
([ADR 0058](../../02-DECISIONS/0058-delivery.md)):
|
([ADR 0023](../../02-DECISIONS/0023-delivery.md)):
|
||||||
|
|
||||||
| Silo | Runs | Where | Does |
|
| Silo | Runs | Where | Does |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
@@ -50,7 +50,7 @@ later stage runs.
|
|||||||
## The artifact
|
## The artifact
|
||||||
|
|
||||||
The artifact is **build output** — compiled and bundled with its dependency graph inlined —
|
The artifact is **build output** — compiled and bundled with its dependency graph inlined —
|
||||||
never a filtered copy of source ([ADR 0058](../../02-DECISIONS/0058-delivery.md)).
|
never a filtered copy of source ([ADR 0023](../../02-DECISIONS/0023-delivery.md)).
|
||||||
A deploy is extract-and-run and touches no network.
|
A deploy is extract-and-run and touches no network.
|
||||||
|
|
||||||
The consequence is the whole cost of the decision: **anything not in the build output does not
|
The consequence is the whole cost of the decision: **anything not in the build output does not
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ status: implemented
|
|||||||
code: [hal]
|
code: [hal]
|
||||||
updated: 2026-08-23
|
updated: 2026-08-23
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0018-the-mesh-creates-no-symlinks.md
|
- 02-DECISIONS/0010-the-mesh-creates-no-symlinks.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# The node runtime, and how a node comes into being
|
# The node runtime, and how a node comes into being
|
||||||
@@ -38,7 +38,7 @@ suggestive word in the system names the node runtime, and the component whose ma
|
|||||||
"mesh messaging" is documented elsewhere as the interactive runtime. Anatomy makes attractive
|
"mesh messaging" is documented elsewhere as the interactive runtime. Anatomy makes attractive
|
||||||
names and poor boundaries.
|
names and poor boundaries.
|
||||||
|
|
||||||
[ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md) replaces this with names
|
[ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md) replaces this with names
|
||||||
taken from what each part owns. Until then, this is the vocabulary in the code.
|
taken from what each part owns. Until then, this is the vocabulary in the code.
|
||||||
|
|
||||||
## Starting a module
|
## Starting a module
|
||||||
@@ -57,14 +57,14 @@ outstanding local migrations, create data directories with the right ownership,
|
|||||||
service under supervision.
|
service under supervision.
|
||||||
|
|
||||||
**The installer is the only thing that creates a link** ([ADR
|
**The installer is the only thing that creates a link** ([ADR
|
||||||
0011](../../02-DECISIONS/0018-the-mesh-creates-no-symlinks.md)). It reconciles rather than assumes: a
|
0011](../../02-DECISIONS/0010-the-mesh-creates-no-symlinks.md)). It reconciles rather than assumes: a
|
||||||
missing link is created, a stale one repointed, and a real file found where a link belongs is
|
missing link is created, a stale one repointed, and a real file found where a link belongs is
|
||||||
adopted into the node's override area and replaced. Nothing else — not a hook, not a fix, not a
|
adopted into the node's override area and replaced. Nothing else — not a hook, not a fix, not a
|
||||||
person debugging — creates one.
|
person debugging — creates one.
|
||||||
|
|
||||||
That is the as-is. The intent is to remove linking altogether and derive a real file instead,
|
That is the as-is. The intent is to remove linking altogether and derive a real file instead,
|
||||||
which the reconciliation machinery already makes possible
|
which the reconciliation machinery already makes possible
|
||||||
([ADR 0018](../../02-DECISIONS/0018-the-mesh-creates-no-symlinks.md), proposed). What is described above
|
([ADR 0010](../../02-DECISIONS/0010-the-mesh-creates-no-symlinks.md), proposed). What is described above
|
||||||
is what runs today.
|
is what runs today.
|
||||||
|
|
||||||
## Supervision
|
## Supervision
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ status: implemented
|
|||||||
code: [hal]
|
code: [hal]
|
||||||
updated: 2026-08-23
|
updated: 2026-08-23
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0004-managed-files-are-generated-never-edited.md
|
- 02-DECISIONS/0002-managed-files-are-generated-never-edited.md
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# Configuration and secrets
|
# Configuration and secrets
|
||||||
@@ -17,7 +17,7 @@ files is **generated**.
|
|||||||
|
|
||||||
A managed file is derived from the mesh database. A synchroniser rewrites it when the values
|
A managed file is derived from the mesh database. A synchroniser rewrites it when the values
|
||||||
behind it change. The write path is the mesh operation that owns the value; the file is an
|
behind it change. The write path is the mesh operation that owns the value; the file is an
|
||||||
output ([ADR 0004](../../02-DECISIONS/0004-managed-files-are-generated-never-edited.md)).
|
output ([ADR 0002](../../02-DECISIONS/0002-managed-files-are-generated-never-edited.md)).
|
||||||
|
|
||||||
An edit to a managed file survives until the next synchronisation and is then overwritten
|
An edit to a managed file survives until the next synchronisation and is then overwritten
|
||||||
silently, taking whatever it was fixing with it — bringing back the bug the edit had removed,
|
silently, taking whatever it was fixing with it — bringing back the bug the edit had removed,
|
||||||
@@ -61,7 +61,7 @@ are both left behind. Configuration is additive in practice, whatever the manife
|
|||||||
Generated secrets are produced by the mesh, never authored. Provisioned credentials arrive as
|
Generated secrets are produced by the mesh, never authored. Provisioned credentials arrive as
|
||||||
database overrides written by the provisioner and are marked as such, so they can be
|
database overrides written by the provisioner and are marked as such, so they can be
|
||||||
distinguished from a deliberate override and cleaned up when the grant is removed
|
distinguished from a deliberate override and cleaned up when the grant is removed
|
||||||
([ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md)).
|
([ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)).
|
||||||
|
|
||||||
Nothing in the repository contains a credential. The repository has no per-node content at all,
|
Nothing in the repository contains a credential. The repository has no per-node content at all,
|
||||||
which is what makes that guarantee structural rather than a matter of care.
|
which is what makes that guarantee structural rather than a matter of care.
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ owning approval and promotion at the boundary. Proposals to edit are reviewed ra
|
|||||||
applied.
|
applied.
|
||||||
|
|
||||||
This is where the mesh's **governed** documents live, including the constitution injected into
|
This is where the mesh's **governed** documents live, including the constitution injected into
|
||||||
design sessions ([ADR 0009](../../02-DECISIONS/0009-the-mesh-is-governed-by-a-constitution.md)).
|
design sessions ([ADR 0005](../../02-DECISIONS/0005-the-mesh-is-governed-by-a-constitution.md)).
|
||||||
|
|
||||||
## Why both
|
## Why both
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ status: implemented
|
|||||||
code: [hal]
|
code: [hal]
|
||||||
updated: 2026-08-23
|
updated: 2026-08-23
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0012-agents-are-persistent-employees.md
|
- 02-DECISIONS/0007-agents-are-persistent-employees.md
|
||||||
- 02-DECISIONS/0009-the-mesh-is-governed-by-a-constitution.md
|
- 02-DECISIONS/0005-the-mesh-is-governed-by-a-constitution.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# Agents and work
|
# Agents and work
|
||||||
@@ -17,7 +17,7 @@ model they run under is the employee model, not a worker pool.
|
|||||||
|
|
||||||
An agent is a singular named identity with a home node, a workspace on that node, accumulating
|
An agent is a singular named identity with a home node, a workspace on that node, accumulating
|
||||||
memory, and an explicit lifecycle
|
memory, and an explicit lifecycle
|
||||||
([ADR 0012](../../02-DECISIONS/0012-agents-are-persistent-employees.md)).
|
([ADR 0007](../../02-DECISIONS/0007-agents-are-persistent-employees.md)).
|
||||||
|
|
||||||
| Property | Meaning |
|
| Property | Meaning |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -45,7 +45,7 @@ Both hold identity, both act, both accumulate memory.
|
|||||||
|
|
||||||
The mesh does not currently record modality completely. Which user, on which node, a human
|
The mesh does not currently record modality completely. Which user, on which node, a human
|
||||||
agent acts as is **required by the model and not stored** — an open question carried over from
|
agent acts as is **required by the model and not stored** — an open question carried over from
|
||||||
[ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md).
|
[ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md).
|
||||||
|
|
||||||
## Work
|
## Work
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ template that names the phases.
|
|||||||
This is where governance meets execution. The constitution is injected into every eligible
|
This is where governance meets execution. The constitution is injected into every eligible
|
||||||
meeting turn — agents do not fetch it, it arrives — and a check phase verifies the meeting's
|
meeting turn — agents do not fetch it, it arrives — and a check phase verifies the meeting's
|
||||||
output against it before the meeting may proceed
|
output against it before the meeting may proceed
|
||||||
([ADR 0009](../../02-DECISIONS/0009-the-mesh-is-governed-by-a-constitution.md)). A named violation
|
([ADR 0005](../../02-DECISIONS/0005-the-mesh-is-governed-by-a-constitution.md)). A named violation
|
||||||
blocks progress.
|
blocks progress.
|
||||||
|
|
||||||
Meeting turns run on the orchestrator's node regardless of where the participating agents are
|
Meeting turns run on the orchestrator's node regardless of where the participating agents are
|
||||||
@@ -84,5 +84,5 @@ integrate through the record, never through a shared schema* — being violated
|
|||||||
own largest component, and it is the reason work that belongs to one domain keeps having to be
|
own largest component, and it is the reason work that belongs to one domain keeps having to be
|
||||||
implemented in another.
|
implemented in another.
|
||||||
|
|
||||||
[ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md) dissolves that arrangement.
|
[ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md) dissolves that arrangement.
|
||||||
Until it does, this is the shape.
|
Until it does, this is the shape.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ code: [hal]
|
|||||||
updated: 2026-08-23
|
updated: 2026-08-23
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0001-nodes-communicate-over-a-broker.md
|
- 02-DECISIONS/0001-nodes-communicate-over-a-broker.md
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# Interfaces and observability
|
# Interfaces and observability
|
||||||
|
|||||||
@@ -4,16 +4,16 @@ status: implemented
|
|||||||
code: [hal]
|
code: [hal]
|
||||||
updated: 2026-08-23
|
updated: 2026-08-23
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0010-applications-live-in-their-own-repository.md
|
- 02-DECISIONS/0006-applications-live-in-their-own-repository.md
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# The catalogue, and what its shape says
|
# The catalogue, and what its shape says
|
||||||
|
|
||||||
The catalogue holds **124 modules**. Thirty-three belong to the mesh's own domain; the other
|
The catalogue holds **124 modules**. Thirty-three belong to the mesh's own domain; the other
|
||||||
ninety-one run *on* the mesh rather than being *of* it
|
ninety-one run *on* the mesh rather than being *of* it
|
||||||
([ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md)).
|
([ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md)).
|
||||||
|
|
||||||
The count is not the finding. The **shape** is.
|
The count is not the finding. The **shape** is.
|
||||||
|
|
||||||
@@ -55,11 +55,11 @@ connectivity is made four times.
|
|||||||
the unit of one piece of software, because that is the only granularity the module system
|
the unit of one piece of software, because that is the only granularity the module system
|
||||||
offers.
|
offers.
|
||||||
|
|
||||||
This is the same failure [ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md)
|
This is the same failure [ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md)
|
||||||
names for the platform core — *boundaries drawn by deployment accident rather than by domain* —
|
names for the platform core — *boundaries drawn by deployment accident rather than by domain* —
|
||||||
appearing outside it, at four times the scale. The core is being recomposed; the flat level is
|
appearing outside it, at four times the scale. The core is being recomposed; the flat level is
|
||||||
addressed in principle by
|
addressed in principle by
|
||||||
[ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md), which
|
[ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md), which
|
||||||
deliberately does not yet settle the domain list.
|
deliberately does not yet settle the domain list.
|
||||||
|
|
||||||
## Where the shape came from
|
## Where the shape came from
|
||||||
@@ -90,7 +90,7 @@ never stated as assumptions — they were just how the thing already worked.
|
|||||||
|
|
||||||
**This is the most useful single fact for anyone changing the catalogue**, and it is why the
|
**This is the most useful single fact for anyone changing the catalogue**, and it is why the
|
||||||
linking principle in particular reads as a deliberate architectural choice when it is an
|
linking principle in particular reads as a deliberate architectural choice when it is an
|
||||||
inheritance. See [ADR 0018](../../02-DECISIONS/0018-the-mesh-creates-no-symlinks.md), whose case
|
inheritance. See [ADR 0010](../../02-DECISIONS/0010-the-mesh-creates-no-symlinks.md), whose case
|
||||||
this strengthens: the argument for links was never made *for a mesh*.
|
this strengthens: the argument for links was never made *for a mesh*.
|
||||||
|
|
||||||
It also explains the measurement in
|
It also explains the measurement in
|
||||||
@@ -108,7 +108,7 @@ which is what makes dogfooding structural rather than a discipline, and what mak
|
|||||||
module out of the repository safe.
|
module out of the repository safe.
|
||||||
|
|
||||||
**Placement is already decided.** A standalone application belongs in its own repository
|
**Placement is already decided.** A standalone application belongs in its own repository
|
||||||
([ADR 0010](../../02-DECISIONS/0010-applications-live-in-their-own-repository.md)), and reviewers reject
|
([ADR 0006](../../02-DECISIONS/0006-applications-live-in-their-own-repository.md)), and reviewers reject
|
||||||
it in the monorepo. The catalogue's flat level is not a dumping ground by policy; it is one by
|
it in the monorepo. The catalogue's flat level is not a dumping ground by policy; it is one by
|
||||||
history.
|
history.
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ status: implemented
|
|||||||
code: [mesh-lab]
|
code: [mesh-lab]
|
||||||
updated: 2026-08-25
|
updated: 2026-08-25
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# The lab, as it stands
|
# The lab, as it stands
|
||||||
@@ -63,7 +63,7 @@ is built.
|
|||||||
|
|
||||||
**The drawing was never designed.** `diagram` renders a scenario as draw.io, from the
|
**The drawing was never designed.** `diagram` renders a scenario as draw.io, from the
|
||||||
declaration or from the running instance, and it exists because it was asked for during the
|
declaration or from the running instance, and it exists because it was asked for during the
|
||||||
build. It has tests and a decision record ([ADR 0035](../../02-DECISIONS/0035-a-picture-is-read-from-what-runs.md),
|
build. It has tests and a decision record ([ADR 0014](../../02-DECISIONS/0014-a-picture-is-read-from-what-runs.md),
|
||||||
proposed) but no document in the to-be layer. It is recorded here because it runs, not because
|
proposed) but no document in the to-be layer. It is recorded here because it runs, not because
|
||||||
it was planned.
|
it was planned.
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ and snapshots roughly 76× slower, which does not make the lab slow, it makes it
|
|||||||
|
|
||||||
`npm run check` — typecheck over source *and* tests, then the offline suite, then integration
|
`npm run check` — typecheck over source *and* tests, then the offline suite, then integration
|
||||||
against a real hypervisor. Mocking the hypervisor is forbidden
|
against a real hypervisor. Mocking the hypervisor is forbidden
|
||||||
([ADR 0034](../../02-DECISIONS/0034-a-test-defends-a-decision.md), proposed): a test that fakes
|
([ADR 0013](../../02-DECISIONS/0013-a-test-defends-a-decision.md), proposed): a test that fakes
|
||||||
the system under integration asserts that the fake behaves as expected.
|
the system under integration asserts that the fake behaves as expected.
|
||||||
|
|
||||||
Integration tests **skip with a reason** on a machine that cannot raise scenarios, rather than
|
Integration tests **skip with a reason** on a machine that cannot raise scenarios, rather than
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ layer: to-be
|
|||||||
status: designed
|
status: designed
|
||||||
code: [hal]
|
code: [hal]
|
||||||
updated: 2026-08-23
|
updated: 2026-08-23
|
||||||
decisions: [02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md]
|
decisions: [02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md]
|
||||||
---
|
---
|
||||||
|
|
||||||
# Work breakdown — the decomposition
|
# Work breakdown — the decomposition
|
||||||
|
|
||||||
How [ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md) gets built, in what order, and where a human must look.
|
How [ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md) gets built, in what order, and where a human must look.
|
||||||
|
|
||||||
Ordering is not preference. Each phase removes a constraint the next one needs gone.
|
Ordering is not preference. Each phase removes a constraint the next one needs gone.
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ status: in-progress
|
|||||||
code: [mesh-lab]
|
code: [mesh-lab]
|
||||||
updated: 2026-08-23
|
updated: 2026-08-23
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0019-how-this-repository-works.md
|
- 02-DECISIONS/0011-how-this-repository-works.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# End-to-end testing
|
# End-to-end testing
|
||||||
@@ -37,7 +37,7 @@ today, that is a gap in the vocabulary rather than a reason to privilege that sh
|
|||||||
|
|
||||||
The design below describes a scenario as a complete mesh — forge (Gitea), coordinator,
|
The design below describes a scenario as a complete mesh — forge (Gitea), coordinator,
|
||||||
delivery cascade — because what it tests is a module. **That is the larger of two classes, and
|
delivery cascade — because what it tests is a module. **That is the larger of two classes, and
|
||||||
not the first one built** ([ADR 0016](../../02-DECISIONS/0016-the-lab.md)).
|
not the first one built** ([ADR 0009](../../02-DECISIONS/0009-the-lab.md)).
|
||||||
|
|
||||||
| | **Bootstrap scenario** | **Full scenario** |
|
| | **Bootstrap scenario** | **Full scenario** |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -127,7 +127,7 @@ drifts.
|
|||||||
a mesh named by the request instead.
|
a mesh named by the request instead.
|
||||||
- **Scenarios must be concurrent and cheap.** Several agents working means several scenarios
|
- **Scenarios must be concurrent and cheap.** Several agents working means several scenarios
|
||||||
at once, each needing its own network and nodes. A lab node is a virtual machine
|
at once, each needing its own network and nodes. A lab node is a virtual machine
|
||||||
([ADR 0016](../../02-DECISIONS/0016-the-lab.md)), and snapshots are
|
([ADR 0009](../../02-DECISIONS/0009-the-lab.md)), and snapshots are
|
||||||
what make repetition cheap — restoring a scenario costs far less than building one. The
|
what make repetition cheap — restoring a scenario costs far less than building one. The
|
||||||
earlier argument here, that only system containers made this affordable, was superseded: the
|
earlier argument here, that only system containers made this affordable, was superseded: the
|
||||||
scale it assumed was invented rather than required.
|
scale it assumed was invented rather than required.
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ status: in-progress
|
|||||||
code: [mesh-lab]
|
code: [mesh-lab]
|
||||||
updated: 2026-08-25
|
updated: 2026-08-25
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# The scenario declaration
|
# The scenario declaration
|
||||||
@@ -15,7 +15,7 @@ A scenario is a **declaration of an underlay**, plus what to put on it. It is th
|
|||||||
everything in the lab hangs off, so it is worth getting small.
|
everything in the lab hangs off, so it is worth getting small.
|
||||||
|
|
||||||
It states what a hosting provider and a home router would provide, and nothing the mesh is
|
It states what a hosting provider and a home router would provide, and nothing the mesh is
|
||||||
responsible for ([ADR 0016](../../02-DECISIONS/0016-the-lab.md)).
|
responsible for ([ADR 0009](../../02-DECISIONS/0009-the-lab.md)).
|
||||||
|
|
||||||
## Public networks are unrelated, and routed rather than bridged
|
## Public networks are unrelated, and routed rather than bridged
|
||||||
|
|
||||||
@@ -258,7 +258,7 @@ otherwise explicit declaration, and it exists because NAT has to run somewhere.
|
|||||||
|
|
||||||
It is a **container, not a virtual machine** — a router is scenery rather than something under
|
It is a **container, not a virtual machine** — a router is scenery rather than something under
|
||||||
test, so the fidelity argument that makes a node a virtual machine does not reach it
|
test, so the fidelity argument that makes a node a virtual machine does not reach it
|
||||||
([ADR 0016](../../02-DECISIONS/0016-the-lab.md)). What a router must
|
([ADR 0009](../../02-DECISIONS/0009-the-lab.md)). What a router must
|
||||||
reproduce is kernel behaviour, and a container has the same kernel.
|
reproduce is kernel behaviour, and a container has the same kernel.
|
||||||
|
|
||||||
**`machines[].at`** — segment and addresses, or a **list** of them for a machine on several
|
**`machines[].at`** — segment and addresses, or a **list** of them for a machine on several
|
||||||
@@ -299,7 +299,7 @@ belongs to a router it does not control, and asleep.
|
|||||||
|
|
||||||
Whether the overlay survives that, re-forms, and is noticed to have changed endpoint is
|
Whether the overlay survives that, re-forms, and is noticed to have changed endpoint is
|
||||||
**observed**, never arranged
|
**observed**, never arranged
|
||||||
([ADR 0016](../../02-DECISIONS/0016-the-lab.md)).
|
([ADR 0009](../../02-DECISIONS/0009-the-lab.md)).
|
||||||
|
|
||||||
## Why the addresses are load-bearing
|
## Why the addresses are load-bearing
|
||||||
|
|
||||||
@@ -320,13 +320,13 @@ it must be.
|
|||||||
The format should make getting this wrong hard rather than merely documented: a segment without
|
The format should make getting this wrong hard rather than merely documented: a segment without
|
||||||
a `gateway:` is a public segment, and an address in it — including a gateway's `address:` — that
|
a `gateway:` is a public segment, and an address in it — including a gateway's `address:` — that
|
||||||
is not documentation space is a declaration error, refused before anything is raised. That is
|
is not documentation space is a declaration error, refused before anything is raised. That is
|
||||||
[ADR 0058](../../02-DECISIONS/0058-delivery.md) applied to a configuration
|
[ADR 0023](../../02-DECISIONS/0023-delivery.md) applied to a configuration
|
||||||
file: the failure it prevents is silent, so the check has to be loud.
|
file: the failure it prevents is silent, so the check has to be loud.
|
||||||
|
|
||||||
## The same declaration serves both classes
|
## The same declaration serves both classes
|
||||||
|
|
||||||
The bootstrap and full scenarios differ **only in `place:`**
|
The bootstrap and full scenarios differ **only in `place:`**
|
||||||
([ADR 0016](../../02-DECISIONS/0016-the-lab.md)). Everything
|
([ADR 0009](../../02-DECISIONS/0009-the-lab.md)). Everything
|
||||||
about the underlay is identical, which is what makes one a strict subset of the other rather
|
about the underlay is identical, which is what makes one a strict subset of the other rather
|
||||||
than a fork.
|
than a fork.
|
||||||
|
|
||||||
@@ -354,7 +354,7 @@ not first.
|
|||||||
## What a scenario deliberately cannot say
|
## What a scenario deliberately cannot say
|
||||||
|
|
||||||
- **Overlay addresses, the hub, peer configuration.** Outcomes, not inputs
|
- **Overlay addresses, the hub, peer configuration.** Outcomes, not inputs
|
||||||
([ADR 0016](../../02-DECISIONS/0016-the-lab.md)).
|
([ADR 0009](../../02-DECISIONS/0009-the-lab.md)).
|
||||||
- **What a machine is in mesh terms** — server or workstation, its site, its names. Mesh
|
- **What a machine is in mesh terms** — server or workstation, its site, its names. Mesh
|
||||||
configuration, established by the mesh.
|
configuration, established by the mesh.
|
||||||
- **A host's capability profile.** Detected, never declared.
|
- **A host's capability profile.** Detected, never declared.
|
||||||
@@ -543,7 +543,7 @@ cannot yet express.
|
|||||||
|
|
||||||
Nothing here mentions overlay addresses, which node is the hub, who peers with whom, any name,
|
Nothing here mentions overlay addresses, which node is the hub, who peers with whom, any name,
|
||||||
or any certificate. Research 004 recorded all of those for this topology, and **a scenario must
|
or any certificate. Research 004 recorded all of those for this topology, and **a scenario must
|
||||||
not state them** ([ADR 0016](../../02-DECISIONS/0016-the-lab.md)): they
|
not state them** ([ADR 0009](../../02-DECISIONS/0009-the-lab.md)): they
|
||||||
are what the mesh does, and a scenario that supplied them would be certifying its own work.
|
are what the mesh does, and a scenario that supplied them would be certifying its own work.
|
||||||
|
|
||||||
The absence is the point. Given the declaration above, whether a hub is elected, whether the
|
The absence is the point. Given the declaration above, whether a hub is elected, whether the
|
||||||
|
|||||||
@@ -4,16 +4,16 @@ status: in-progress
|
|||||||
code: [mesh-lab]
|
code: [mesh-lab]
|
||||||
updated: 2026-08-25
|
updated: 2026-08-25
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# Scenario lifecycle
|
# Scenario lifecycle
|
||||||
|
|
||||||
The first thing the lab must do, and the only thing it must do before anything else can be
|
The first thing the lab must do, and the only thing it must do before anything else can be
|
||||||
written: **materialise a mesh, return it to a known state, and destroy it**
|
written: **materialise a mesh, return it to a known state, and destroy it**
|
||||||
([ADR 0016](../../02-DECISIONS/0016-the-lab.md)).
|
([ADR 0009](../../02-DECISIONS/0009-the-lab.md)).
|
||||||
|
|
||||||
A [declaration](02-scenario-declaration.md) describes a scenario. This describes what happens
|
A [declaration](02-scenario-declaration.md) describes a scenario. This describes what happens
|
||||||
to one.
|
to one.
|
||||||
@@ -39,7 +39,7 @@ The order is not arbitrary — each step needs the one before it to exist:
|
|||||||
|
|
||||||
1. **Segments.** Isolated links, one per declared segment, belonging to this instance and
|
1. **Segments.** Isolated links, one per declared segment, belonging to this instance and
|
||||||
joined to nothing outside it
|
joined to nothing outside it
|
||||||
([ADR 0016](../../02-DECISIONS/0016-the-lab.md)).
|
([ADR 0009](../../02-DECISIONS/0009-the-lab.md)).
|
||||||
2. **Gateways.** Derived, never declared as machines: a gateway is materialised for each
|
2. **Gateways.** Derived, never declared as machines: a gateway is materialised for each
|
||||||
distinct `gateway:` declaration, sitting on both its segment and its parent, carrying the
|
distinct `gateway:` declaration, sitting on both its segment and its parent, carrying the
|
||||||
translation, forwarding and mapping-expiry the declaration asked for.
|
translation, forwarding and mapping-expiry the declaration asked for.
|
||||||
@@ -60,7 +60,7 @@ habit.
|
|||||||
## A failed raise leaves the wreckage
|
## A failed raise leaves the wreckage
|
||||||
|
|
||||||
A step that fails stops the raise
|
A step that fails stops the raise
|
||||||
([ADR 0058](../../02-DECISIONS/0058-delivery.md)) — and **does not tear
|
([ADR 0023](../../02-DECISIONS/0023-delivery.md)) — and **does not tear
|
||||||
down**.
|
down**.
|
||||||
|
|
||||||
Tearing down on failure destroys the only evidence of what went wrong, which is precisely
|
Tearing down on failure destroys the only evidence of what went wrong, which is precisely
|
||||||
@@ -100,7 +100,7 @@ made after it, and returning undoes it like any other change.
|
|||||||
## Reaching in
|
## Reaching in
|
||||||
|
|
||||||
Everything the lab does to a machine goes through the virtualisation layer, never over IP
|
Everything the lab does to a machine goes through the virtualisation layer, never over IP
|
||||||
([ADR 0016](../../02-DECISIONS/0016-the-lab.md)). `exec` runs a
|
([ADR 0009](../../02-DECISIONS/0009-the-lab.md)). `exec` runs a
|
||||||
command on a machine and returns its output.
|
command on a machine and returns its output.
|
||||||
|
|
||||||
This has one consequence worth stating plainly: **a reachability question is asked from inside**.
|
This has one consequence worth stating plainly: **a reachability question is asked from inside**.
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ status: designed
|
|||||||
code: [mesh-lab]
|
code: [mesh-lab]
|
||||||
updated: 2026-08-24
|
updated: 2026-08-24
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0016-the-lab.md
|
- 02-DECISIONS/0009-the-lab.md
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# Installing the lab on a clean machine
|
# Installing the lab on a clean machine
|
||||||
|
|
||||||
The lab has prerequisites — a virtualisation daemon, copy-on-write storage, a pool, an identity
|
The lab has prerequisites — a virtualisation daemon, copy-on-write storage, a pool, an identity
|
||||||
permitted to talk to it — and it cannot get them from the mesh, because it is where the mesh is
|
permitted to talk to it — and it cannot get them from the mesh, because it is where the mesh is
|
||||||
built ([ADR 0016](../../02-DECISIONS/0016-the-lab.md)).
|
built ([ADR 0009](../../02-DECISIONS/0009-the-lab.md)).
|
||||||
|
|
||||||
So the lab needs an install path of its own. This describes it, and the shape it has to take is
|
So the lab needs an install path of its own. This describes it, and the shape it has to take is
|
||||||
determined by two failures observed while measuring
|
determined by two failures observed while measuring
|
||||||
@@ -55,7 +55,7 @@ and unbounded at worst.
|
|||||||
|
|
||||||
**The lab refuses to run degraded.** It does not warn and continue: a warning about a slow inner
|
**The lab refuses to run degraded.** It does not warn and continue: a warning about a slow inner
|
||||||
loop is read once and ignored forever, and the loop stays slow. This is
|
loop is read once and ignored forever, and the loop stays slow. This is
|
||||||
[ADR 0058](../../02-DECISIONS/0058-delivery.md) applied where the failure is
|
[ADR 0023](../../02-DECISIONS/0023-delivery.md) applied where the failure is
|
||||||
performance rather than an error.
|
performance rather than an error.
|
||||||
|
|
||||||
## Two ways the prerequisites arrive
|
## Two ways the prerequisites arrive
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ status: in-progress
|
|||||||
code: [mesh-host]
|
code: [mesh-host]
|
||||||
updated: 2026-08-27
|
updated: 2026-08-27
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0019-how-this-repository-works.md
|
- 02-DECISIONS/0011-how-this-repository-works.md
|
||||||
- 02-DECISIONS/0036-a-node-and-how-it-joins.md
|
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0036-a-node-and-how-it-joins.md
|
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
|
||||||
- 02-DECISIONS/0036-a-node-and-how-it-joins.md
|
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# The node host
|
# The node host
|
||||||
@@ -25,11 +25,11 @@ Tier 0. The one thing ever installed by hand, and the only thing that changes a
|
|||||||
|
|
||||||
A **statically linked binary that requires nothing to be present** — copy it onto a machine and
|
A **statically linked binary that requires nothing to be present** — copy it onto a machine and
|
||||||
run it, and that is the whole installation
|
run it, and that is the whole installation
|
||||||
([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)). Written in Go, because the
|
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)). Written in Go, because the
|
||||||
job is system-level and because the host shares no code with any other tier.
|
job is system-level and because the host shares no code with any other tier.
|
||||||
|
|
||||||
A single binary with one job: **apply declared state on this machine**
|
A single binary with one job: **apply declared state on this machine**
|
||||||
([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)). Overlay
|
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)). Overlay
|
||||||
membership, packet filtering, packages, services, containers and filesystems are not six
|
membership, packet filtering, packages, services, containers and filesystems are not six
|
||||||
concerns it carries; they are six instances of the one.
|
concerns it carries; they are six instances of the one.
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ returns it.
|
|||||||
Three properties, each following a recorded decision:
|
Three properties, each following a recorded decision:
|
||||||
|
|
||||||
**A failed step fails the apply.** Not "logs and continues"
|
**A failed step fails the apply.** Not "logs and continues"
|
||||||
([ADR 0058](../../02-DECISIONS/0058-delivery.md)). A partial apply that
|
([ADR 0023](../../02-DECISIONS/0023-delivery.md)). A partial apply that
|
||||||
reports success is the mesh's most expensive shape.
|
reports success is the mesh's most expensive shape.
|
||||||
|
|
||||||
**Each applier reads back.** Setting a value is not evidence the value took. The firewall is
|
**Each applier reads back.** Setting a value is not evidence the value took. The firewall is
|
||||||
@@ -69,7 +69,7 @@ asked whether the rule loaded; conntrack is asked what timeout it holds. This is
|
|||||||
§5 as a component requirement rather than a review habit.
|
§5 as a component requirement rather than a review habit.
|
||||||
|
|
||||||
**What was applied is recorded after it works, never before**
|
**What was applied is recorded after it works, never before**
|
||||||
([ADR 0035](../../02-DECISIONS/0035-a-picture-is-read-from-what-runs.md)). A failed apply leaves
|
([ADR 0014](../../02-DECISIONS/0014-a-picture-is-read-from-what-runs.md)). A failed apply leaves
|
||||||
the machine in whatever state it reached, and nothing must claim otherwise.
|
the machine in whatever state it reached, and nothing must claim otherwise.
|
||||||
|
|
||||||
### store
|
### store
|
||||||
@@ -78,14 +78,14 @@ Local, and **authoritative while disconnected**. Not a cache of the control plan
|
|||||||
of what this node has applied and what it currently holds.
|
of what this node has applied and what it currently holds.
|
||||||
|
|
||||||
This is structural rather than convenient: if disconnection is an ordinary situation rather
|
This is structural rather than convenient: if disconnection is an ordinary situation rather
|
||||||
than an exception ([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)), the
|
than an exception ([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)), the
|
||||||
store is what makes it ordinary. A laptop shut for a week comes back and reconciles; it does
|
store is what makes it ordinary. A laptop shut for a week comes back and reconciles; it does
|
||||||
not come back and ask what it is.
|
not come back and ask what it is.
|
||||||
|
|
||||||
### link
|
### link
|
||||||
|
|
||||||
The node's one connection to the control plane, and its security boundary
|
The node's one connection to the control plane, and its security boundary
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)).
|
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)).
|
||||||
|
|
||||||
It is the broker connection that already exists
|
It is the broker connection that already exists
|
||||||
([ADR 0001](../../02-DECISIONS/0001-nodes-communicate-over-a-broker.md)) — outbound,
|
([ADR 0001](../../02-DECISIONS/0001-nodes-communicate-over-a-broker.md)) — outbound,
|
||||||
@@ -108,7 +108,7 @@ architecture, a network position.
|
|||||||
capability is real when it is present, running and working, and the difference is the whole
|
capability is real when it is present, running and working, and the difference is the whole
|
||||||
point of detecting it.
|
point of detecting it.
|
||||||
|
|
||||||
The profile is what makes [ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)
|
The profile is what makes [ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)
|
||||||
work: a node is a node, and what varies between them is here rather than in the definition.
|
work: a node is a node, and what varies between them is here rather than in the definition.
|
||||||
|
|
||||||
### inventory
|
### inventory
|
||||||
@@ -133,7 +133,7 @@ is the component; that one is what happens to it.
|
|||||||
## Where a declaration comes from
|
## Where a declaration comes from
|
||||||
|
|
||||||
One behaviour, two sources
|
One behaviour, two sources
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)):
|
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)):
|
||||||
|
|
||||||
| Situation | Source |
|
| Situation | Source |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -150,7 +150,7 @@ the mesh, and the full peer set arrives derived.
|
|||||||
|
|
||||||
## What a declaration is
|
## What a declaration is
|
||||||
|
|
||||||
Settled by [ADR 0037](../../02-DECISIONS/0037-the-node-host.md).
|
Settled by [ADR 0016](../../02-DECISIONS/0016-the-node-host.md).
|
||||||
|
|
||||||
**JSON**, because the host has no dependencies to spend and the standard library carries no
|
**JSON**, because the host has no dependencies to spend and the standard library carries no
|
||||||
YAML. **An ordered list of typed resources**, each with a stable identity — the order is stated
|
YAML. **An ordered list of typed resources**, each with a stable identity — the order is stated
|
||||||
@@ -187,8 +187,8 @@ Raising the substrate needs six shapes in the host's vocabulary, and **all six a
|
|||||||
| `directory`, `file` | **built** | no machine dependency at all |
|
| `directory`, `file` | **built** | no machine dependency at all |
|
||||||
| `service` | **built** | running/stopped **and** enabled/disabled at boot — a unit started but not enabled stops being true at the next reboot |
|
| `service` | **built** | running/stopped **and** enabled/disabled at boot — a unit started but not enabled stops being true at the next reboot |
|
||||||
| `package` | **built** | present, never upgraded, and **never uninstalled** — the host cannot know what else needs it, so dropping one is *forgotten*, not *removed* |
|
| `package` | **built** | present, never upgraded, and **never uninstalled** — the host cannot know what else needs it, so dropping one is *forgotten*, not *removed* |
|
||||||
| `container` | **built** | pinned by digest ([ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md)); identified by a label carrying a digest of the declaration that made it, because a runtime normalises what it is given and that is indistinguishable from drift |
|
| `container` | **built** | pinned by digest ([ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)); identified by a label carrying a digest of the declaration that made it, because a runtime normalises what it is given and that is indistinguishable from drift |
|
||||||
| `action` | **built** | bundle-only ([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)); verify is mandatory and is the idempotency check as well as the read-back |
|
| `action` | **built** | bundle-only ([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)); verify is mandatory and is the idempotency check as well as the read-back |
|
||||||
|
|
||||||
**The parser enforces the boundary rather than the caller remembering it.** `Parse` refuses an
|
**The parser enforces the boundary rather than the caller remembering it.** `Parse` refuses an
|
||||||
action and is what the link uses; `ParseTrusted` permits one and is what the bundle uses. The
|
action and is what the link uses; `ParseTrusted` permits one and is what the bundle uses. The
|
||||||
@@ -205,7 +205,7 @@ until it is done the substrate bootstrap has no end-to-end test.
|
|||||||
**3 — link and store.** The node connects, receives declarations, and holds what it applied.
|
**3 — link and store.** The node connects, receives declarations, and holds what it applied.
|
||||||
|
|
||||||
**4 — enrolment.** The one genuinely new mechanism in
|
**4 — enrolment.** The one genuinely new mechanism in
|
||||||
[ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md); everything else there
|
[ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md); everything else there
|
||||||
is configuration of what already runs.
|
is configuration of what already runs.
|
||||||
|
|
||||||
Stage 1 is deliberately the smallest useful thing. The lab currently raises **empty machines**
|
Stage 1 is deliberately the smallest useful thing. The lab currently raises **empty machines**
|
||||||
@@ -216,7 +216,7 @@ that, and every later stage is tested by a lab that already works.
|
|||||||
|
|
||||||
**The lab is the harness.** A scenario places a host on a machine and asserts what it did —
|
**The lab is the harness.** A scenario places a host on a machine and asserts what it did —
|
||||||
against a real hypervisor, with the boundary never mocked
|
against a real hypervisor, with the boundary never mocked
|
||||||
([ADR 0034](../../02-DECISIONS/0034-a-test-defends-a-decision.md)).
|
([ADR 0013](../../02-DECISIONS/0013-a-test-defends-a-decision.md)).
|
||||||
|
|
||||||
Each decision above owes a test:
|
Each decision above owes a test:
|
||||||
|
|
||||||
@@ -239,7 +239,7 @@ Each decision above owes a test:
|
|||||||
package, a unit, a container and a dataset *are*. Nothing has measured that surface, and it is
|
package, a unit, a container and a dataset *are*. Nothing has measured that surface, and it is
|
||||||
the residue of the question [`host-size.md`](../../01-RESEARCH/006-mesh-from-scratch/host-size.md)
|
the residue of the question [`host-size.md`](../../01-RESEARCH/006-mesh-from-scratch/host-size.md)
|
||||||
answered.
|
answered.
|
||||||
- **Rescue.** [ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md) suggests it is
|
- **Rescue.** [ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md) suggests it is
|
||||||
a node whose local state is discarded so the mesh re-derives it, and does not decide it.
|
a node whose local state is discarded so the mesh re-derives it, and does not decide it.
|
||||||
- **What may expire.** An identity needing refresh to stay valid would make a laptop fail for
|
- **What may expire.** An identity needing refresh to stay valid would make a laptop fail for
|
||||||
being a laptop ([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)).
|
being a laptop ([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)).
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ status: designed
|
|||||||
code: []
|
code: []
|
||||||
updated: 2026-08-27
|
updated: 2026-08-27
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0048-the-substrate-and-the-control-plane.md
|
- 02-DECISIONS/0021-the-substrate-and-the-control-plane.md
|
||||||
- 02-DECISIONS/0019-how-this-repository-works.md
|
- 02-DECISIONS/0011-how-this-repository-works.md
|
||||||
- 02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md
|
- 02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md
|
||||||
- 02-DECISIONS/0048-the-substrate-and-the-control-plane.md
|
- 02-DECISIONS/0021-the-substrate-and-the-control-plane.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# The control plane
|
# The control plane
|
||||||
@@ -24,7 +24,7 @@ This document defines it. It does **not** design the contexts inside it; those a
|
|||||||
> **The control plane is everything that needs to know about more than one node.**
|
> **The control plane is everything that needs to know about more than one node.**
|
||||||
|
|
||||||
That is the whole test, and it is not arbitrary — it follows from
|
That is the whole test, and it is not arbitrary — it follows from
|
||||||
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md). The host applies and
|
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md). The host applies and
|
||||||
does not decide *because deciding needs knowledge the machine does not have*. So the line falls
|
does not decide *because deciding needs knowledge the machine does not have*. So the line falls
|
||||||
exactly there:
|
exactly there:
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ catch it because the dependency direction is still correct.
|
|||||||
## What is inside it
|
## What is inside it
|
||||||
|
|
||||||
**Seven contexts and one interface**
|
**Seven contexts and one interface**
|
||||||
([ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md)) —
|
([ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)) —
|
||||||
each one earning its place by the test above rather than by being ours:
|
each one earning its place by the test above rather than by being ours:
|
||||||
|
|
||||||
| | | needs to know about more than one node because |
|
| | | needs to know about more than one node because |
|
||||||
@@ -64,7 +64,7 @@ anything else does. A task does not need to know a node exists, and *being ours
|
|||||||
something infrastructure*. `ai` is folded into `config`: a provider licence is an ordinary grant.
|
something infrastructure*. `ai` is folded into `config`: a provider licence is an ordinary grant.
|
||||||
|
|
||||||
**`record` is an open question rather than an eighth entry.** Contexts integrate through it
|
**`record` is an open question rather than an eighth entry.** Contexts integrate through it
|
||||||
([ADR 0045](../../02-DECISIONS/0045-a-context-owns-its-store.md)), which makes it load-bearing,
|
([ADR 0020](../../02-DECISIONS/0020-a-context-owns-its-store.md)), which makes it load-bearing,
|
||||||
and [research 006](../../01-RESEARCH/006-mesh-from-scratch/skeleton.md) leaves *where it lives*
|
and [research 006](../../01-RESEARCH/006-mesh-from-scratch/skeleton.md) leaves *where it lives*
|
||||||
unresolved — putting it in the substrate risks recreating the circularity the tier design just
|
unresolved — putting it in the substrate risks recreating the circularity the tier design just
|
||||||
removed. Listing it here would settle by naming what has not been settled by arguing.
|
removed. Listing it here would settle by naming what has not been settled by arguing.
|
||||||
@@ -88,10 +88,10 @@ because each one alone reads like a detail:
|
|||||||
|
|
||||||
| | |
|
| | |
|
||||||
|---|---|
|
|---|---|
|
||||||
| [ADR 0037](../../02-DECISIONS/0037-the-node-host.md) | the host never queries the mesh database |
|
| [ADR 0016](../../02-DECISIONS/0016-the-node-host.md) | the host never queries the mesh database |
|
||||||
| [ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md) | a node holds its own identity **and nothing else** — the shared database credential every node carries today is the exposure this exists to remove |
|
| [ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md) | a node holds its own identity **and nothing else** — the shared database credential every node carries today is the exposure this exists to remove |
|
||||||
| [ADR 0045](../../02-DECISIONS/0045-a-context-owns-its-store.md) | a context is granted only what it **exclusively** owns: no shared writes, no read-only roles |
|
| [ADR 0020](../../02-DECISIONS/0020-a-context-owns-its-store.md) | a context is granted only what it **exclusively** owns: no shared writes, no read-only roles |
|
||||||
| [ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md) | there is no single mesh database, and nothing reads one |
|
| [ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md) | there is no single mesh database, and nothing reads one |
|
||||||
|
|
||||||
### So how does anything get in
|
### So how does anything get in
|
||||||
|
|
||||||
@@ -123,17 +123,17 @@ node ──► broker ──► the control plane, consuming
|
|||||||
Seven contexts, **one deployable** — they are not separate services, so this is one process
|
Seven contexts, **one deployable** — they are not separate services, so this is one process
|
||||||
consuming and dispatching internally, not seven consumers racing. Each context then writes only
|
consuming and dispatching internally, not seven consumers racing. Each context then writes only
|
||||||
the store it exclusively owns
|
the store it exclusively owns
|
||||||
([ADR 0045](../../02-DECISIONS/0045-a-context-owns-its-store.md)).
|
([ADR 0020](../../02-DECISIONS/0020-a-context-owns-its-store.md)).
|
||||||
|
|
||||||
**One consumer is a property worth having**, not just a consequence of
|
**One consumer is a property worth having**, not just a consequence of
|
||||||
[ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md). The as-is records that
|
[ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md). The as-is records that
|
||||||
*two consumers accidentally sharing one queue silently split the traffic between them, each
|
*two consumers accidentally sharing one queue silently split the traffic between them, each
|
||||||
receiving half of what it expects* — which has happened, between a module's daemon and its
|
receiving half of what it expects* — which has happened, between a module's daemon and its
|
||||||
capability server. With one consumer that class of fault cannot arise.
|
capability server. With one consumer that class of fault cannot arise.
|
||||||
|
|
||||||
**And the broker is the buffer while the control plane is down.** Nodes go on publishing;
|
**And the broker is the buffer while the control plane is down.** Nodes go on publishing;
|
||||||
messages queue; the control plane drains them when it returns. That is what makes
|
messages queue; the control plane drains them when it returns. That is what makes
|
||||||
[ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md)'s single control plane
|
[ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)'s single control plane
|
||||||
tolerable — an outage delays the mesh's *knowledge* rather than losing it.
|
tolerable — an outage delays the mesh's *knowledge* rather than losing it.
|
||||||
|
|
||||||
**With one consequence that must be bounded before it is discovered:** a queue with no limit
|
**With one consequence that must be bounded before it is discovered:** a queue with no limit
|
||||||
@@ -149,12 +149,12 @@ before designing for throughput.** The registry is `inventory`'s store: nodes, m
|
|||||||
assignments, versions. Those change when somebody changes something.
|
assignments, versions. Those change when somebody changes something.
|
||||||
|
|
||||||
**Logs, metrics and health checks belong to `observability`**, which owns a different store
|
**Logs, metrics and health checks belong to `observability`**, which owns a different store
|
||||||
([ADR 0045](../../02-DECISIONS/0045-a-context-owns-its-store.md)). Sending them to the registry
|
([ADR 0020](../../02-DECISIONS/0020-a-context-owns-its-store.md)). Sending them to the registry
|
||||||
would be exactly the shared-schema mistake 0045 exists to stop, arriving through the back door
|
would be exactly the shared-schema mistake 0045 exists to stop, arriving through the back door
|
||||||
marked *performance*.
|
marked *performance*.
|
||||||
|
|
||||||
That leaves one genuine funnel: every context's writes go through the process that owns it, and
|
That leaves one genuine funnel: every context's writes go through the process that owns it, and
|
||||||
[ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md) says there is one of it.
|
[ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md) says there is one of it.
|
||||||
For a mesh of a handful of machines this is not a scaling problem, and **it should not be solved
|
For a mesh of a handful of machines this is not a scaling problem, and **it should not be solved
|
||||||
by giving nodes database credentials** — that trades a bounded problem for an unbounded one. If
|
by giving nodes database credentials** — that trades a bounded problem for an unbounded one. If
|
||||||
it ever binds, the answers are at the consumer: batch, apply backpressure, or move the highest
|
it ever binds, the answers are at the consumer: batch, apply backpressure, or move the highest
|
||||||
@@ -170,10 +170,10 @@ volume genuinely argues against a relational store.
|
|||||||
- **Not a surface.** Tier 3 is how people and agents reach it. It has one interface; the
|
- **Not a surface.** Tier 3 is how people and agents reach it. It has one interface; the
|
||||||
surfaces are what speak to that interface.
|
surfaces are what speak to that interface.
|
||||||
- **Not the substrate.** It *runs on* tier 1 — PostgreSQL, LavinMQ, MinIO, an OCI registry
|
- **Not the substrate.** It *runs on* tier 1 — PostgreSQL, LavinMQ, MinIO, an OCI registry
|
||||||
([ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md)) — and cannot start without
|
([ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)) — and cannot start without
|
||||||
them, which is what makes them a lower tier.
|
them, which is what makes them a lower tier.
|
||||||
- **Not privileged on a node.** It has no more access to a machine than the declaration
|
- **Not privileged on a node.** It has no more access to a machine than the declaration
|
||||||
vocabulary allows ([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)).
|
vocabulary allows ([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)).
|
||||||
|
|
||||||
## It is also a consumer
|
## It is also a consumer
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ module needs, granted the same way.
|
|||||||
That is the circularity the tiers exist to resolve rather than hide: the control plane cannot
|
That is the circularity the tiers exist to resolve rather than hide: the control plane cannot
|
||||||
provision its own database, because it is not running yet. So its **store** is raised from the
|
provision its own database, because it is not running yet. So its **store** is raised from the
|
||||||
bundle the host carries, before there is a control plane to ask
|
bundle the host carries, before there is a control plane to ask
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md),
|
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md),
|
||||||
[research 011](../../01-RESEARCH/011-the-module-graph/worked-provider.md)).
|
[research 011](../../01-RESEARCH/011-the-module-graph/worked-provider.md)).
|
||||||
|
|
||||||
Its virtual host and its bucket are **not** in the bundle — by the time they are wanted there is
|
Its virtual host and its bucket are **not** in the bundle — by the time they are wanted there is
|
||||||
@@ -198,15 +198,15 @@ it.
|
|||||||
hosts, assigned to nodes by the same mechanism as everything else.
|
hosts, assigned to nodes by the same mechanism as everything else.
|
||||||
|
|
||||||
**One node runs it, and nothing takes over**
|
**One node runs it, and nothing takes over**
|
||||||
([ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md)). The node is assigned,
|
([ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)). The node is assigned,
|
||||||
never elected — no promotion, no quorum, no split brain.
|
never elected — no promotion, no quorum, no split brain.
|
||||||
|
|
||||||
That is sound rather than merely cheap, because the design already tolerates the control plane
|
That is sound rather than merely cheap, because the design already tolerates the control plane
|
||||||
being absent by construction: a node reconciles from **its own** store
|
being absent by construction: a node reconciles from **its own** store
|
||||||
([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)) and
|
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)) and
|
||||||
never needed to ask anybody to hold the state it was last given. So the control plane being down
|
never needed to ask anybody to hold the state it was last given. So the control plane being down
|
||||||
is not a new failure mode — it is
|
is not a new failure mode — it is
|
||||||
[ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)'s ordinary disconnected
|
[ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)'s ordinary disconnected
|
||||||
situation, happening to every node at once. **What is lost is change, not operation.**
|
situation, happening to every node at once. **What is lost is change, not operation.**
|
||||||
|
|
||||||
The honest half: this node is a single point of failure, recovery is restore rather than
|
The honest half: this node is a single point of failure, recovery is restore rather than
|
||||||
@@ -216,14 +216,14 @@ every public name.
|
|||||||
## Open
|
## Open
|
||||||
|
|
||||||
- ~~**The contexts themselves.**~~ **Decided** — seven, by
|
- ~~**The contexts themselves.**~~ **Decided** — seven, by
|
||||||
[ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md).
|
[ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md).
|
||||||
What remains open is narrower and named there: **where the record lives**, which research 006
|
What remains open is narrower and named there: **where the record lives**, which research 006
|
||||||
leaves unresolved because the substrate is the one place it must not go.
|
leaves unresolved because the substrate is the one place it must not go.
|
||||||
- **How far it may be split.** One deployable today. Splitting a context out costs the single
|
- **How far it may be split.** One deployable today. Splitting a context out costs the single
|
||||||
interface a surface depends on
|
interface a surface depends on
|
||||||
([research 011](../../01-RESEARCH/011-the-module-graph/00-overview.md)).
|
([research 011](../../01-RESEARCH/011-the-module-graph/00-overview.md)).
|
||||||
- ~~**How many run, and what a node does without one.**~~ **Resolved** by
|
- ~~**How many run, and what a node does without one.**~~ **Resolved** by
|
||||||
[ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md). What remains is
|
[ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md). What remains is
|
||||||
measurement: nothing reports how long the control plane has been unreachable, or how close a
|
measurement: nothing reports how long the control plane has been unreachable, or how close a
|
||||||
certificate is to expiry — both needed for restore-not-failover to be a plan rather than a
|
certificate is to expiry — both needed for restore-not-failover to be a plan rather than a
|
||||||
hope.
|
hope.
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ status: designed
|
|||||||
code: []
|
code: []
|
||||||
updated: 2026-08-27
|
updated: 2026-08-27
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0019-how-this-repository-works.md
|
- 02-DECISIONS/0011-how-this-repository-works.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0036-a-node-and-how-it-joins.md
|
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
|
||||||
- 02-DECISIONS/0048-the-substrate-and-the-control-plane.md
|
- 02-DECISIONS/0021-the-substrate-and-the-control-plane.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0048-the-substrate-and-the-control-plane.md
|
- 02-DECISIONS/0021-the-substrate-and-the-control-plane.md
|
||||||
- 02-DECISIONS/0049-connectivity.md
|
- 02-DECISIONS/0022-connectivity.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# The substrate
|
# The substrate
|
||||||
@@ -27,7 +27,7 @@ Every module that needs a database asks the control plane's provisioning for one
|
|||||||
plane needs a database too — and it cannot ask itself, because it is not running yet. That
|
plane needs a database too — and it cannot ask itself, because it is not running yet. That
|
||||||
circularity is not an awkwardness to work around; it *is* the definition. Anything on the wrong
|
circularity is not an awkwardness to work around; it *is* the definition. Anything on the wrong
|
||||||
side of it must be raised some other way, and the other way is the bundle the host carries
|
side of it must be raised some other way, and the other way is the bundle the host carries
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)).
|
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)).
|
||||||
|
|
||||||
The test, applied:
|
The test, applied:
|
||||||
|
|
||||||
@@ -38,11 +38,11 @@ The test, applied:
|
|||||||
| an object store — **MinIO** | artifacts and blobs it delivers | no — it needs a bucket to hold them | **substrate** |
|
| an object store — **MinIO** | artifacts and blobs it delivers | no — it needs a bucket to hold them | **substrate** |
|
||||||
| an image registry — **the OCI registry** | images it delivers to nodes | no — it needs a repository | **substrate** |
|
| an image registry — **the OCI registry** | images it delivers to nodes | no — it needs a repository | **substrate** |
|
||||||
| an identity provider | only if it delegates authentication | — | **conditional, below** |
|
| an identity provider | only if it delegates authentication | — | **conditional, below** |
|
||||||
| ingress — **Traefik** | not to start; only to be reached by name | — it grants itself one afterwards | **not substrate** ([ADR 0049](../../02-DECISIONS/0049-connectivity.md)) |
|
| ingress — **Traefik** | not to start; only to be reached by name | — it grants itself one afterwards | **not substrate** ([ADR 0022](../../02-DECISIONS/0022-connectivity.md)) |
|
||||||
| anything else the mesh hosts | no | — | not substrate |
|
| anything else the mesh hosts | no | — | not substrate |
|
||||||
|
|
||||||
**The role and the product are both written**, here and everywhere
|
**The role and the product are both written**, here and everywhere
|
||||||
([ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md)). The role is what the argument
|
([ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)). The role is what the argument
|
||||||
turns on — the test above works on roles, and would give the same answers for a different store.
|
turns on — the test above works on roles, and would give the same answers for a different store.
|
||||||
The product is what actually gets installed and pinned, and a design that names only the role
|
The product is what actually gets installed and pinned, and a design that names only the role
|
||||||
does not record that the choice was ever made.
|
does not record that the choice was ever made.
|
||||||
@@ -96,19 +96,19 @@ Being substrate and being in the bundle are two different questions:
|
|||||||
| PostgreSQL | yes — the control plane's own state lives in it | **yes** — there is nowhere to put that state otherwise |
|
| PostgreSQL | yes — the control plane's own state lives in it | **yes** — there is nowhere to put that state otherwise |
|
||||||
| LavinMQ | yes — it cannot grant itself a virtual host | **not established** — see below |
|
| LavinMQ | yes — it cannot grant itself a virtual host | **not established** — see below |
|
||||||
| MinIO | yes — it cannot grant itself a bucket | no — nothing is delivered before the mesh exists |
|
| MinIO | yes — it cannot grant itself a bucket | no — nothing is delivered before the mesh exists |
|
||||||
| the OCI registry | yes — it cannot grant itself a repository | no — the first node fetches upstream ([ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md)) |
|
| the OCI registry | yes — it cannot grant itself a repository | no — the first node fetches upstream ([ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)) |
|
||||||
|
|
||||||
The three on the bottom rows are **substrate by role and ordinary by delivery**: by the time
|
The three on the bottom rows are **substrate by role and ordinary by delivery**: by the time
|
||||||
they are wanted there is a control plane, and it provisions them the way it provisions anything.
|
they are wanted there is a control plane, and it provisions them the way it provisions anything.
|
||||||
That keeps the bundle to roughly one image rather than four, which is what makes it small enough
|
That keeps the bundle to roughly one image rather than four, which is what makes it small enough
|
||||||
for the review [ADR 0037](../../02-DECISIONS/0037-the-node-host.md)
|
for the review [ADR 0016](../../02-DECISIONS/0016-the-node-host.md)
|
||||||
requires.
|
requires.
|
||||||
|
|
||||||
**Why pinned:** the bundle is applied when no mesh exists, so nothing can resolve a version, ask
|
**Why pinned:** the bundle is applied when no mesh exists, so nothing can resolve a version, ask
|
||||||
a registry, or check a constraint. What the host carries must already be exact.
|
a registry, or check a constraint. What the host carries must already be exact.
|
||||||
|
|
||||||
**Why references and not payload:** the bundle names images by **digest** and the host fetches
|
**Why references and not payload:** the bundle names images by **digest** and the host fetches
|
||||||
them ([ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md)). A first node is
|
them ([ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)). A first node is
|
||||||
a real machine with a network; the sealed case is the lab, and the lab places images itself.
|
a real machine with a network; the sealed case is the lab, and the lab places images itself.
|
||||||
|
|
||||||
Reproducibility comes from pinning the identity of a thing rather than carrying its bytes, which
|
Reproducibility comes from pinning the identity of a thing rather than carrying its bytes, which
|
||||||
@@ -136,7 +136,7 @@ container, so a container runtime must be working before anything else happens
|
|||||||
is a *package*, not a container.
|
is a *package*, not a container.
|
||||||
|
|
||||||
**Which runtime is detected, not chosen**
|
**Which runtime is detected, not chosen**
|
||||||
([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)): a machine that
|
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)): a machine that
|
||||||
already has one keeps it. On a machine with none, the control plane names the package, because
|
already has one keeps it. On a machine with none, the control plane names the package, because
|
||||||
what it is called differs per system. It is:
|
what it is called differs per system. It is:
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ what it is called differs per system. It is:
|
|||||||
- **adopted rather than installed** when the machine already has one with configuration somebody
|
- **adopted rather than installed** when the machine already has one with configuration somebody
|
||||||
chose ([research 012](../../01-RESEARCH/012-the-minimum-viable-node/00-overview.md));
|
chose ([research 012](../../01-RESEARCH/012-the-minimum-viable-node/00-overview.md));
|
||||||
- a package, which needs the machine's own package manager and a network — both permitted by
|
- a package, which needs the machine's own package manager and a network — both permitted by
|
||||||
[ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md).
|
[ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md).
|
||||||
|
|
||||||
So the host's bootstrap vocabulary is six shapes: **package**, **container**, **file**,
|
So the host's bootstrap vocabulary is six shapes: **package**, **container**, **file**,
|
||||||
**directory**, **service**, and **action**. **All six are built**
|
**directory**, **service**, and **action**. **All six are built**
|
||||||
@@ -155,7 +155,7 @@ blocked on the host any longer.
|
|||||||
**Steps 2 and 3 happen before there is a mesh to do them**, which is why provisioning is part of
|
**Steps 2 and 3 happen before there is a mesh to do them**, which is why provisioning is part of
|
||||||
the bootstrap rather than a service consumers use later. They are **actions** the bundle
|
the bootstrap rather than a service consumers use later. They are **actions** the bundle
|
||||||
declares and the host runs
|
declares and the host runs
|
||||||
([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)) — so the
|
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)) — so the
|
||||||
host's vocabulary grows by one shape rather than by one resource type per substrate service.
|
host's vocabulary grows by one shape rather than by one resource type per substrate service.
|
||||||
|
|
||||||
## Open
|
## Open
|
||||||
@@ -170,7 +170,7 @@ host's vocabulary grows by one shape rather than by one resource type per substr
|
|||||||
question about the control plane's internal shape, not about the substrate**, which is why it is
|
question about the control plane's internal shape, not about the substrate**, which is why it is
|
||||||
not answered here.
|
not answered here.
|
||||||
- ~~**Whether the host can do step 2.**~~ **Resolved** by
|
- ~~**Whether the host can do step 2.**~~ **Resolved** by
|
||||||
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md). A service
|
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md). A service
|
||||||
running on this machine is part of this machine, so the scope was never in question — the real
|
running on this machine is part of this machine, so the scope was never in question — the real
|
||||||
question was whether the host must learn what a database is, and it must not. The bundle
|
question was whether the host must learn what a database is, and it must not. The bundle
|
||||||
declares an **action**; the host runs it and verifies it, and what a database means stays with
|
declares an **action**; the host runs it and verifies it, and what a database means stays with
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ status: designed
|
|||||||
code: []
|
code: []
|
||||||
updated: 2026-08-27
|
updated: 2026-08-27
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0036-a-node-and-how-it-joins.md
|
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
|
||||||
- 02-DECISIONS/0049-connectivity.md
|
- 02-DECISIONS/0022-connectivity.md
|
||||||
- 02-DECISIONS/0049-connectivity.md
|
- 02-DECISIONS/0022-connectivity.md
|
||||||
- 02-DECISIONS/0036-a-node-and-how-it-joins.md
|
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
|
||||||
- 02-DECISIONS/0049-connectivity.md
|
- 02-DECISIONS/0022-connectivity.md
|
||||||
- 02-DECISIONS/0048-the-substrate-and-the-control-plane.md
|
- 02-DECISIONS/0021-the-substrate-and-the-control-plane.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# Connectivity
|
# Connectivity
|
||||||
@@ -31,7 +31,7 @@ node* — to each responsibility:
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **overlay** — who peers with whom, at what address | **every node**, and which of them can be dialled | control plane |
|
| **overlay** — who peers with whom, at what address | **every node**, and which of them can be dialled | control plane |
|
||||||
| **resolution** — which name is which node | **every node** | control plane |
|
| **resolution** — which name is which node | **every node** | control plane |
|
||||||
| **exposure** — which public name reaches which container | **which node is publicly reachable** ([ADR 0049](../../02-DECISIONS/0049-connectivity.md)) | control plane |
|
| **exposure** — which public name reaches which container | **which node is publicly reachable** ([ADR 0022](../../02-DECISIONS/0022-connectivity.md)) | control plane |
|
||||||
| **filtering** — which port is open, to whom | what is assigned here, and the overlay's shape | control plane decides, host applies |
|
| **filtering** — which port is open, to whom | what is assigned here, and the overlay's shape | control plane decides, host applies |
|
||||||
| **certificates** — who may present which name | which name belongs to which node | control plane |
|
| **certificates** — who may present which name | which name belongs to which node | control plane |
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ It is also what removes the last two upward dependencies.
|
|||||||
[Research 006](../../01-RESEARCH/006-mesh-from-scratch/host-size.md) counted exactly two modules
|
[Research 006](../../01-RESEARCH/006-mesh-from-scratch/host-size.md) counted exactly two modules
|
||||||
opening a direct connection to the control plane's database — `wireguard` and `traefik` — and
|
opening a direct connection to the control plane's database — `wireguard` and `traefik` — and
|
||||||
they are the reason every node permanently holds a credential to it
|
they are the reason every node permanently holds a credential to it
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)). Both are connectivity
|
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)). Both are connectivity
|
||||||
modules. **Closing this context closes that set.**
|
modules. **Closing this context closes that set.**
|
||||||
|
|
||||||
## The order it comes up in
|
## The order it comes up in
|
||||||
@@ -63,7 +63,7 @@ The one thing to get right, because everything else depends on it:
|
|||||||
```
|
```
|
||||||
0 the node has an underlay address the machine's own — DHCP, or a provider gave it one
|
0 the node has an underlay address the machine's own — DHCP, or a provider gave it one
|
||||||
1 the node dials the mesh OVER THE UNDERLAY, at the address in its token
|
1 the node dials the mesh OVER THE UNDERLAY, at the address in its token
|
||||||
2 it proves itself, and is proved to the link exists (ADR 0039, ADR 0051)
|
2 it proves itself, and is proved to the link exists (ADR 0015, ADR 0015)
|
||||||
3 the mesh grants it an identity and an overlay address
|
3 the mesh grants it an identity and an overlay address
|
||||||
4 the overlay comes up peer graph delivered as files
|
4 the overlay comes up peer graph delivered as files
|
||||||
5 names resolve resolver config delivered as files
|
5 names resolve resolver config delivered as files
|
||||||
@@ -77,7 +77,7 @@ never be established on a new node. The link stays on the underlay permanently
|
|||||||
outbound-only and carries its own identity, so it needs nothing the overlay provides.
|
outbound-only and carries its own identity, so it needs nothing the overlay provides.
|
||||||
|
|
||||||
**Nothing before step 3 can resolve a mesh name**, which is why the token carries an *address*
|
**Nothing before step 3 can resolve a mesh name**, which is why the token carries an *address*
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)). Today this is
|
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)). Today this is
|
||||||
patched with an `/etc/hosts` floor written underneath the resolver; under this design there is
|
patched with an `/etc/hosts` floor written underneath the resolver; under this design there is
|
||||||
nothing to patch.
|
nothing to patch.
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ which of those it may dial, and which must dial it.
|
|||||||
**Inputs, all declared:**
|
**Inputs, all declared:**
|
||||||
|
|
||||||
- **reachability** — an endpoint, or none
|
- **reachability** — an endpoint, or none
|
||||||
([ADR 0049](../../02-DECISIONS/0049-connectivity.md)). Not
|
([ADR 0022](../../02-DECISIONS/0022-connectivity.md)). Not
|
||||||
inferred from the address shape, which is wrong for carrier-grade NAT, wrong for IPv6, and
|
inferred from the address shape, which is wrong for carrier-grade NAT, wrong for IPv6, and
|
||||||
wrong for a routable address behind a closed firewall.
|
wrong for a routable address behind a closed firewall.
|
||||||
- **site** — where the machine physically is, or nothing if it roams.
|
- **site** — where the machine physically is, or nothing if it roams.
|
||||||
@@ -98,7 +98,7 @@ which of those it may dial, and which must dial it.
|
|||||||
|
|
||||||
**Keys.** Each node generates its own keypair. **The private key never leaves the machine**; the
|
**Keys.** Each node generates its own keypair. **The private key never leaves the machine**; the
|
||||||
public key is published to the mesh. This is already true and it is already right — it is
|
public key is published to the mesh. This is already true and it is already right — it is
|
||||||
[ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)'s *a node holds its own
|
[ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)'s *a node holds its own
|
||||||
identity* applied to the overlay, and it means the control plane computes a graph it cannot
|
identity* applied to the overlay, and it means the control plane computes a graph it cannot
|
||||||
itself impersonate.
|
itself impersonate.
|
||||||
|
|
||||||
@@ -141,17 +141,17 @@ expensively enough to be worth restating:
|
|||||||
name and overlay address.
|
name and overlay address.
|
||||||
|
|
||||||
**What goes away:** the `/etc/hosts` floor. It exists because a node had to reach the mesh
|
**What goes away:** the `/etc/hosts` floor. It exists because a node had to reach the mesh
|
||||||
database before its own DNS existed; with [ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)
|
database before its own DNS existed; with [ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)
|
||||||
nothing needs a name before the link, and a fallback nothing needs is a path nothing tests.
|
nothing needs a name before the link, and a fallback nothing needs is a path nothing tests.
|
||||||
|
|
||||||
## 3 — Exposure
|
## 3 — Exposure
|
||||||
|
|
||||||
Settled by [ADR 0049](../../02-DECISIONS/0049-connectivity.md); summarised here because
|
Settled by [ADR 0022](../../02-DECISIONS/0022-connectivity.md); summarised here because
|
||||||
this is where it belongs.
|
this is where it belongs.
|
||||||
|
|
||||||
**A route is a grant.** A module that must be reachable declares it needs one; the proxy provides
|
**A route is a grant.** A module that must be reachable declares it needs one; the proxy provides
|
||||||
it and hands back the public name. Ordinary
|
it and hands back the public name. Ordinary
|
||||||
[ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md)
|
[ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md)
|
||||||
vocabulary — the mirror of a database grant, where the consumer supplies a target and receives a
|
vocabulary — the mirror of a database grant, where the consumer supplies a target and receives a
|
||||||
name rather than supplying nothing and receiving credentials.
|
name rather than supplying nothing and receiving credentials.
|
||||||
|
|
||||||
@@ -164,14 +164,14 @@ the case is a mesh-level fact, which is the fourth reason exposure is control-pl
|
|||||||
consequence of what runs on it and who must reach it, not an independent declaration to keep in
|
consequence of what runs on it and who must reach it, not an independent declaration to keep in
|
||||||
step by hand.
|
step by hand.
|
||||||
|
|
||||||
**A rule names its source** ([ADR 0049](../../02-DECISIONS/0049-connectivity.md)).
|
**A rule names its source** ([ADR 0022](../../02-DECISIONS/0022-connectivity.md)).
|
||||||
A rule with no source is open, and must say so rather than appear to restrict something. `scope:`
|
A rule with no source is open, and must say so rather than appear to restrict something. `scope:`
|
||||||
is removed rather than implemented: five manifests carry it today, it is referenced by no code,
|
is removed rather than implemented: five manifests carry it today, it is referenced by no code,
|
||||||
and it is the clearest instance in the repository of *an unenforced rule is indistinguishable
|
and it is the clearest instance in the repository of *an unenforced rule is indistinguishable
|
||||||
from a wrong one, and costs more, because people believe it.*
|
from a wrong one, and costs more, because people believe it.*
|
||||||
|
|
||||||
**Unknown keys are refused** — the discipline the host's declaration parser already has
|
**Unknown keys are refused** — the discipline the host's declaration parser already has
|
||||||
([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)), and
|
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)), and
|
||||||
the one manifests lack. `scope:` survived because nothing rejected it.
|
the one manifests lack. `scope:` survived because nothing rejected it.
|
||||||
|
|
||||||
## 5 — Certificates
|
## 5 — Certificates
|
||||||
@@ -197,7 +197,7 @@ worse than the lab problem that found it — every certificate experiment on a r
|
|||||||
production issuance quota, and a retry loop can exhaust it for a week.
|
production issuance quota, and a retry loop can exhaust it for a week.
|
||||||
|
|
||||||
**The mesh CA is not a bootstrap concern.** A joining node verifies the control plane against the
|
**The mesh CA is not a bootstrap concern.** A joining node verifies the control plane against the
|
||||||
fingerprint in its token ([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)),
|
fingerprint in its token ([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)),
|
||||||
so nothing needs the CA before membership. It certifies internal names afterwards, and that is
|
so nothing needs the CA before membership. It certifies internal names afterwards, and that is
|
||||||
all it does.
|
all it does.
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@ The list is worth having in one place, because it is most of the argument:
|
|||||||
- **The last two direct database connections from nodes** — `wireguard` and `traefik`, the only
|
- **The last two direct database connections from nodes** — `wireguard` and `traefik`, the only
|
||||||
two, both connectivity.
|
two, both connectivity.
|
||||||
- **Therefore the database credential on every node**, and the object-store credential beside it.
|
- **Therefore the database credential on every node**, and the object-store credential beside it.
|
||||||
[ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)'s central claim becomes
|
[ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)'s central claim becomes
|
||||||
true rather than aspirational.
|
true rather than aspirational.
|
||||||
- **The `/etc/hosts` floor**, and the bootstrap circularity it patched.
|
- **The `/etc/hosts` floor**, and the bootstrap circularity it patched.
|
||||||
- **Hub election by address prefix**, and the silent no-hub failure when nobody knew the
|
- **Hub election by address prefix**, and the silent no-hub failure when nobody knew the
|
||||||
@@ -219,7 +219,7 @@ The list is worth having in one place, because it is most of the argument:
|
|||||||
## Open
|
## Open
|
||||||
|
|
||||||
- ~~**What happens when the hub is down.**~~ **Resolved** by
|
- ~~**What happens when the hub is down.**~~ **Resolved** by
|
||||||
[ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md), together with `06`'s
|
[ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md), together with `06`'s
|
||||||
matching question — they were one question. Nothing takes over. WireGuard has no failover, the
|
matching question — they were one question. Nothing takes over. WireGuard has no failover, the
|
||||||
hub is declared rather than elected, and non-co-located paths stop while co-located direct peers
|
hub is declared rather than elected, and non-co-located paths stop while co-located direct peers
|
||||||
and every already-assigned workload keep running. The recovery path is restore, and its deadline
|
and every already-assigned workload keep running. The recovery path is restore, and its deadline
|
||||||
@@ -227,9 +227,9 @@ The list is worth having in one place, because it is most of the argument:
|
|||||||
- **Renumbering the overlay.** Made *possible* by declaring the hub rather than inferring it from
|
- **Renumbering the overlay.** Made *possible* by declaring the hub rather than inferring it from
|
||||||
an address, but no procedure exists, and a graph delivered node by node has an ordering problem
|
an address, but no procedure exists, and a graph delivered node by node has an ordering problem
|
||||||
while it is half-applied.
|
while it is half-applied.
|
||||||
- **Revoking a route** when a module is unassigned ([ADR 0049](../../02-DECISIONS/0049-connectivity.md)).
|
- **Revoking a route** when a module is unassigned ([ADR 0022](../../02-DECISIONS/0022-connectivity.md)).
|
||||||
A stale public name pointing at nothing fails more visibly than a stale grant.
|
A stale public name pointing at nothing fails more visibly than a stale grant.
|
||||||
- **IPv6.** [ADR 0049](../../02-DECISIONS/0049-connectivity.md) makes
|
- **IPv6.** [ADR 0022](../../02-DECISIONS/0022-connectivity.md) makes
|
||||||
it expressible; nothing here says the overlay or the resolver handle it.
|
it expressible; nothing here says the overlay or the resolver handle it.
|
||||||
- **Reporting declared-versus-observed.** ADR 0050 makes the disagreement detectable and does not
|
- **Reporting declared-versus-observed.** ADR 0022 makes the disagreement detectable and does not
|
||||||
say who looks or what they are told.
|
say who looks or what they are told.
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ status: designed
|
|||||||
code: []
|
code: []
|
||||||
updated: 2026-08-27
|
updated: 2026-08-27
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0036-a-node-and-how-it-joins.md
|
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0036-a-node-and-how-it-joins.md
|
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
|
||||||
- 02-DECISIONS/0036-a-node-and-how-it-joins.md
|
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0036-a-node-and-how-it-joins.md
|
- 02-DECISIONS/0015-a-node-and-how-it-joins.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
- 02-DECISIONS/0037-the-node-host.md
|
- 02-DECISIONS/0016-the-node-host.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# The node lifecycle
|
# The node lifecycle
|
||||||
@@ -42,11 +42,11 @@ questions that were not being asked live.
|
|||||||
|
|
||||||
**Only `enrolled` and `disconnected` are nodes**, and they are the same node in two situations
|
**Only `enrolled` and `disconnected` are nodes**, and they are the same node in two situations
|
||||||
rather than two kinds of thing
|
rather than two kinds of thing
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)). **`hosted` is not a
|
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)). **`hosted` is not a
|
||||||
node** — it is a machine with a program on it that has not been told which mesh it belongs to.
|
node** — it is a machine with a program on it that has not been told which mesh it belongs to.
|
||||||
|
|
||||||
There is no state for *the first node*. That is the point of
|
There is no state for *the first node*. That is the point of
|
||||||
[ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md): the first node walks the
|
[ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md): the first node walks the
|
||||||
same path, in an unusual order.
|
same path, in an unusual order.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -54,7 +54,7 @@ same path, in an unusual order.
|
|||||||
## unmanaged → hosted: installing
|
## unmanaged → hosted: installing
|
||||||
|
|
||||||
In the machine's own idiom, because the package manager and the init file are the system's
|
In the machine's own idiom, because the package manager and the init file are the system's
|
||||||
([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)):
|
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)):
|
||||||
|
|
||||||
```
|
```
|
||||||
# Alpine — the intended first node
|
# Alpine — the intended first node
|
||||||
@@ -67,7 +67,7 @@ systemctl enable --now nox-mesh-host
|
|||||||
```
|
```
|
||||||
|
|
||||||
Two lines each, and the init file behind them is four
|
Two lines each, and the init file behind them is four
|
||||||
([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)) — it says
|
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)) — it says
|
||||||
*run the launcher at boot* and nothing else, so a third system is transcription rather than a
|
*run the launcher at boot* and nothing else, so a third system is transcription rather than a
|
||||||
port.
|
port.
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ WantedBy=multi-user.target
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Two lines of policy, and that is deliberate**
|
**Two lines of policy, and that is deliberate**
|
||||||
([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)). The init is
|
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)). The init is
|
||||||
asked to *start this at boot* and *start it again if it exits*, and nothing else. Both are
|
asked to *start this at boot* and *start it again if it exits*, and nothing else. Both are
|
||||||
expressible in OpenRC, runit, s6 and an Android `init.rc`, so porting this file is transcription
|
expressible in OpenRC, runit, s6 and an Android `init.rc`, so porting this file is transcription
|
||||||
rather than design.
|
rather than design.
|
||||||
@@ -135,7 +135,7 @@ nox-mesh-host enrol --token <one-time token>
|
|||||||
```
|
```
|
||||||
|
|
||||||
The token carries three things and is carried by a person
|
The token carries three things and is carried by a person
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)): the broker's
|
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)): the broker's
|
||||||
address, the fingerprint to expect, and the right to join once.
|
address, the fingerprint to expect, and the right to join once.
|
||||||
|
|
||||||
What happens, in order:
|
What happens, in order:
|
||||||
@@ -153,7 +153,7 @@ a container runtime, an architecture. The profile is not a diagnostic; it is the
|
|||||||
|
|
||||||
**The node computes nothing about the mesh.** It needs one peer to reach; the whole overlay is
|
**The node computes nothing about the mesh.** It needs one peer to reach; the whole overlay is
|
||||||
derived centrally and pushed down
|
derived centrally and pushed down
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md),
|
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md),
|
||||||
[`08-connectivity.md`](08-connectivity.md)).
|
[`08-connectivity.md`](08-connectivity.md)).
|
||||||
|
|
||||||
### The first declaration is the overlay, and nothing else
|
### The first declaration is the overlay, and nothing else
|
||||||
@@ -172,7 +172,7 @@ Three reasons, and the third is the one that matters when something goes wrong:
|
|||||||
address and peer set are *assigned* — it generates a keypair, publishes the public half, and
|
address and peer set are *assigned* — it generates a keypair, publishes the public half, and
|
||||||
receives the rest ([`08-connectivity.md`](08-connectivity.md)). So the overlay is the first
|
receives the rest ([`08-connectivity.md`](08-connectivity.md)). So the overlay is the first
|
||||||
thing the mesh can give it, and it should be.
|
thing the mesh can give it, and it should be.
|
||||||
- **It is what [ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md) already
|
- **It is what [ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md) already
|
||||||
says:** *a joining node does the minimum to be reachable, and nothing else.*
|
says:** *a joining node does the minimum to be reachable, and nothing else.*
|
||||||
- **It is the way back in.** Once the overlay is up, the node is reachable over it — by SSH, by
|
- **It is the way back in.** Once the overlay is up, the node is reachable over it — by SSH, by
|
||||||
anything. If a later declaration breaks the machine, there is a route to it that does not
|
anything. If a later declaration breaks the machine, there is a route to it that does not
|
||||||
@@ -188,9 +188,9 @@ Worth stating plainly, because the two rules read as a contradiction and are not
|
|||||||
|---|---|
|
|---|---|
|
||||||
| **every node reaches every other node** | over the overlay — SSH, services, ordinary traffic. This is the point of having one |
|
| **every node reaches every other node** | over the overlay — SSH, services, ordinary traffic. This is the point of having one |
|
||||||
| **every node consumes from the broker** | its own queue, over its own outbound connection ([ADR 0001](../../02-DECISIONS/0001-nodes-communicate-over-a-broker.md)) |
|
| **every node consumes from the broker** | its own queue, over its own outbound connection ([ADR 0001](../../02-DECISIONS/0001-nodes-communicate-over-a-broker.md)) |
|
||||||
| **nothing dials a node to control it** | the host has no inbound control surface ([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)) |
|
| **nothing dials a node to control it** | the host has no inbound control surface ([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)) |
|
||||||
|
|
||||||
**[ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md) is about the control
|
**[ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md) is about the control
|
||||||
channel, not about network reachability.** What it forbids is a listening thing that accepts
|
channel, not about network reachability.** What it forbids is a listening thing that accepts
|
||||||
instructions and changes the machine. A node being reachable on the overlay — the whole purpose
|
instructions and changes the machine. A node being reachable on the overlay — the whole purpose
|
||||||
of the overlay — is untouched by it, and so is a person opening a shell on it.
|
of the overlay — is untouched by it, and so is a person opening a shell on it.
|
||||||
@@ -232,7 +232,7 @@ used months later on node two.
|
|||||||
## Two kinds of host
|
## Two kinds of host
|
||||||
|
|
||||||
Everything above assumes a machine with an init that runs the host at boot. Not every machine
|
Everything above assumes a machine with an init that runs the host at boot. Not every machine
|
||||||
has one ([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)).
|
has one ([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)).
|
||||||
|
|
||||||
| | **resident** | **episodic** |
|
| | **resident** | **episodic** |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -245,7 +245,7 @@ has one ([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)).
|
|||||||
| can be the first node | yes | **no** |
|
| can be the first node | yes | **no** |
|
||||||
|
|
||||||
**An episodic host being killed is disconnection, not failure.** That is
|
**An episodic host being killed is disconnection, not failure.** That is
|
||||||
[ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md) doing the work it was written
|
[ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md) doing the work it was written
|
||||||
for: reachability is state, not class. Everything the design already does for a laptop that
|
for: reachability is state, not class. Everything the design already does for a laptop that
|
||||||
closes — an authoritative local store, reconcile on start, *last heard from* reported without an
|
closes — an authoritative local store, reconcile on start, *last heard from* reported without an
|
||||||
alarm — is what an episodic host needs, at a shorter period.
|
alarm — is what an episodic host needs, at a shorter period.
|
||||||
@@ -258,7 +258,7 @@ empty placeholder waiting to be filled in.
|
|||||||
**Two things this changes for anything reading the mesh.** *Last heard from* is a much weaker
|
**Two things this changes for anything reading the mesh.** *Last heard from* is a much weaker
|
||||||
signal on an episodic host — a healthy phone looks like a dead server — so a reader has to know
|
signal on an episodic host — a healthy phone looks like a dead server — so a reader has to know
|
||||||
which kind it is looking at. And a declaration may take a long time to land, which makes
|
which kind it is looking at. And a declaration may take a long time to land, which makes
|
||||||
[ADR 0058](../../02-DECISIONS/0058-delivery.md)'s separation of
|
[ADR 0023](../../02-DECISIONS/0023-delivery.md)'s separation of
|
||||||
*outstanding* from *failed* load-bearing rather than tidy.
|
*outstanding* from *failed* load-bearing rather than tidy.
|
||||||
|
|
||||||
**Still open:** how an episodic host is started in practice — an APK with a foreground service,
|
**Still open:** how an episodic host is started in practice — an APK with a foreground service,
|
||||||
@@ -272,7 +272,7 @@ Adoption is not a state. It is what the **first apply** does when it is told to
|
|||||||
machine already has ([research 012](../../01-RESEARCH/012-the-minimum-viable-node/00-overview.md)).
|
machine already has ([research 012](../../01-RESEARCH/012-the-minimum-viable-node/00-overview.md)).
|
||||||
|
|
||||||
A candidate machine is not empty. It has a package manager, probably a container runtime,
|
A candidate machine is not empty. It has a package manager, probably a container runtime,
|
||||||
configuration somebody chose. [ADR 0037](../../02-DECISIONS/0037-the-node-host.md)
|
configuration somebody chose. [ADR 0016](../../02-DECISIONS/0016-the-node-host.md)
|
||||||
says the host never touches what it did not create — adoption is the deliberate act of taking
|
says the host never touches what it did not create — adoption is the deliberate act of taking
|
||||||
ownership of exactly that, so it is a companion to that rule rather than an exception:
|
ownership of exactly that, so it is a companion to that rule rather than an exception:
|
||||||
|
|
||||||
@@ -299,7 +299,7 @@ outcome **derived** from the worst line rather than stated alongside it.
|
|||||||
**Changes are pushed, not polled.** A declaration arrives as a message on the link and the host
|
**Changes are pushed, not polled.** A declaration arrives as a message on the link and the host
|
||||||
applies it then. The link is already open and outbound
|
applies it then. The link is already open and outbound
|
||||||
([ADR 0001](../../02-DECISIONS/0001-nodes-communicate-over-a-broker.md),
|
([ADR 0001](../../02-DECISIONS/0001-nodes-communicate-over-a-broker.md),
|
||||||
[ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)) — asking it
|
[ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)) — asking it
|
||||||
repeatedly whether anything has changed would be slower to land *and* constant traffic to learn
|
repeatedly whether anything has changed would be slower to land *and* constant traffic to learn
|
||||||
nothing.
|
nothing.
|
||||||
|
|
||||||
@@ -326,11 +326,11 @@ So the two periodic things do different jobs and should not be conflated:
|
|||||||
without a heartbeat that is indistinguishable from a node that stopped. With one, *last heard
|
without a heartbeat that is indistinguishable from a node that stopped. With one, *last heard
|
||||||
from* is a fact beside every node — which is what
|
from* is a fact beside every node — which is what
|
||||||
[how long disconnected](#how-long-disconnected-and-who-is-told) reports and what
|
[how long disconnected](#how-long-disconnected-and-who-is-told) reports and what
|
||||||
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md) exists
|
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md) exists
|
||||||
because a stuck node cannot send.
|
because a stuck node cannot send.
|
||||||
|
|
||||||
**Rebooting mid-apply is safe by construction.** The store records each resource *after* it
|
**Rebooting mid-apply is safe by construction.** The store records each resource *after* it
|
||||||
worked ([ADR 0035](../../02-DECISIONS/0035-a-picture-is-read-from-what-runs.md)), so a host that
|
worked ([ADR 0014](../../02-DECISIONS/0014-a-picture-is-read-from-what-runs.md)), so a host that
|
||||||
dies half way through comes back, finds the completed ones already matching, and applies the
|
dies half way through comes back, finds the completed ones already matching, and applies the
|
||||||
rest. The rule that exists to stop the host lying about what it did also makes it crash-safe.
|
rest. The rule that exists to stop the host lying about what it did also makes it crash-safe.
|
||||||
|
|
||||||
@@ -357,7 +357,7 @@ runtime because a declaration changed would stop every container on the node.
|
|||||||
## enrolled ⇄ disconnected
|
## enrolled ⇄ disconnected
|
||||||
|
|
||||||
Not a failure. Not degraded. A situation
|
Not a failure. Not degraded. A situation
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)).
|
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)).
|
||||||
|
|
||||||
A disconnected node **keeps reconciling against its own store**, so it goes on holding its
|
A disconnected node **keeps reconciling against its own store**, so it goes on holding its
|
||||||
machine in the last state it was told to hold. A laptop shut for a week comes back and
|
machine in the last state it was told to hold. A laptop shut for a week comes back and
|
||||||
@@ -365,7 +365,7 @@ reconciles; it does not come back and ask what it is.
|
|||||||
|
|
||||||
What it cannot do: receive new declarations, be granted anything new, or have its certificates
|
What it cannot do: receive new declarations, be granted anything new, or have its certificates
|
||||||
renewed — which is the clock on the whole arrangement
|
renewed — which is the clock on the whole arrangement
|
||||||
([ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md)).
|
([ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)).
|
||||||
|
|
||||||
**How long it has been disconnected is a fact the mesh must hold**, and nothing holds it today.
|
**How long it has been disconnected is a fact the mesh must hold**, and nothing holds it today.
|
||||||
Without it, a node running last month's assignments looks exactly like one that is current.
|
Without it, a node running last month's assignments looks exactly like one that is current.
|
||||||
@@ -384,7 +384,7 @@ nox-mesh-host profile # what can this machine actually do?
|
|||||||
|
|
||||||
`apply FILE` accepts actions, because someone who can write that file and run this binary as
|
`apply FILE` accepts actions, because someone who can write that file and run this binary as
|
||||||
root can already do anything it can. The bound in
|
root can already do anything it can. The bound in
|
||||||
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md) is on what a
|
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md) is on what a
|
||||||
**remote** party may push, not on what a person at the machine may do.
|
**remote** party may push, not on what a person at the machine may do.
|
||||||
|
|
||||||
This replaces the three hand-run scripts that exist today — first node, joining, rescue — with
|
This replaces the three hand-run scripts that exist today — first node, joining, rescue — with
|
||||||
@@ -401,14 +401,14 @@ what it owns by the table above, reports, and drops its identity. The machine ke
|
|||||||
installed and is back to `hosted`. Nothing is left behind that anybody has to remember.
|
installed and is back to `hosted`. Nothing is left behind that anybody has to remember.
|
||||||
|
|
||||||
**The node is gone.** Stolen, dead, or simply unreachable. The mesh cannot tell it anything, and
|
**The node is gone.** Stolen, dead, or simply unreachable. The mesh cannot tell it anything, and
|
||||||
by [ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md) it will go on reconciling
|
by [ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md) it will go on reconciling
|
||||||
its last declaration **forever**.
|
its last declaration **forever**.
|
||||||
|
|
||||||
That is the honest consequence of making disconnection ordinary, and the answer is not to make
|
That is the honest consequence of making disconnection ordinary, and the answer is not to make
|
||||||
the host expire. It is that **the node holds nothing that outlives revocation**: its identity is
|
the host expire. It is that **the node holds nothing that outlives revocation**: its identity is
|
||||||
its own, and every grant it holds is a per-node credential at the provider
|
its own, and every grant it holds is a per-node credential at the provider
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md),
|
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md),
|
||||||
[ADR 0045](../../02-DECISIONS/0045-a-context-owns-its-store.md)). Revoking is done at the
|
[ADR 0020](../../02-DECISIONS/0020-a-context-owns-its-store.md)). Revoking is done at the
|
||||||
database, the broker, the object store — not on the machine.
|
database, the broker, the object store — not on the machine.
|
||||||
|
|
||||||
So a lost node keeps *running* and stops being able to *reach* anything. That is the best
|
So a lost node keeps *running* and stops being able to *reach* anything. That is the best
|
||||||
@@ -438,7 +438,7 @@ remains locally authoritative for *operating*; the copy exists only for this.
|
|||||||
## Upgrading the host
|
## Upgrading the host
|
||||||
|
|
||||||
The host is delivered like anything else
|
The host is delivered like anything else
|
||||||
([ADR 0058](../../02-DECISIONS/0058-delivery.md)), and this is worth
|
([ADR 0023](../../02-DECISIONS/0023-delivery.md)), and this is worth
|
||||||
walking through because tier 0 looks like it should be special and is not.
|
walking through because tier 0 looks like it should be special and is not.
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -471,7 +471,7 @@ the test of whether this is really uniform.
|
|||||||
3 it finishes the apply and reports never mid-way
|
3 it finishes the apply and reports never mid-way
|
||||||
4 it exits 0 having finished, not having been stopped
|
4 it exits 0 having finished, not having been stopped
|
||||||
5 the launcher starts it again on the new binary — it supervises the host
|
5 the launcher starts it again on the new binary — it supervises the host
|
||||||
rather than exec'ing it (ADR 0061), so this
|
rather than exec'ing it (ADR 0016), so this
|
||||||
needs nothing from the init
|
needs nothing from the init
|
||||||
6 the new host reconciles on start trigger 1, confirming the machine still matches
|
6 the new host reconciles on start trigger 1, confirming the machine still matches
|
||||||
```
|
```
|
||||||
@@ -489,7 +489,7 @@ own apply completes. A node must therefore report the version it is **running**,
|
|||||||
installed — otherwise the mesh believes an upgrade landed at step 1.
|
installed — otherwise the mesh believes an upgrade landed at step 1.
|
||||||
|
|
||||||
**A version that crashes on start rolls itself back**
|
**A version that crashes on start rolls itself back**
|
||||||
([ADR 0037](../../02-DECISIONS/0037-the-node-host.md)).
|
([ADR 0016](../../02-DECISIONS/0016-the-node-host.md)).
|
||||||
|
|
||||||
What the init starts is not the host but a **launcher**, and the launcher is where the policy
|
What the init starts is not the host but a **launcher**, and the launcher is where the policy
|
||||||
lives:
|
lives:
|
||||||
@@ -551,7 +551,7 @@ credentials still valid — the case
|
|||||||
**The host reports what it owns, and the mesh keeps the last report.**
|
**The host reports what it owns, and the mesh keeps the last report.**
|
||||||
|
|
||||||
The store stays locally authoritative — a node operates from its own copy and needs nothing to
|
The store stays locally authoritative — a node operates from its own copy and needs nothing to
|
||||||
do so ([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)). What changes is that
|
do so ([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)). What changes is that
|
||||||
the mesh holds a **copy for recovery**, refreshed on every apply report.
|
the mesh holds a **copy for recovery**, refreshed on every apply report.
|
||||||
|
|
||||||
So a node that loses its state file re-enrols, receives both the declaration *and* the record of
|
So a node that loses its state file re-enrols, receives both the declaration *and* the record of
|
||||||
@@ -616,12 +616,12 @@ keeps cataloguing.
|
|||||||
### Where the enrolment token comes from
|
### Where the enrolment token comes from
|
||||||
|
|
||||||
**`mesh-control token issue` prints it once**, to the person running it. Single-use, and it
|
**`mesh-control token issue` prints it once**, to the person running it. Single-use, and it
|
||||||
expires whether used or not ([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)).
|
expires whether used or not ([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)).
|
||||||
|
|
||||||
It is carried by hand — read off a screen, pasted into a terminal. That is the design rather than
|
It is carried by hand — read off a screen, pasted into a terminal. That is the design rather than
|
||||||
a gap in it: its authenticity comes from the channel it travelled, which is what
|
a gap in it: its authenticity comes from the channel it travelled, which is what
|
||||||
lets a node verify a mesh it has never spoken to
|
lets a node verify a mesh it has never spoken to
|
||||||
([ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md)). A token emailed,
|
([ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md)). A token emailed,
|
||||||
committed, or dropped in shared storage has lost the only property that makes it worth carrying.
|
committed, or dropped in shared storage has lost the only property that makes it worth carrying.
|
||||||
|
|
||||||
**On the first node it comes from the control plane that was raised two commands ago**, which is
|
**On the first node it comes from the control plane that was raised two commands ago**, which is
|
||||||
@@ -632,10 +632,10 @@ the same command against a mesh that is one machine old.
|
|||||||
## Still open
|
## Still open
|
||||||
|
|
||||||
- ~~**Automatic rollback of a bad host version.**~~ **Resolved** by
|
- ~~**Automatic rollback of a bad host version.**~~ **Resolved** by
|
||||||
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md): a launcher
|
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md): a launcher
|
||||||
counts failed starts and rolls back — shipped by the package, not the host binary, because a
|
counts failed starts and rolls back — shipped by the package, not the host binary, because a
|
||||||
binary that will not start cannot recover itself. It rolls back once; a second failure means
|
binary that will not start cannot recover itself. It rolls back once; a second failure means
|
||||||
the machine is the problem, not the binary.
|
the machine is the problem, not the binary.
|
||||||
- **How a previous declaration is retained and chosen**, which is what rollback of anything else
|
- **How a previous declaration is retained and chosen**, which is what rollback of anything else
|
||||||
would use ([ADR 0058](../../02-DECISIONS/0058-delivery.md)).
|
would use ([ADR 0023](../../02-DECISIONS/0023-delivery.md)).
|
||||||
- **A node returning after months** applies a very large jump in one go. Correct, and untested.
|
- **A node returning after months** applies a very large jump in one go. Correct, and untested.
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ status: designed
|
|||||||
code: []
|
code: []
|
||||||
updated: 2026-08-28
|
updated: 2026-08-28
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
- 02-DECISIONS/0058-delivery.md
|
- 02-DECISIONS/0023-delivery.md
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
- 02-DECISIONS/0044-modules-and-the-graph.md
|
- 02-DECISIONS/0019-modules-and-the-graph.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# Modules and delivery
|
# Modules and delivery
|
||||||
|
|||||||
@@ -9,27 +9,27 @@ document is written and this one's status becomes `implemented`.
|
|||||||
|
|
||||||
| Document | Covers | Rests on |
|
| Document | Covers | Rests on |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| [`00-work-breakdown.md`](00-work-breakdown.md) | How the decomposition gets built, in what order, and where a human must look | [ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md) |
|
| [`00-work-breakdown.md`](00-work-breakdown.md) | How the decomposition gets built, in what order, and where a human must look | [ADR 0008](../../02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md) |
|
||||||
| [`01-end-to-end-testing.md`](01-end-to-end-testing.md) | The lab: a real mesh a change can be run against before it reaches nodes | [ADR 0016](../../02-DECISIONS/0016-the-lab.md), [0029](../../02-DECISIONS/0016-the-lab.md) |
|
| [`01-end-to-end-testing.md`](01-end-to-end-testing.md) | The lab: a real mesh a change can be run against before it reaches nodes | [ADR 0009](../../02-DECISIONS/0009-the-lab.md), [0029](../../02-DECISIONS/0009-the-lab.md) |
|
||||||
| [`02-scenario-declaration.md`](02-scenario-declaration.md) | What a scenario declares — the underlay, and what to place on it | [ADR 0016](../../02-DECISIONS/0016-the-lab.md) |
|
| [`02-scenario-declaration.md`](02-scenario-declaration.md) | What a scenario declares — the underlay, and what to place on it | [ADR 0009](../../02-DECISIONS/0009-the-lab.md) |
|
||||||
| [`03-scenario-lifecycle.md`](03-scenario-lifecycle.md) | What happens to a scenario — raise, snapshot, restore, move, destroy | [ADR 0016](../../02-DECISIONS/0016-the-lab.md) |
|
| [`03-scenario-lifecycle.md`](03-scenario-lifecycle.md) | What happens to a scenario — raise, snapshot, restore, move, destroy | [ADR 0009](../../02-DECISIONS/0009-the-lab.md) |
|
||||||
| [`04-lab-installation.md`](04-lab-installation.md) | Getting the lab onto a clean machine, and why it verifies capability rather than installation | [ADR 0058](../../02-DECISIONS/0058-delivery.md) |
|
| [`04-lab-installation.md`](04-lab-installation.md) | Getting the lab onto a clean machine, and why it verifies capability rather than installation | [ADR 0023](../../02-DECISIONS/0023-delivery.md) |
|
||||||
| [`05-the-node-host.md`](05-the-node-host.md) | Tier 0 — the one thing installed by hand, and the only thing that changes a machine | [ADR 0037](../../02-DECISIONS/0037-the-node-host.md) |
|
| [`05-the-node-host.md`](05-the-node-host.md) | Tier 0 — the one thing installed by hand, and the only thing that changes a machine | [ADR 0016](../../02-DECISIONS/0016-the-node-host.md) |
|
||||||
| [`06-the-control-plane.md`](06-the-control-plane.md) | Tier 2 — what the term means, and the test for what belongs in it | [ADR 0037](../../02-DECISIONS/0037-the-node-host.md) |
|
| [`06-the-control-plane.md`](06-the-control-plane.md) | Tier 2 — what the term means, and the test for what belongs in it | [ADR 0016](../../02-DECISIONS/0016-the-node-host.md) |
|
||||||
| [`07-the-substrate.md`](07-the-substrate.md) | Tier 1 — what the control plane consumes and cannot grant itself | [ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md), [0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md) |
|
| [`07-the-substrate.md`](07-the-substrate.md) | Tier 1 — what the control plane consumes and cannot grant itself | [ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md), [0048](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md) |
|
||||||
| [`08-connectivity.md`](08-connectivity.md) | One context in full — overlay, resolution, exposure, filtering, certificates | [ADR 0049](../../02-DECISIONS/0049-connectivity.md), [0050](../../02-DECISIONS/0049-connectivity.md), [0051](../../02-DECISIONS/0036-a-node-and-how-it-joins.md), [0055](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md) |
|
| [`08-connectivity.md`](08-connectivity.md) | One context in full — overlay, resolution, exposure, filtering, certificates | [ADR 0022](../../02-DECISIONS/0022-connectivity.md), [0050](../../02-DECISIONS/0022-connectivity.md), [0051](../../02-DECISIONS/0015-a-node-and-how-it-joins.md), [0055](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md) |
|
||||||
| [`09-the-node-lifecycle.md`](09-the-node-lifecycle.md) | How a machine becomes a node, stays one, and stops being one | [ADR 0036](../../02-DECISIONS/0036-a-node-and-how-it-joins.md), [0051](../../02-DECISIONS/0036-a-node-and-how-it-joins.md) |
|
| [`09-the-node-lifecycle.md`](09-the-node-lifecycle.md) | How a machine becomes a node, stays one, and stops being one | [ADR 0015](../../02-DECISIONS/0015-a-node-and-how-it-joins.md), [0051](../../02-DECISIONS/0015-a-node-and-how-it-joins.md) |
|
||||||
| [`10-delivery.md`](10-delivery.md) | Modules, the three edges, and how a change becomes a running thing | [ADR 0058](../../02-DECISIONS/0058-delivery.md), [0064](../../02-DECISIONS/0044-modules-and-the-graph.md), [0065](../../02-DECISIONS/0044-modules-and-the-graph.md) |
|
| [`10-delivery.md`](10-delivery.md) | Modules, the three edges, and how a change becomes a running thing | [ADR 0023](../../02-DECISIONS/0023-delivery.md), [0064](../../02-DECISIONS/0019-modules-and-the-graph.md), [0065](../../02-DECISIONS/0019-modules-and-the-graph.md) |
|
||||||
|
|
||||||
## Not yet written
|
## Not yet written
|
||||||
|
|
||||||
- **The remaining six contexts.**
|
- **The remaining six contexts.**
|
||||||
[ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md)
|
[ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)
|
||||||
settles the list at seven; `connectivity` is the first written in full
|
settles the list at seven; `connectivity` is the first written in full
|
||||||
([`08`](08-connectivity.md)) and the other six do not exist yet. The work breakdown says in
|
([`08`](08-connectivity.md)) and the other six do not exist yet. The work breakdown says in
|
||||||
what order they are needed.
|
what order they are needed.
|
||||||
- ~~**Domain grouping outside the core.**~~ **Not needed.**
|
- ~~**Domain grouping outside the core.**~~ **Not needed.**
|
||||||
[ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md) is
|
[ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md) is
|
||||||
superseded by [ADR 0044](../../02-DECISIONS/0044-modules-and-the-graph.md):
|
superseded by [ADR 0019](../../02-DECISIONS/0019-modules-and-the-graph.md):
|
||||||
there is no domain module to group into, so there is no domain list to settle. Relationships
|
there is no domain module to group into, so there is no domain list to settle. Relationships
|
||||||
are edges, and grouping is a tag and a query.
|
are edges, and grouping is a tag and a query.
|
||||||
|
|||||||
@@ -31,14 +31,14 @@ exists to catch — a step that failed, reported success, and left the next step
|
|||||||
state that was never produced.
|
state that was never produced.
|
||||||
|
|
||||||
It is also a direct violation of a decision already taken and recorded:
|
It is also a direct violation of a decision already taken and recorded:
|
||||||
[ADR 0058](../../02-DECISIONS/0058-delivery.md) says a step that fails must fail the
|
[ADR 0023](../../02-DECISIONS/0023-delivery.md) says a step that fails must fail the
|
||||||
job. That record notes the rule is applied instance by instance and enforced by no mechanism.
|
job. That record notes the rule is applied instance by instance and enforced by no mechanism.
|
||||||
This is an instance where it was never applied.
|
This is an instance where it was never applied.
|
||||||
|
|
||||||
## Evidence
|
## Evidence
|
||||||
|
|
||||||
- Observed 2026-08-22 while declaring the virtualisation package required by
|
- Observed 2026-08-22 while declaring the virtualisation package required by
|
||||||
[ADR 0016](../../02-DECISIONS/0016-the-lab.md).
|
[ADR 0009](../../02-DECISIONS/0009-the-lab.md).
|
||||||
- A fix is written and open as a pull request, unmerged since 2026-08-20.
|
- A fix is written and open as a pull request, unmerged since 2026-08-20.
|
||||||
|
|
||||||
## Open questions
|
## Open questions
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ recoverable by retrying — it removes the ability to issue a certificate anyone
|
|||||||
|
|
||||||
The consequence lands hardest on exactly the work most likely to iterate: standing up a new
|
The consequence lands hardest on exactly the work most likely to iterate: standing up a new
|
||||||
node, changing how names resolve, or testing the lab's certificate authority split
|
node, changing how names resolve, or testing the lab's certificate authority split
|
||||||
([ADR 0016](../../02-DECISIONS/0016-the-lab.md)).
|
([ADR 0009](../../02-DECISIONS/0009-the-lab.md)).
|
||||||
|
|
||||||
## Evidence
|
## Evidence
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ coverage was assumed, not checked.
|
|||||||
## Evidence
|
## Evidence
|
||||||
|
|
||||||
- The workspace was removed by pull request #240 on 2026-06-04
|
- The workspace was removed by pull request #240 on 2026-06-04
|
||||||
([ADR 0007](../../02-DECISIONS/0007-no-npm-workspace.md)).
|
([ADR 0004](../../02-DECISIONS/0004-no-npm-workspace.md)).
|
||||||
- The harness has not built since that date.
|
- The harness has not built since that date.
|
||||||
- Recorded in the knowledge base as a standing entry, not as a fixed incident.
|
- Recorded in the knowledge base as a standing entry, not as a fixed incident.
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ checked it — including in the same commit that wrote the rule.
|
|||||||
## Proposed direction — Nox is the search
|
## Proposed direction — Nox is the search
|
||||||
|
|
||||||
*Added 2026-08-23.* Rather than syncing these documents into the knowledge base, **Nox
|
*Added 2026-08-23.* Rather than syncing these documents into the knowledge base, **Nox
|
||||||
([ADR 0019](../../02-DECISIONS/0019-how-this-repository-works.md)) works from within this
|
([ADR 0011](../../02-DECISIONS/0011-how-this-repository-works.md)) works from within this
|
||||||
repository and holds its knowledge directly.** Retrieval becomes an agent reading the source,
|
repository and holds its knowledge directly.** Retrieval becomes an agent reading the source,
|
||||||
not a copy living in a second store.
|
not a copy living in a second store.
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ This is a better answer than the one the README originally promised, on three co
|
|||||||
was that it adds a fourth knowledge *system*. An agent with read access adds no store at all.
|
was that it adds a fourth knowledge *system*. An agent with read access adds no store at all.
|
||||||
- **It is always current**, including for uncommitted work in progress.
|
- **It is always current**, including for uncommitted work in progress.
|
||||||
|
|
||||||
**But it changes the promise, and that is worth stating rather than glossing.** ADR 0019's
|
**But it changes the promise, and that is worth stating rather than glossing.** ADR 0011's
|
||||||
answer was that these documents would be returned *beside everything else* in a symptom search.
|
answer was that these documents would be returned *beside everything else* in a symptom search.
|
||||||
An agent that must be **asked** is reachable; it is not surfacing. The two differ in exactly
|
An agent that must be **asked** is reachable; it is not surfacing. The two differ in exactly
|
||||||
the case the operational memory is designed for: someone debugging an error who has no reason
|
the case the operational memory is designed for: someone debugging an error who has no reason
|
||||||
@@ -82,9 +82,9 @@ So the open question narrows to one thing:
|
|||||||
> When a symptom is searched and the answer happens to live in a design document or a decision
|
> When a symptom is searched and the answer happens to live in a design document or a decision
|
||||||
> record here, does the searcher find it without already suspecting it exists?
|
> record here, does the searcher find it without already suspecting it exists?
|
||||||
|
|
||||||
If Nox is the only path, the answer is no, and the reasoning in ADR 0019 needs amending rather
|
If Nox is the only path, the answer is no, and the reasoning in ADR 0011 needs amending rather
|
||||||
than satisfying. If Nox also contributes what it knows to a symptom search — or the search
|
than satisfying. If Nox also contributes what it knows to a symptom search — or the search
|
||||||
consults Nox — the answer is yes and the original promise holds.
|
consults Nox — the answer is yes and the original promise holds.
|
||||||
|
|
||||||
That is a design question for Nox, not a defect in this repository, and it should be settled
|
That is a design question for Nox, not a defect in this repository, and it should be settled
|
||||||
before ADR 0019 is treated as answered.
|
before ADR 0011 is treated as answered.
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ The distance between the two is the same one the delivery layer already has a na
|
|||||||
## Why it matters now
|
## Why it matters now
|
||||||
|
|
||||||
This is the first requirement of the lab
|
This is the first requirement of the lab
|
||||||
([ADR 0016](../../02-DECISIONS/0016-the-lab.md)), which is
|
([ADR 0009](../../02-DECISIONS/0009-the-lab.md)), which is
|
||||||
phase 0 of the entire migration. The first capability the new work depends on is present,
|
phase 0 of the entire migration. The first capability the new work depends on is present,
|
||||||
declared, and unusable — and would have stayed unusable silently.
|
declared, and unusable — and would have stayed unusable silently.
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ node recovers itself.
|
|||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
**The as-is only.** The design being built has a different answer:
|
**The as-is only.** The design being built has a different answer:
|
||||||
[ADR 0037](../../02-DECISIONS/0037-the-node-host.md) puts recovery
|
[ADR 0016](../../02-DECISIONS/0016-the-node-host.md) puts recovery
|
||||||
in a launcher that supervises the host, and that recovery is tested — 32 assertions, each
|
in a launcher that supervises the host, and that recovery is tested — 32 assertions, each
|
||||||
confirmed to fail when the behaviour is removed.
|
confirmed to fail when the behaviour is removed.
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ is, which is a scheduling question rather than a technical one.
|
|||||||
## What it would take to be sure
|
## What it would take to be sure
|
||||||
|
|
||||||
Read back rather than assumed
|
Read back rather than assumed
|
||||||
([ADR 0035](../../02-DECISIONS/0035-a-picture-is-read-from-what-runs.md)): list every unit on a
|
([ADR 0014](../../02-DECISIONS/0014-a-picture-is-read-from-what-runs.md)): list every unit on a
|
||||||
node and grep for `OnFailure=`; list every timer and check what each one calls. The finding above
|
node and grep for `OnFailure=`; list every timer and check what each one calls. The finding above
|
||||||
came from reading the repository, and confirming it against a running node is the difference
|
came from reading the repository, and confirming it against a running node is the difference
|
||||||
between *no unit declares this* and *no unit in the source declares this*.
|
between *no unit declares this* and *no unit in the source declares this*.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ amended-design:
|
|||||||
|
|
||||||
Two accepted decisions collide, and the collision makes one resource shape untestable.
|
Two accepted decisions collide, and the collision makes one resource shape untestable.
|
||||||
|
|
||||||
- **[ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md)** pins images by
|
- **[ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)** pins images by
|
||||||
digest, and the host **refuses** an image reference that is not pinned:
|
digest, and the host **refuses** an image reference that is not pinned:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -66,7 +66,7 @@ for.
|
|||||||
## The shape of a resolution
|
## The shape of a resolution
|
||||||
|
|
||||||
**A registry inside the scenario**, on its public segment, that machines pull from. That is not a
|
**A registry inside the scenario**, on its public segment, that machines pull from. That is not a
|
||||||
workaround: it is what the real mesh does — [ADR 0048](../../02-DECISIONS/0048-the-substrate-and-the-control-plane.md)
|
workaround: it is what the real mesh does — [ADR 0021](../../02-DECISIONS/0021-the-substrate-and-the-control-plane.md)
|
||||||
names an OCI registry as substrate, and every node after the first pulls from the mesh's own.
|
names an OCI registry as substrate, and every node after the first pulls from the mesh's own.
|
||||||
Testing against a registry is testing the real path rather than a stand-in for it.
|
Testing against a registry is testing the real path rather than a stand-in for it.
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ It also removes the lab's export-and-push mechanism rather than fixing it, which
|
|||||||
outcome: pushing image tarballs over the hypervisor was always a lab-only invention.
|
outcome: pushing image tarballs over the hypervisor was always a lab-only invention.
|
||||||
|
|
||||||
**Not decided here**, because it is design rather than repair: where the registry runs, whether
|
**Not decided here**, because it is design rather than repair: where the registry runs, whether
|
||||||
it is scenery like the router ([ADR 0016](../../02-DECISIONS/0016-the-lab.md))
|
it is scenery like the router ([ADR 0009](../../02-DECISIONS/0009-the-lab.md))
|
||||||
or a placed artifact, and how images get into it.
|
or a placed artifact, and how images get into it.
|
||||||
|
|
||||||
## Incidental, and already fixed
|
## Incidental, and already fixed
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Agent instructions — Novox HQ
|
# Agent instructions — Novox HQ
|
||||||
|
|
||||||
This repository is the source of truth for Novox's mission, research, design and decisions —
|
This repository is the source of truth for Novox's mission, research, design and decisions —
|
||||||
today almost entirely those of **Novox Mesh**, its first product ([ADR 0019](02-DECISIONS/0019-how-this-repository-works.md)). Implementation lives in the code repositories (see
|
today almost entirely those of **Novox Mesh**, its first product ([ADR 0011](02-DECISIONS/0011-how-this-repository-works.md)). Implementation lives in the code repositories (see
|
||||||
[`00-META/repos.md`](00-META/repos.md)).
|
[`00-META/repos.md`](00-META/repos.md)).
|
||||||
|
|
||||||
Before changing anything here, read the playbooks in
|
Before changing anything here, read the playbooks in
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ Answered separately, a repository of its own is the better home:
|
|||||||
changes. Tying documents to a code branch means they merge on the code's schedule.
|
changes. Tying documents to a code branch means they merge on the code's schedule.
|
||||||
- **The reviewers are different.** A design argument is not reviewed the way an
|
- **The reviewers are different.** A design argument is not reviewed the way an
|
||||||
implementation is, and it should not queue behind a build.
|
implementation is, and it should not queue behind a build.
|
||||||
- **The scope is wider than one repository.** [ADR 0015](02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md) sends most modules out of the
|
- **The scope is wider than one repository.** [ADR 0008](02-DECISIONS/0008-mesh-brokers-nodes-host-agents-think.md) sends most modules out of the
|
||||||
monorepo entirely. Documentation that governs several repositories cannot live inside one
|
monorepo entirely. Documentation that governs several repositories cannot live inside one
|
||||||
of them.
|
of them.
|
||||||
|
|
||||||
@@ -110,4 +110,4 @@ than a mechanism — which is why every decision is recorded in
|
|||||||
[`02-DECISIONS`](02-DECISIONS/) as it is taken, and why a document that states a rule should
|
[`02-DECISIONS`](02-DECISIONS/) as it is taken, and why a document that states a rule should
|
||||||
say how the rule is checked.
|
say how the rule is checked.
|
||||||
|
|
||||||
Recorded as [ADR 0019](02-DECISIONS/0019-how-this-repository-works.md).
|
Recorded as [ADR 0011](02-DECISIONS/0011-how-this-repository-works.md).
|
||||||
|
|||||||
Reference in New Issue
Block a user