The mesh's knowledge is a fact a module asks for, not three modules

mesh-names, mesh-resolver and the names half of the overlay generators are gone.
They ran no software and could not be swapped for anything, which is the test of
whether something is a module at all — they existed because computed output
needed somewhere to live, and the control plane's only shape for output was a
module.

Now a module says where it wants what the mesh knows:

  facts: { node-zones: /etc/mesh-resolver/nodes.conf }

and is given a file, under its own name, applied and removed like anything else
it declares. Two facts exist: node-names (a hosts file — exact names) and
node-zones (every machine as a wildcard, *.homer.internal is homer). Asking for
a fact the mesh does not compute is refused naming what would have worked,
because a daemon that starts and reads a file nobody wrote is a worse way to
find out.

The names ride with the network now: wireguard's manifest asks for node-names
into /etc/hosts, because being on the private network is what gives a machine a
name. networking no longer requires name-resolution — names are not a provision,
and the module that answered it ran nothing.

One behaviour inverted, deliberately: choosing another VPN used to drag
WireGuard in anyway, because only WireGuard provided the addressing the names
module required — the node-scope claim existed to at least make that loud. With
names as a fact there is nothing to drag in: tailscale assigned means tailscale,
alone. The claim still catches two VPNs assigned explicitly.

And a machine the mesh cannot place is left out of both files rather than named
at nothing: a name resolving to nothing hangs a connection, where an unknown
name fails at once and says so. In practice that is only ever a token issued and
not yet used — a machine that has announced itself has an address.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
2026-09-15 21:31:18 +02:00
parent 18ec632baa
commit fcdb065660
13 changed files with 348 additions and 344 deletions
+19
View File
@@ -315,6 +315,25 @@ type Manifest struct {
// that could only see its own ports would write a rule set that closed everything else.
Filtering *Filtering `json:"filtering,omitempty"`
// Facts are things only the mesh knows, written where this module asks for them.
//
// **The graph is the control plane's; how a machine uses it is the module's.** The mesh knows
// which machines exist, what they are called and where they are. Making a name resolve, or a
// peer reachable, is somebody's software — dnsmasq, a resolver, a VPN — and the mesh has no
// business shipping one, choosing which, or knowing its configuration language.
//
// So a module says *put the node names here* and owns everything after that. The same shape as
// `filtering`, generalised: a fact, and a path.
//
// It replaces three modules that existed only because computed output needed somewhere to
// live — they ran no software, could not be swapped for anything, and appeared in the graph as
// modules while being a data channel wearing a costume.
//
// Keyed by fact name; the names are a closed list, because a module asking for one the mesh
// does not compute is asking for something nobody will write, and finding that out on a machine
// is worse than being told here.
Facts map[string]string `json:"facts,omitempty"`
// Certificate is where this module wants a certificate for its machine's name inside the
// mesh, and where the key that goes with it can be found.
//