From edad6acf49673e9bebaa0afd396255fbff1f220e Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 6 Oct 2026 02:21:15 +0200 Subject: [PATCH] ADR 0226: assign the private network by its own name, let the proxy name its public issuer Two modules existed only to say one thing each: networking required mesh-wireguard and nothing else, and public-acme pointed the one proxy at Let's Encrypt. dhcpcd and cloudflare-dns are assigned nowhere. Retire all four, keeping every machine's network and every certificate as they are. --- 02-DECISIONS/0009-modules-and-the-graph.md | 5 + ...name-is-provisioned-like-any-capability.md | 4 + .../0117-a-machines-uplink-is-a-seat.md | 4 + ...e-and-the-proxy-names-its-public-issuer.md | 155 ++++++++++++++++++ 02-DECISIONS/README.md | 1 + 03-DESIGN/01-to-be/08-connectivity.md | 35 +++- 03-DESIGN/01-to-be/12-a-module-repository.md | 8 +- .../01-to-be/21-the-installation-in-full.md | 7 +- 8 files changed, 207 insertions(+), 12 deletions(-) create mode 100644 02-DECISIONS/0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md diff --git a/02-DECISIONS/0009-modules-and-the-graph.md b/02-DECISIONS/0009-modules-and-the-graph.md index 08fe2ab..390c36a 100644 --- a/02-DECISIONS/0009-modules-and-the-graph.md +++ b/02-DECISIONS/0009-modules-and-the-graph.md @@ -38,6 +38,11 @@ hand. ### What a domain module turns out to be, and why it is not the one refused above +> **Narrowed, not replaced — 2026-10-06, by [ADR 0226](0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md).** The catalogue no longer +> holds a domain module: `networking` was retired, and every machine is assigned the private network's +> own module. A module with requirements and no files stays a thing a module may be; "why it is worth +> having" below describes what this record decided for `networking`, not what runs. + *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.* diff --git a/02-DECISIONS/0044-a-public-name-is-provisioned-like-any-capability.md b/02-DECISIONS/0044-a-public-name-is-provisioned-like-any-capability.md index d86e0a5..0aa0f9d 100644 --- a/02-DECISIONS/0044-a-public-name-is-provisioned-like-any-capability.md +++ b/02-DECISIONS/0044-a-public-name-is-provisioned-like-any-capability.md @@ -9,6 +9,10 @@ extends: 0027-a-provision-names-what-the-consumer-is-coupled-to.md # 44. A public name is provisioned, not registered by hand +> **The mechanism changed — 2026-10-06, by [ADR 0226](0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md).** The interface stands; its one provider, +> `cloudflare-dns`, left the catalogue, assigned nowhere and required by nothing. A mesh that needs a +> public name made for it adds a provider of `public-dns` again. + ## Context The mesh names and resolves its own machines internally: the overlay generates diff --git a/02-DECISIONS/0117-a-machines-uplink-is-a-seat.md b/02-DECISIONS/0117-a-machines-uplink-is-a-seat.md index 7fbc5da..d5e6f8f 100644 --- a/02-DECISIONS/0117-a-machines-uplink-is-a-seat.md +++ b/02-DECISIONS/0117-a-machines-uplink-is-a-seat.md @@ -9,6 +9,10 @@ extends: 0110-a-seat-is-a-module-assignment-from-a-closed-set.md # 117. A machine's uplink is a seat: the mesh configures the manager, never the link +> **The mechanism changed — 2026-10-06, by [ADR 0226](0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md).** The seat stands; the catalogue holds two +> of its three modules. `dhcpcd`, held by no machine, left it. What this record says of dhcpcd is what +> a module for it must do, if one is written again. + ## Context The mesh installs on top of a machine's own networking. The private network's generator says diff --git a/02-DECISIONS/0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md b/02-DECISIONS/0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md new file mode 100644 index 0000000..6d0973a --- /dev/null +++ b/02-DECISIONS/0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md @@ -0,0 +1,155 @@ +--- +topic: the tiers +status: accepted +date: 2026-10-06 +deciders: jochen +reconstructed: false +supersedes-in-part: + - 0009-modules-and-the-graph.md +extends: 02-DECISIONS/0007-connectivity.md +--- + +# 226. The private network is assigned by its own name, and the proxy names its public issuer + +## Context + +**The operator, 2026-10-06: "too many network-related modules … we simply have a private network, +some docker stuff, a firewall and a public and internal certificate resolver."** Counted on the +production mesh that day, from the controller's module listing and each machine's plan: + +| module | on | what it is | +|---|---|---| +| `networking` | all four machines | requirements only: `private-network`, nothing else. Ships nothing | +| `mesh-wireguard` | on none directly; on all four through `networking` | the private network; its resources are computed by the controller | +| `public-acme` | the anchor and the home server | runs nothing; offers `acme-ca`, pointing at Let's Encrypt's production directory | +| `route-proxy` | the anchor and the home server | the public front door; the only consumer of `acme-ca` | +| `dhcpcd` | none | a third holder of `node-uplink`, for a machine whose link is dhcpcd's | +| `cloudflare-dns` | none | the only provider of `public-dns`; nothing in the catalogue requires `public-dns` | + +**`networking` is the domain module [ADR 0009](0009-modules-and-the-graph.md) argued for in its +"what a domain module turns out to be" section**: a module holding nothing, so `assign networking` +finds one VPN and takes it, and choosing another is assigning that instead. It has never been more +than a second name for one module: one VPN exists, every machine runs it, and the name resolution it +once also required became a fact (ADR 0199). Its cost is real: every machine carries two modules for +one network, genesis assigns a module that is not the network, and the resolver's hint for a machine +off the network names the bundle rather than what it installs. + +**`public-acme` is a provision with one consumer and one answer.** It was introduced so a lab without +a public issuer could answer `acme-ca` from `step-ca` instead (ADR 0066); `step-ca` now offers only +`internal-acme-ca`, and the mesh itself is the test bed (ADR 0149). What remained was a module +assigned beside every proxy to say "Let's Encrypt", and a pin when two answers stood on one machine +(issue 258). + +**The proxy's account directory is named after the issuer as rendered.** `route-proxy` keeps each +authority's ACME account and certificates under `/var/lib/route-proxy/acme`, in a directory named by +a digest of the directory URL and of the root bundle its `trust` step copies in (the proxy's +`forThisAuthority`). The binding rendered the URL as `https://acme-v02.api.letsencrypt.org:443/directory` +— with the port — and the root as the system bundle of the pinned `trust` image. Spelled any other +way, the proxy sees a new authority, registers a new account and orders every routed name again. On +2026-10-06 that is 25 names under one registered domain on the home server, all issued on 2026-10-01, +and 26 on the anchor: a reissue of the home server's would reach Let's Encrypt's 50-per-week limit for that +domain inside the week. + +## Considered Options + +1. **The private network a default of every machine**, with no assignment. Rejected: "a machine is + on the private network because it was assigned the module" is what made the network a module at + all (connectivity §1, 2026-08-29); a default is a second path to the same state, and a machine + that should stay off would need an exception mechanism that does not exist. +2. **Keep `networking`, document it better.** Rejected: it answers a question — which VPN — that has + had one answer since it was written, and costs a module on every machine to do so. Choosing + another VPN is still assigning it; the bundle never added anything to that. +3. **Assign `mesh-wireguard` directly on every machine and retire the bundle.** Chosen. +4. **`route-proxy` provides `acme-ca` itself**, keeping the provision. Rejected: nothing else + consumes it, and a provision whose only consumer is its provider is a field, not an edge. +5. **The public issuer as a setting of `route-proxy`.** Rejected: a setting has no default (ADR + 0112), so every mesh would need it set before the proxy resolves, and the one value that must not + change — the URL as rendered, port included — would become a value anybody can change with + `settings set`. Changing the public issuer of a live proxy is a reissue of every certificate it + holds; it should take an edit of the module and a decision, not a command. +6. **`route-proxy` states Let's Encrypt in its own `acme.env`, byte for byte what the binding + rendered, and `public-acme` retires.** Chosen. + +## Decision + +**1. The private network is assigned by its own name.** Every machine is assigned `mesh-wireguard` +directly. The `networking` module is retired: the controller no longer ships it, genesis assigns +`mesh-wireguard`, and the resolver's refusal for two machines that share no private network names +`mesh-wireguard`. Another VPN is still chosen by assigning it instead, and the node-scoped claim +`the-private-network` still refuses two. This supersedes ADR 0009's section *what a domain module +turns out to be*: the mechanism — a module with requirements and no files — stands, and the +catalogue no longer holds one. + +**2. A module the controller stops shipping is retired at its next start, never from under a +machine.** `module forget` refuses a module the controller ships, because the next start would put +it back; so a retired one could be removed by nothing. At start the controller removes every module +it recorded as its own and no longer ships — unless a machine is still assigned it, or the mesh still +holds settings, secrets or ports for it, in which case it is kept and the start says why. + +**3. The proxy names its public issuer.** `route-proxy` no longer requires `acme-ca`; its `acme.env` +states Let's Encrypt's production directory exactly as the binding rendered it. `public-acme` leaves +the catalogue and the mesh. The internal issuer stays a provision (`internal-acme-ca`, from +`step-ca`): it is a module that runs, held on one machine and consumed by every proxy and by +`ca-trust`. The proxy binary's own default stays Let's Encrypt *staging*, for anything that runs it +without the module. + +**4. `dhcpcd` and `cloudflare-dns` leave the catalogue.** Neither is assigned anywhere; nothing +requires `public-dns`. `node-uplink` keeps two holders (NetworkManager, systemd-networkd), and the +`public-dns` interface of [ADR 0044](0044-a-public-name-is-provisioned-like-any-capability.md) has no +provider until a mesh needs one. + +`avahi` and `netcheck` are not decided here. + +## Consequences + +- **The rollout changes no machine's network or certificates.** Assigning `mesh-wireguard` beside + `networking` and then unassigning `networking` leaves every machine's declaration of the private + network as it was; the proxy's `acme.env` is unchanged, so its `trust` step does not run again and + its account directory is the one it has. The one file that goes is the proxy's binding to + `acme-ca`, which nothing reads. +- **The order is assign, then unassign.** Unassigning `networking` first takes `mesh-wireguard` off + every machine that does not also run `dnsmasq` (which requires the mesh's addressing) at the next + push — the private network down. The controller's retirement refuses nothing here; it only keeps + the bundle while it is assigned. +- **The public issuer moves only with a plan for the account.** The proxy's account directory depends + on the URL as spelled and on the pinned `trust` image's root bundle. Moving that image's digest + reorders every certificate on both proxies; a test holds both values and says so. +- **The lab's beds that assigned `networking`, or pointed the proxy at `step-ca` for public names, + need changing before they run again.** A lab proxy now orders public names from Let's Encrypt's + production directory, so a bed that routes public names must not assign the module as it stands. +- **Genesis from an older host binary assigns `networking`, which a newer controller refuses.** The + host's change is merged before the controller's. +- **A mesh that wants another public issuer edits `route-proxy`**, rather than assigning a different + provider. That is deliberate (option 5). + +## How it is checked + +| Rule | Checked by | +|---|---| +| The controller ships one network module and no bundle; it resolves on its own | mesh-controller `internal/catalogue/provided_test.go` | +| A refusal for want of the private network names `mesh-wireguard` | the same file, `TestARefusalForWantOfThePrivateNetworkNamesItsModule` | +| Another VPN is chosen by assigning it, and WireGuard is not dragged in; two VPNs collide | the same file | +| A module no longer shipped is retired at start; kept while assigned or holding anything; a registered module is never touched | mesh-controller `internal/inventory/retire_test.go`, against a live database | +| Genesis assigns `mesh-wireguard`, never `networking` | mesh-host `internal/bootstrap/network_module_test.go` | +| `route-proxy` requires no `acme-ca`; its `acme.env` is byte for byte what the binding rendered; its `trust` image is the pinned one | mesh-controller `internal/catalogue/public_issuer_test.go` against the sibling catalogue | +| `public-acme`, `dhcpcd`, `cloudflare-dns` are not in the catalogue; nothing in it requires `acme-ca` or `public-dns` | the same file | +| Live: every machine still on the private network; every public name's certificate serial and expiry unchanged; no new file in a proxy's account directory | the rollout's checks, before and after each step: the controller's network view, `wg show` through each machine's tools, `openssl s_client` against every routed public name, and a listing of `/var/lib/route-proxy/acme` | + +## References + +- [ADR 0009](0009-modules-and-the-graph.md) — its section on a domain module is superseded; the rest + stands. +- [ADR 0007](0007-connectivity.md), [connectivity](../03-DESIGN/01-to-be/08-connectivity.md) §1 and §5, + amended alongside. +- [ADR 0066](0066-public-routing-is-name-agnostic.md) — the proxy requiring an issuer; the public one + is now its own fact. +- [ADR 0044](0044-a-public-name-is-provisioned-like-any-capability.md) — `public-dns`, without a + provider in the catalogue. +- [ADR 0117](0117-a-machines-uplink-is-a-seat.md) — `node-uplink`, now with two holders. +- [ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md), [ADR 0149](0149-the-live-mesh-is-the-test-bed.md). +- [Issue 258](../04-ISSUES/258-every-machine-bound-the-resolver-to-itself/00-report.md) — the pin two + issuers on one machine needed. +- mesh-controller `internal/overlay/generator.go`, `cmd/mesh-controller/modules.go`, `stores.go`, + `internal/inventory/catalogue.go`, `internal/catalogue/resolve.go`; mesh-host + `internal/bootstrap/phase2.go`; mesh-catalog `modules/route-proxy`, and the removal of + `modules/public-acme`, `modules/dhcpcd`, `modules/cloudflare-dns`. diff --git a/02-DECISIONS/README.md b/02-DECISIONS/README.md index 3a52a78..9cb8425 100644 --- a/02-DECISIONS/README.md +++ b/02-DECISIONS/README.md @@ -237,6 +237,7 @@ python3 00-META/checks/index.py fail if stale - **0196** — [A node asks the mesh's resolver first, and a public one only when it is silent](0196-a-node-asks-the-meshs-resolver-first-and-a-public-one-only-when-it-is-silent.md) - **0199** — [A module that answers names declares its zone, and a node's hosts file is one module's](0199-a-module-that-answers-names-declares-its-zone-and-a-nodes-hosts-file-is-one-modules.md) - **0223** — [The mesh has two resolvers, and a machine lists only them](0223-the-mesh-has-two-resolvers-and-a-machine-lists-only-them.md) +- **0226** — [The private network is assigned by its own name, and the proxy names its public issuer](0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md) ### What runs on them, and how it gets there diff --git a/03-DESIGN/01-to-be/08-connectivity.md b/03-DESIGN/01-to-be/08-connectivity.md index ed14fcd..af44f2d 100644 --- a/03-DESIGN/01-to-be/08-connectivity.md +++ b/03-DESIGN/01-to-be/08-connectivity.md @@ -12,12 +12,15 @@ code: - mesh-controller cmd/mesh-controller/holdings.go (the holders of a replicated seat, ADR 0223) - mesh-controller internal/catalogue/roster.go (each replicated seat's holders, for a template) - mesh-catalog modules/dnsmasq (the mesh's resolvers) - - mesh-catalog modules/networkmanager, modules/systemd-networkd, modules/dhcpcd (what a node asks, written by its uplink's holder) + - mesh-catalog modules/networkmanager, modules/systemd-networkd (what a node asks, written by its uplink's holder) + - mesh-catalog modules/route-proxy (the public issuer named by the proxy, ADR 0226) + - mesh-controller internal/overlay/generator.go (the private network's module, assigned by its own name, ADR 0226) - mesh-catalog modules/hostname (a node's /etc/hostname and /etc/hosts) - mesh-host internal/identity/serving.go - mesh-host internal/apply (the service that reflects a rule set; a whole file handed to its new owner) -updated: 2026-10-05 +updated: 2026-10-06 decisions: + - 02-DECISIONS/0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md - 02-DECISIONS/0223-the-mesh-has-two-resolvers-and-a-machine-lists-only-them.md - 02-DECISIONS/0220-what-a-machine-asks-needs-its-uplink-held-and-the-retired-resolver-pieces-go.md - 02-DECISIONS/0199-a-module-that-answers-names-declares-its-zone-and-a-nodes-hosts-file-is-one-modules.md @@ -121,7 +124,15 @@ settings, and absent from a machine nobody gave it to. |---|---|---|---| | 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 | | +| ~~`networking`~~ | | both of the above | | + +> **Amended 2026-10-06, by [ADR 0226](../../02-DECISIONS/0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md).** The names +> became a fact (ADR 0199), leaving `networking` a module that required one other and nothing else, +> assigned on every machine. It is retired: every machine is assigned the WireGuard module — +> `mesh-wireguard` — by its own name, and genesis does the same. Choosing another VPN is still +> assigning it instead; the claim still refuses two. A module the controller stops shipping is +> retired at its next start, and kept while any machine is assigned it. The paragraphs below record +> why the bundle was built. **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 @@ -399,8 +410,9 @@ time.* A network manager rewrites `/etc/resolv.conf` on every connectivity change unless it is told not to ([ADR 0117](../../02-DECISIONS/0117-a-machines-uplink-is-a-seat.md)), so the module holding `node-uplink` — the one telling it — writes the file itself, and there is one owner for it, the one -whose program would otherwise overwrite it. Each of the catalogue's three managers' modules -(NetworkManager, systemd-networkd, dhcpcd) renders the same template from the resolver's holders and +whose program would otherwise overwrite it. Each of the catalogue's managers' modules +(NetworkManager, systemd-networkd; dhcpcd's left the catalogue with +[ADR 0226](../../02-DECISIONS/0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md), held by no machine) renders the same template from the resolver's holders and requires the mesh's resolver, so a machine is refused when nothing in the mesh resolves rather than given a file listing nothing. The managers' own mechanisms were weighed and not used: NetworkManager's global DNS and dhcpcd's static nameservers each write the file in their own form — their own header, @@ -412,7 +424,7 @@ node declaring one path are refused. The module that wrote the file before, its ([ADR 0220](../../02-DECISIONS/0220-what-a-machine-asks-needs-its-uplink-held-and-the-retired-resolver-pieces-go.md)) retire. The file changes owner in one apply on each machine: the node-engine hands a whole file to the resource declaring its path now rather than removing it first, so a machine is never without it. -*Checked by the controller's tests that the three modules carry one identical template and that +*Checked by the controller's tests that the uplink modules carry one identical template and that nothing else in the catalogue writes the path, a resolution test refusing a second writer, and the node-engine's handover test, in which the file is present at every step of the apply.* @@ -923,6 +935,17 @@ defaults to the public authority's *production* endpoint. Two consequences, and worse than the lab problem that found it — every certificate experiment on a real node consumes production issuance quota, and a retry loop can exhaust it for a week. +> **Amended 2026-10-06, by [ADR 0226](../../02-DECISIONS/0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md).** Configurable in the +> proxy binary, which defaults to the authority's *staging* endpoint; fixed in the mesh. The public +> issuer was a provision, `acme-ca`, answered by a module that ran nothing and was assigned beside +> every proxy; it is the proxy module's own `acme.env` now, Let's Encrypt's production directory +> spelled exactly as the binding rendered it. The proxy names each authority's account directory after +> that spelling and the root it trusts, so changing either is a new account and every certificate +> ordered again: moving the public issuer is an edit of the module with a plan for the account, never +> an assignment. The internal issuer stays a provision, `internal-acme-ca`. +> *How it is checked:* mesh-controller `internal/catalogue/public_issuer_test.go` holds the rendered +> file byte for byte and the `trust` image's digest, against the catalogue. + **The mesh CA is not a bootstrap concern.** A joining node verifies the controller against the fingerprint in its token ([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md)), so nothing needs the CA before membership. It certifies internal names afterwards, and that is diff --git a/03-DESIGN/01-to-be/12-a-module-repository.md b/03-DESIGN/01-to-be/12-a-module-repository.md index 7cf2592..7b15046 100644 --- a/03-DESIGN/01-to-be/12-a-module-repository.md +++ b/03-DESIGN/01-to-be/12-a-module-repository.md @@ -7,8 +7,9 @@ code: - mesh-controller internal/catalogue/build.go - mesh-controller internal/inventory/secrets.go - mesh-controller cmd/mesh-builder -updated: 2026-09-30 +updated: 2026-10-06 decisions: + - 02-DECISIONS/0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md - 02-DECISIONS/0069-a-module-is-a-repository-and-a-path.md - 02-DECISIONS/0037-where-a-module-lives.md - 02-DECISIONS/0009-modules-and-the-graph.md @@ -37,8 +38,9 @@ three things the mesh now does separately: | turning one on for one node | **assignment**, which is per node already | | keeping related things together | **`requires`**, and a module with requirements and no files of its own | -`networking` is exactly that last row: it ships nothing, requires a private network and name -resolution, and assigning it brings both. So the module count does not return, because the thing +`networking` was exactly that last row: it shipped nothing, required a private network, and +assigning it brought one — retired by [ADR 0226](../../02-DECISIONS/0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md) once one VPN was the +only answer it ever gave, and every machine is assigned that module by its own name. So the module count does not return, because the thing that made it return — *a module is expensive, so put several things in one* — is gone. A module here is cheap: a manifest and, usually, nothing else. diff --git a/03-DESIGN/01-to-be/21-the-installation-in-full.md b/03-DESIGN/01-to-be/21-the-installation-in-full.md index 4183861..8313fd3 100644 --- a/03-DESIGN/01-to-be/21-the-installation-in-full.md +++ b/03-DESIGN/01-to-be/21-the-installation-in-full.md @@ -5,8 +5,9 @@ code: - mesh-host internal/bootstrap - mesh-host cmd/mesh-bootstrap - mesh-lab test/integration/one-node-mesh.test.ts -updated: 2026-09-21 +updated: 2026-10-06 decisions: + - 02-DECISIONS/0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md - 02-DECISIONS/0067-genesis-is-a-pivot.md - 02-DECISIONS/0073-the-installer-carries-a-builder.md - 02-DECISIONS/0071-where-genesis-gets-its-source.md @@ -72,7 +73,7 @@ catalogue be missing from a test for weeks without anything complaining. | 15 | the catalogue is built and run | the module graph | without it the mesh cannot say what it holds, what a change reaches, or what must be rebuilt | | 16 | the catalogue asks for what it missed | the builds made before it existed are replayed | on a fresh mesh those are always the base, the store and the catalogue itself ([issue 050](../../04-ISSUES/050-the-catalogue-knows-nothing-built-before-it/00-report.md)) | | 17 | the controller is rebuilt from its own repository | and rolled out through the module path | the moment the mesh stops depending on the installer for anything | -| 18 | `networking` is assigned **and the node placed** | a private network, and names | assigning installs the module; placing says where this machine is on it. Both, or the names file is written empty | +| 18 | `mesh-wireguard` is assigned **and the node placed** | a private network | assigning installs the module; placing says where this machine is on it. Both, or the machine has no peers. Assigned by its own name since [ADR 0226](../../02-DECISIONS/0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md) retired the `networking` bundle | | 19 | the packet filter is assigned | rules generated from what modules declared | until this, every rule the mesh computes has never been applied to anything | ## Phase three — machines arrive @@ -147,7 +148,7 @@ two. | 5 | `builder` | — | turns source into artifacts | | 6 | `mesh-tools` | build inputs | the base everything with code compiles against. **Runs nowhere** | | 7 | `mesh-catalog` | the module graph | what is held, what a change reaches, what must be rebuilt | -| 8 | `networking` | `private-network`, naming | requirements only — assigning it brings `mesh-wireguard` and `mesh-names` | +| 8 | `mesh-wireguard` | `private-network` | the private network, computed per machine by the controller; assigned directly ([ADR 0226](../../02-DECISIONS/0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md)) | | 9 | `dnsmasq` + one of `resolved-split-dns` / `resolv-conf` | `wildcard-resolution` | names that actually resolve, on top of `mesh-resolver`'s data | | 10 | `step-ca` | `acme-ca` | certificates for `.internal` | | 11 | `firewall` | *claims* `the-packet-filter` | rules generated from what modules declared |