No description
Find a file
Natalie b8d41a9509 feat(@tools/net-tools): add dynamic lan host ip discovery
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 19:59:24 -07:00
bin feat(@tools/net-tools): add dynamic lan host ip discovery 2026-06-09 19:59:24 -07:00
data feat(@tools/net-tools): add dynamic lan host ip discovery 2026-06-09 19:59:24 -07:00
docs feat(@tools/net-tools): add mesh/lan tooling with host renderers 2026-06-09 19:53:08 -07:00
smart-lan-router feat(@tools/net-tools): add mesh/lan tooling with host renderers 2026-06-09 19:53:08 -07:00
.gitignore feat(@tools/net-tools): add mesh/lan tooling with host renderers 2026-06-09 19:53:08 -07:00
install.sh feat(@tools/net-tools): add mesh/lan tooling with host renderers 2026-06-09 19:53:08 -07:00
README.md feat(@tools/net-tools): add dynamic lan host ip discovery 2026-06-09 19:59:24 -07:00

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, two jobs. (1) Route: detect HOME (default gateway's MAC == lan.gateway_mac) → route 10.0.0.0/24 via the LAN interface (direct ~5ms); AWAY → via the wg mesh. (2) Name-sync: at home, discover each LAN host's current IP by MAC via ARP (stable MAC, drifting DHCP IP), write data/lan-state.json, and regenerate /etc/hosts (mesh-hosts-render) + the console user's ~/.ssh/config (host-apply). So ssh apricot/apricot.lan follow the host wherever DHCP puts it — no reservations. --status to inspect. Supersedes the old per-host /32 pinner, the wg-route-watchdog, and setup-lan-dns.
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.