net-tools/README.md
Natalie 03e47fc4df feat(@tools/net-tools): add mesh/lan tooling with host renderers
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 19:53:08 -07:00

4.6 KiB

net-tools

Mesh/LAN tooling for the four-host wg1 mesh + home LAN, built around one source of truth (data/mesh-hosts.json).

Components:

  • bin/ — renderers that project the source of truth onto each device: host-apply (ssh config), mesh-hosts-render (/etc/hosts), wg-dns-sync (apricot's mesh dnsmasq).
  • smart-lan-router/ — the policy-routing daemon that makes the LAN "smart": the laptop automatically uses the 5ms LAN path to home hosts when home, and the WireGuard tunnel when away — identity-gated so it never routes to a stranger at the same RFC1918 IP. (The home gateway is a dumb Xfinity box with no API; the intelligence lives here, on the client.)

Everything that needs a host address, MAC, or identity probe derives from one file: data/mesh-hosts.json. Never hardcode a mesh IP, MAC, or identity URL anywhere else — add it here and regenerate.

The four hosts — fruit family encodes machine class

Class Canonical Old alias LAN WG mesh Public
GPU compute (stone fruit) apricot 10.0.0.116 10.9.0.2
CPU / storage (pome) pear black 10.0.0.11 10.9.0.4
laptop (vegetable) fennel plum roams 10.9.0.3
cloud hub (citrus) yuzu vps,quinn-vps 10.9.0.1 89.127.233.145

Names are mid-migration (alias-first): the source of truth declares the fruit name canonical with the old name as an alias, and every renderer emits both, so pear.wg and black.wg resolve during the transition. Live infra (forge URL, NFS, ssh) still uses old names until the gated cutovers land — see docs/topology.md.

Naming: two views, one rule

The suffix is authoritative — a name is never ambiguous:

  • <host>.wg → mesh IP (10.9.0.x). Works anywhere the tunnel is up.
  • <host>.lan → LAN IP (10.0.0.x). Home network only.

(The old *.local scheme is retired — the platform moved to real .com domains and infra to .lan. net-tools carries no .local records.)

Tools

Tool Runs on What it does
bin/host-apply every host Renders this device's view of the fleet. Detects which host it is, then writes a managed ssh-config block (~/.ssh/config) with per-vantage HostNames: public > .lan (if this host reaches the LAN) > .wg. --whoami/--ssh-print/--ssh-diff/--ssh-apply. The hosts leg is mesh-hosts-render.
smart-lan-router/smart-lan-router.py fennel (laptop) LaunchDaemon. Detects HOME (default gateway's MAC == lan.gateway_mac) and switches the home /24: HOME → route 10.0.0.0/24 via the LAN interface (direct, ~5ms); AWAY → via the wg mesh (home reachable through the tunnel). One subnet route, normal ARP — drift-immune (any DHCP IP works) and free of the self-MAC bug. --status to inspect. Supersedes the old per-host /32 pinner and the wg-route-watchdog.
bin/wg-dns-sync apricot Renders mesh-hosts.json/etc/dnsmasq.d/wg-mesh.conf (host .wg + .lan records on 10.9.0.2:53, for wg clients with DNS=10.9.0.2). Idempotent; --dry-run.
bin/mesh-hosts-render any (esp. fennel) Renders a static /etc/hosts block for roaming clients. --print/--diff/--install.
smart-lan-router/ fennel com.lilith.smart-lan-router.plist (launchd) + install-smart-router.sh (installs it, retires the old loose copies).

All tools locate data/mesh-hosts.json by resolving their own symlink chain and walking up to the repo, so they work whether run from the repo or a PATH symlink.

Install

./install.sh                      # symlink bin/* into ~/bin or ~/.local/bin
sudo smart-lan-router/install-smart-router.sh   # install + start the LaunchDaemon (fennel only)

Changing addresses / hosts

  1. Edit data/mesh-hosts.json.
  2. apricot: sudo wg-dns-sync · roaming clients: sudo mesh-hosts-render --install.
  3. The daemon re-reads the file each cycle — no restart needed.

Never hand-edit /etc/dnsmasq.d/wg-mesh.conf or the managed /etc/hosts block — both are generated and overwritten on the next run.

Status

Consolidates previously-scattered tooling (the session-tools generators, the magic-civilization/scripts/lan resolver scripts, and the loose ~/bin/smart-lan-router.py daemon) into one repo. Pending gated cutovers (apricot DNS, the fleet rename, retiring originals) are in docs/topology.md.