790adb07a9
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.7 KiB
4.7 KiB
REPORT — agent v0.21.0: agent-managed split-horizon LAN resolver (2026-06-11)
Repo: felhom-agent · Version: 0.21.0 · Pushed commit: a43e981 · paired with
felhom-controller v0.42.1 (real wildcard cert — the GATE this depends on).
What shipped — internal/lanresolver
LAN clients reach their guest directly at the same public hostname with the same real wildcard cert (no Cloudflare hairpin), via a host-side dnsmasq the agent manages. The host is the stable anchor (static LAN IP); the guest stays DHCP/ephemeral and the agent tracks its live IP.
- Renderer — a base drop-in (
/etc/dnsmasq.d/felhom-resolver-base.conf:bind-interfaces,listen-address=<host-LAN-IP>+127.0.0.1,no-resolv,server=<upstreams>) + a per-customer drop-in (felhom-<customer-id>.conf:local=/<domain>/+address=/<domain>/<guest-ip>). The proven two-line shape:local=makes dnsmasq authoritative for the zone so AAAA → NODATA (no Cloudflare-AAAA split-brain — the guest has only link-local v6);address=is the wildcard A; everything else (and its AAAA) forwards upstream unchanged. Manager—EnsureDnsmasq(apt-install if absent, base config,systemctl enable --now),ReconcileGuest(discover live IPv4 viapct exec <vmid> -- ip -4 -o addr show dev eth0; discover domain from the guest controller's pulledcontroller.yaml— the v2 bootstrap omits it; write-if- changed;systemctl reload),Remove(decommission). Tolerates the early-boot pre-lease window (empty IP → skip+retry, never a blank record); logs IP transitions. Never touches/etc/resolv.conf.Loop— a 7th daemon goroutine; every interval (default 300s) enumerates provisioned guests (/var/lib/felhom-agent/guests/<vmid>/) and reconciles each, so the resolver follows DHCP IP moves.- Config
lan_resolver.{enable,host_ip,upstreams,interval_seconds,state_dir}(host_ip defaults to the local-API bridge IP).--selftest=lanresolver -vmid N. NewFELHOM_DNSMASQsudoers alias.
2A spike findings (host environment — all green, no blockers)
:53is FREE on the host (no systemd-resolved/dnsmasq/named).- Host IP is STATIC —
vmbr0 inet static 192.168.0.162/24(the stable anchor the router points at). - Host DNS intact —
/etc/resolv.conf(→ Pi-hole192.168.0.250) is untouched; the host resolves upstream for itself independent of the dnsmasq we add for LAN clients. - Domain sourcing —
pct exec <vmid> -- docker exec felhom-controller cat …/controller.yaml→customer.domain(no new credential).
Live validation (felhom-pve, guest 9201 = demo-felhom)
--selftest=lanresolver -vmid 9201: installed dnsmasq, wrote base + per-customer config, discovered IP192.168.0.151+ domaindemo-felhom.eu, dnsmasq listening on192.168.0.162:53.- Full loop from dooplex (real LAN client):
felhom.demo-felhom.eu A → 192.168.0.151, AAAA → NODATA,example.com → forwards, real cert200 ssl_verify=0. - IP-change tracking: poisoned the drop-in with a stale IP → reconcile detected the live IP differs, re-rendered + reloaded → corrected.
- Daemon loop runs (
lanresolver: enabled host_ip=192.168.0.162 interval_s=300); gracefully skips a not-yet-leased / absent guest. - Box-down: with dnsmasq stopped, the primary (
192.168.0.162) refuses; a secondary (1.1.1.1) still answers normal names — so a client configured[host-IP, secondary]fails over to the Cloudflare path; recovery confirmed on restart (dnsmasq isenabled → starts on boot).
Install step (the one irreducible manual action) + robustness
- One-time: set the customer router's DNS servers to [host-IP (192.168.0.x) primary, an upstream
secondary (e.g. 1.1.1.1)]. After that, split-horizon is automatic forever — new apps auto-resolve
locally (the wildcard
address=covers every subdomain) and serve the real wildcard cert. - Box-down: the secondary means a box reboot degrades to the public/Cloudflare (hairpin) path, not total DNS loss. Client DNS-failover is imperfect but standard; this is a required install config, not agent code.
- Locked ISP routers (can't repoint DNS): LAN-direct isn't achievable; the Cloudflare tunnel remains the graceful fallback. Documented, not forced.
- New host dependency the agent ensures: dnsmasq (installed/enabled by
EnsureDnsmasq).
Notes / follow-ups
- Stale guest state (
/var/lib/felhom-agent/guests/<vmid>/for a destroyed guest) makes the loop log a harmless per-tick skip; a decommission lifecycle should remove that dir + callManager.Remove. Left for the decommission slice. (Cleaned the one stale9200dir on the demo by hand.)