Facts carry the format as a template, so the control plane holds none (ADR 0120)
A roster fact used to be a name from a closed list, each formatted in Go here — node-names as a hosts file, node-zones as a resolver's zones. Every new consumer (ssh's known_hosts, an authorized_keys) meant another formatter in the control plane, in the consumer's own configuration language. Now a fact is a path and a Go template over the roster view (this node, the suffix, and every served name vs the machines). The mesh owns the data; the module owns the format. /etc/hosts is a template on the network module; dnsmasq's zones move to dnsmasq. The controller renders and reads neither. WireGuard stays a computed generator: the overlay is the substrate delivery rides on, and its config is topology, not a roster projection. Output is byte-for-byte unchanged, pinned by the hosts golden tests and the resolver tests that compose the real dnsmasq manifest.
This commit is contained in:
@@ -366,24 +366,29 @@ type Manifest struct {
|
||||
// firewall does. Ignored on a converged node, whose derived filter already closes them.
|
||||
Guards []int `json:"guards,omitempty"`
|
||||
|
||||
// Facts are things only the mesh knows, written where this module asks for them.
|
||||
// Facts are things only the mesh knows, written where this module asks for them — in the
|
||||
// module's own format.
|
||||
//
|
||||
// **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.
|
||||
// **The graph is the control plane's; the format is the module's.** The mesh knows which
|
||||
// machines exist, what they are called and where they are. Turning that into a name that
|
||||
// resolves, a peer that is reachable, a host a client trusts, is somebody's software — dnsmasq,
|
||||
// a resolver, a VPN, ssh — in its own configuration language, and the mesh has no business
|
||||
// knowing it. So a module gives a path and a template; the mesh renders the roster through it
|
||||
// and owns nothing of what the file says.
|
||||
//
|
||||
// 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.
|
||||
// This used to be a closed list of fact names, each formatted in Go in the control plane, so a
|
||||
// new consumer meant a new formatter here in the consumer's language. Now the data is the mesh's
|
||||
// and the format is the module's: the two built-in cases — the network module's `/etc/hosts` and
|
||||
// dnsmasq's zones — render through the same template path any module uses, and no format lives
|
||||
// in the control plane at all. See RosterFile for what a template sees.
|
||||
//
|
||||
// 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"`
|
||||
// Keyed by a name the module chooses, which is the rendered file's id (`fact-<name>`) — what a
|
||||
// `restart-on` names to restart when the roster changes.
|
||||
Facts map[string]RosterFile `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.
|
||||
|
||||
Reference in New Issue
Block a user