From 7fe2c31bdf7bf0d07798d34cc8837d5cf3f06e44 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 29 Aug 2026 23:21:01 +0200 Subject: [PATCH] Networking is a module, and what a domain module actually is MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two records, from building it. 0009 has a section titled "there are no domain modules", and `networking` now exists. It is not a contradiction and it reads as one, so the difference is written down: what was refused contains WireGuard and a proxy and is assigned where half of it is unwanted. What exists contains nothing — requirements and a name — so there is no half. Every artifact it leads to is still an ordinary module assigned on its own terms. With the cost stated, because it is real: adding a second implementation turns a settled question into an open one for everyone using the bundle, not only for whoever wanted the alternative. That is the refusing rule applied consistently, and the alternative is a default, which is the flavor field returning under a better name. 08-connectivity gains why the network stopped being code beside the module system: a machine was on the private network because it had an address, and there was no way to keep one off. A manifest can now say its resources are computed, which is what a peer list needs. And three modules rather than one, because WireGuard is one VPN of several. Naming a module after the job and putting one implementation inside it is flavor wearing a generic name — the second VPN has nowhere to go. --- 02-DECISIONS/0009-modules-and-the-graph.md | 46 +++++++++++++++++++ 03-DESIGN/01-to-be/08-connectivity.md | 51 +++++++++++++++++++++- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/02-DECISIONS/0009-modules-and-the-graph.md b/02-DECISIONS/0009-modules-and-the-graph.md index 1220777..da422f4 100644 --- a/02-DECISIONS/0009-modules-and-the-graph.md +++ b/02-DECISIONS/0009-modules-and-the-graph.md @@ -36,6 +36,46 @@ module containing both would be assigned where half of it is unwanted. seeing what belongs together — is a tag and a query, neither of which anybody has to keep true by hand. +### What a domain module turns out to be, and why it is not the one refused above + +*Written 2026-08-29, from building it. The heading above reads as a contradiction of what now +exists and is not one — but only if the difference is stated, so it is stated here.* + +**What was refused contains things. What exists contains nothing.** + +| | `networking` as refused | `networking` as built | +|---|---|---| +| what is in it | WireGuard, a proxy, a firewall — artifacts | nothing at all | +| what it says | *these ship together* | *I want a private network and names* | +| what is assigned | one module, half of it unwanted | whatever answers each requirement, each on its own | + +The objection above is untouched by this and still correct: a module holding WireGuard and a +proxy is assigned where half of it is unwanted. **A module holding nothing cannot be, because +there is no half.** It is requirements and a name, and every artifact it leads to is still an +ordinary module assigned on its own terms. + +**Why it is worth having.** Most people want the network working and do not want to choose a VPN. +`assign networking` finds one answer to each requirement and takes it without asking, because +with one candidate there was never a question — the rule below about refusing does the work. +Somebody who does care assigns the VPN they want, and *that is the whole of choosing*: there is no +flavor field, no variant syntax, and no second verb. **Picking an implementation is assigning a +module.** + +**What it costs, stated because it is real.** Adding a second implementation to the catalogue +turns a settled question into an open one for **everyone using the bundle**, not only for whoever +wanted the alternative. Every node assigned `networking` refuses until somebody says which. That +is [the refusing rule](#a-requirement-with-several-answers-is-refused-never-guessed) applied +consistently, and the alternative is a default — which is the flavor field returning under a +better name. The cost is one assignment per node, and the message names the candidates. + +**A consequence that had to be found by running it.** A bundle can drag an implementation in +through a requirement nobody looked at. Choosing a different VPN still installed WireGuard, +because the names module needed addresses only WireGuard hands out, and nobody was told. Two VPNs +on one machine is not always wrong — a machine may run one for another purpose — but being **the** +network the mesh runs over is singular, so that is a claim, and the collision is refused by name. +**The general rule: what a bundle pulls in is only as safe as the claims on what it pulls in +from.** + ## Three edges | edge | means | declared? | satisfied | @@ -188,6 +228,12 @@ What it was reaching for is two ordinary things: If something is neither, it is probably two modules. +**A third thing it was reaching for, added 2026-08-29:** *I want this working and I do not care +which one.* That is a module with requirements and no files — +[a domain module](#what-a-domain-module-turns-out-to-be-and-why-it-is-not-the-one-refused-above) — +and it is what makes "different modules that provide the same thing" bearable for somebody who +does not want to know there is a choice. + ## The core library is the mesh's domain One module everything may depend on. It holds **what is true of the mesh regardless of which diff --git a/03-DESIGN/01-to-be/08-connectivity.md b/03-DESIGN/01-to-be/08-connectivity.md index 81e1084..3f58828 100644 --- a/03-DESIGN/01-to-be/08-connectivity.md +++ b/03-DESIGN/01-to-be/08-connectivity.md @@ -2,7 +2,7 @@ layer: to-be status: designed code: [] -updated: 2026-08-27 +updated: 2026-08-29 decisions: - 02-DECISIONS/0005-the-node-host.md - 02-DECISIONS/0004-a-node-and-how-it-joins.md @@ -56,6 +56,55 @@ they are the reason every node permanently holds a credential to it ([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)). Both are connectivity modules. **Closing this context closes that set.** +### And they are modules, not a second mechanism beside the module system + +*Written 2026-08-29, from building it. The first version was code beside the module system doing +the module system's job, and the fault it produced is the point of writing this down.* + +**A machine was on the private network because it had an address.** Every node that had been +placed got a peer list, whether or not anybody wanted it there, and there was no way to say a +machine should stay off. That is what "special-cased" cost, and it was invisible until somebody +wanted the exception. + +**What made it look unavoidable:** a peer list cannot be written in a manifest. It is derived from +every other machine, so it differs on each one and changes when any of them changes. So the +manifest says its resources are **computed** — it names something in the control plane that works +them out per node — and it is a module in every other respect: assigned, resolved, configured by +settings, and absent from a machine nobody gave it to. + +**What that made possible immediately** is the arrangement below, which the code has: + +| module | provides | requires | claims | +|---|---|---|---| +| the WireGuard one | a private network, **and the mesh's own addressing** | | *the* private network, one per node | +| the names one | name resolution | the mesh's own addressing | | +| `networking` | | both of the above | | + +**Three rather than one, because WireGuard is one VPN of several.** Naming the module after the +job — `networking` — and putting WireGuard inside it is the retired *flavor* idea wearing a +generic name: the second VPN has nowhere to go. So a module is named for what it *is* and declares +what it *does*, and `networking` is the third row — requirements and no files +([ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md)). + +**Names left the WireGuard module for their own.** They had been delivered inside it, on the +argument that a machine with peers and no names is half on the network. True, and the wrong place +to fix it — names are identical over a *different* private network, so bundling them made one +module out of two things. They require the mesh's **addressing** rather than a private network in +general, because that is what they are computed from: over a VPN that hands out its own addresses +the mesh has nothing to write, and refusing is what stops a machine being given a hosts file that +means nothing on it. + +**And the claim is not decoration.** Choosing a different VPN still installed WireGuard — dragged +back in by the names, which needed addresses only WireGuard hands out — and nobody was told. +Running two VPNs is not always wrong; being *the* one the mesh runs over is singular. So it is a +claim, and the collision is refused by name. + +**What is still not a module, and why that is correct.** The host needs none of this. It has an +address and a route before the mesh exists — that is the machine's own networking — and the +broker's address is carried in the token rather than resolved +([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)). **The one connection that +carries modules cannot itself be one.** Everything above it can be, and now is. + ## The order it comes up in The one thing to get right, because everything else depends on it: