Files
jschoubben 75c104c355 Issue 112 diagnosis: correct located-in attribution
The carried-peer record (CarriedPeer/TunnelPeer) lives in mesh-controller
internal/inventory, not internal/catalogue. internal/catalogue is the
right package for the zone-generation side of the fix (facts.go's
nodeZones), but a different concern from where the name field itself
would go. Split the two so a decision record doesn't get pointed at the
wrong package.
2026-09-24 13:54:20 +02:00

4.4 KiB

Diagnosis

2026-09-24 — where the mesh's own record ends, and what stands beside it

Checked the mechanism first. The resolver module's config (mesh-catalog modules/dnsmasq) is generated whole from nodes.conf, the dnsmasq.fact-node-zones fact mesh-controller computes: one address=/<node>.<suffix>/<address> wildcard per node the mesh has a name for, plus local=/<suffix>/ — which tells dnsmasq it is authoritative for the whole suffix, so anything under it that is not one of those wildcards is refused, not forwarded. That is the exact mechanism the report describes: the mesh does not lack an upstream to ask, it has told itself there is nothing to ask.

Checked whether "forward to the resolver it replaced" (the third open question, ADR 0104's shape) is literal here the way it was for the proxy. It is not, and the difference matters: the proxy's predecessor kept running throughout its migration, a separate process on ports the mesh's proxy did not yet hold. DNS has one process on one port. mesh-catalog's dnsmasq module, on assignment, replaces /etc/dnsmasq.conf whole and restarts the one dnsmasq.service unit — the same unit the predecessor's own resolver is running as, right now, on this control-node:

$ systemctl status dnsmasq
● dnsmasq.service ... Active: active (running) since Fri 2026-09-18 ...

There is no predecessor process left standing after that assignment for a forward rule to reach. An adapter in ADR 0104's literal shape — a second thing running, written into by a first — does not fit; whatever answers this has to be data carried across the cutover, not a live thing forwarded to across it.

What is actually missing, and where it already exists

The report's first open question — should a carried peer be nameable, the operator saying which machine a carried address is, before it enrols — turns out not to need a guess. The predecessor's own resolver config, still live on this control-node, already states it:

# /etc/dnsmasq.d/hal-dns.conf — Generated by HAL dnsmasq-app
address=/novox.internal/10.10.0.1
address=/ace.internal/10.10.0.2
address=/g14.internal/10.10.0.4
address=/shanks.internal/10.10.0.3

Checked against what the mesh itself recorded when it took the tunnel over (overlay show):

a peer of the tunnel (zTKYP6Sz…) 10.10.0.2      not yet enrolled
a peer of the tunnel (XT1l25Z0…) 10.10.0.3      not yet enrolled
a peer of the tunnel (mq76YPW0…) 10.10.0.4      not yet enrolled

10.10.0.2/.3/.4 match ace/shanks/g14 exactly, one for one. This is not the operator inventing a fact under time pressure — it is the predecessor's own record, current, and it has been correctly serving these three names for six days without correction. Recording it is transcription, not assertion.

located-in, tentatively: mesh-controller internal/inventory (where a carried peer's record lives today — CarriedPeer/TunnelPeer in tunnel.go carry a public key and an address but no name field) and cmd/mesh-controller (a command to set it — nothing today lets an operator attach a name to a carried-tunnel-peer record; node add is for enrolling nodes, not naming peers, and node public-domain <name> <d> is the closest existing shape to model a new verb on). Separately, mesh-controller internal/catalogue (facts.go's nodeZones) would then need to emit an address= wildcard for a named carried peer the same way it does for a node — it renders only from the mesh's addresses map today, keyed by node name, and does not distinguish a named carried peer from an unnamed one. mesh-catalog modules/dnsmasq itself needs no change: it already restarts on the node-zones fact and would pick up the new wildcard the moment internal/catalogue emits it.

What is still open

  • The mechanism for recording the name is not designed — a new field on the carried-peer record, a new CLI verb, and what happens if the name later disagrees with what the peer states on enrolling (it should win; nothing says so yet).
  • Whether this generalises: the predecessor's static hosts file was the answer here because it happened to be readable and correct. A migration without one still has the report's harder second option (refuse the resolver until every peer enrols) as its fallback.
  • Not implemented. This diagnosis is what the fix would touch and why the ADR 0104 framing in the report's third option does not transfer directly — not the fix itself.