Two bugs found bringing the nyc3 segment live (citron hub + lime spoke): - Hub render ended in `[ -n "$miss" ] && echo`, which returns 1 when no spokes are unkeyed; under `set -e` that silently aborted `render_conf > tmp` on the apply path (spokes were fine — they end in printf). Use an if-block. - `wg syncconf <(wg-quick strip)` used bash process substitution but the script runs under /bin/sh (dash) — replaced with a POSIX temp file. Also: nyc3 endpoint -> citron's bound public IP (104.248.9.88), not the reserved IP (143.244.223.5) — DO routes the reserved IP in but WG replies from the primary, so the reserved IP can't be a WG endpoint without anchor source-routing. Verified live: lime<->citron handshake, ping 10.9.0.7 0% loss, citron dnsmasq resolving *.wg on 10.9.0.7. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
211 lines
No EOL
9.9 KiB
JSON
211 lines
No EOL
9.9 KiB
JSON
{
|
|
"_purpose": "Single source of truth for the wg1 mesh + LAN: the four hosts, their addresses on each path, MAC-based DHCP discovery, L7 health probes for `net doctor`, and the DNS records apricot's dnsmasq serves. Everything that needs a host address derives from here \u2014 never hardcode mesh IPs, MACs, or identity URLs elsewhere.",
|
|
"_schema": {
|
|
"hosts[].name": "Canonical name = fruit family encodes machine class (gpu=stone fruit, cpu=pome, cloud=citrus, laptop=vegetable, phone=berry).",
|
|
"fleet.enforce_hostname": "true => every agent converges its node's OS hostname to its canonical name (scutil on darwin, hostnamectl on linux). The FLEET renames hosts \u2014 never run hostnamectl by hand.",
|
|
"phones": "class=phone (berry family): no agent possible (ios/android run nothing); they are DNS clients \u2014 WireGuard app with DNS=10.9.0.2, names served by apricot's mesh dnsmasq (wg-dns-sync). ssh_user null => no ssh stanza rendered. os distinguishes ios/android. Enroll with wg-phone-add, then add the entry here. If the phone's per-SSID Wi-Fi MAC is pinned (iOS 'Private Wi-Fi Address: Fixed'), add mac to get home-LAN discovery too.",
|
|
"hosts[].aliases": "Old names, kept working during the alias-first rename. Renderers emit a record for name AND every alias.",
|
|
"hosts[].class": "gpu | cpu | cloud | laptop.",
|
|
"hosts[].wg/lan/public": "wg = mesh IP (10.9.0.0/24); lan = home LAN IP (10.0.0.0/24, null if roaming/no LAN leg); public = internet IP (null if none).",
|
|
"hosts[].mac": "LAN interface MAC \u2014 the stable key the daemon uses to DISCOVER the host's current DHCP IP via ARP (name-sync). null = not discoverable.",
|
|
"hosts[].identity": "L7 health probe for `net doctor` only (url with '{ip}' substituted, markers all required). null = skip service check. Routing uses subnet /24 + gateway-MAC fingerprint, not per-host identity.",
|
|
"services": "{host: [fqdn, ...]} \u2014 service vhost names that live ON a host and must resolve to that host's CURRENT LAN IP. Rendered by mesh-hosts-render with the discovered overlay, so they track DHCP drift. Add names here, never hand-edit /etc/hosts.",
|
|
"naming": "'<host>.wg' = mesh IP (explicit tunnel path); '<host>.lan' + BARE '<host>' = current LAN IP (direct at home; via tunnel when away, since the daemon routes the LAN /24 through wg then). Hosts without a LAN IP get bare name \u2192 wg IP. ('.local' is retired \u2014 platform uses .com, infra .lan.)",
|
|
"routing": "smart-lan-router.py (laptop role) routes the entire LAN /24 direct when HOME (gateway MAC match) or via wg when AWAY. No per-host /32 pins."
|
|
},
|
|
"_consumers": [
|
|
"bin/wg-dns-sync",
|
|
"bin/mesh-hosts-render",
|
|
"smart-lan-router/smart-lan-router.py"
|
|
],
|
|
"fleet": {
|
|
"enforce_hostname": true
|
|
},
|
|
"mesh": {
|
|
"interface": "wg1",
|
|
"cidr": "10.9.0.0/24",
|
|
"hub": "yuzu",
|
|
"hub_endpoint": "89.127.233.145:51820",
|
|
"dns_host": "apricot",
|
|
"dns_listen": "10.9.0.2:53",
|
|
"segments": {
|
|
"_note": "A segment = a WireGuard hub + its spokes (bin/wg-render). hosts[].segment names the segment a host belongs to; hosts[].wg_pubkey is its public key (never private). yuzu (iceland) and citron (nyc3) are independent stars. Hosts without a `segment` fall back to the legacy single hub (mesh.hub) in wg-render.",
|
|
"iceland": { "hub": "yuzu", "endpoint": "89.127.233.145:51820", "dns_host": "apricot", "dns_listen": "127.0.0.1,10.9.0.2" },
|
|
"nyc3": { "hub": "citron", "endpoint": "104.248.9.88:51820", "dns_host": "citron", "dns_listen": "127.0.0.1,10.9.0.7" }
|
|
}
|
|
},
|
|
"lan": {
|
|
"cidr": "10.0.0.0/24",
|
|
"dns_host": "pear",
|
|
"dns_listen": "10.0.0.11:53",
|
|
"gateway": "10.0.0.1",
|
|
"gateway_mac": "c4:4f:d5:5a:61:6f",
|
|
"gateway_note": "Xfinity broadband gateway. gateway_mac is the home-LAN fingerprint: the smart-lan-router daemon treats the laptop as 'home' only when the default gateway on the LAN interface has this MAC \u2014 distinguishes the real home LAN from any visited 10.0.0.0/24 network. DHCP reservations only via xFi/web UI, no scriptable API."
|
|
},
|
|
"dx": {
|
|
"hide_homelan": true,
|
|
"_note": "When true, renderers (mesh-hosts-render, host-apply) omit homelan hosts (apricot/pear/fennel + their LAN/.lan/bare names and services on them) from generated /etc/hosts and ~/.ssh/config. Only cloud/DO hosts (yuzu, lime + their public/WG) and dx-forges (separate) are shown. Full homelan data preserved in 'hosts'/'lan'/'services' for easy recovery \u2014 set false and re-render (net sync). We are currently DO-only for DX."
|
|
},
|
|
"hosts": [
|
|
{
|
|
"name": "apricot",
|
|
"aliases": [],
|
|
"class": "gpu",
|
|
"role": "Threadripper GPU compute \u2014 LLM serving, quinn dev, claude rc units, mesh DNS (dnsmasq 10.9.0.2:53)",
|
|
"os": "linux",
|
|
"ssh_user": "lilith",
|
|
"wg": "10.9.0.2",
|
|
"lan": "10.0.0.116",
|
|
"public": null,
|
|
"mac": "b4:2e:99:35:24:c5",
|
|
"identity": {
|
|
"url": "http://{ip}:8200/health",
|
|
"markers": [
|
|
"llama_service_available"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "pear",
|
|
"aliases": [
|
|
"black"
|
|
],
|
|
"class": "cpu",
|
|
"role": "Threadripper CPU/storage \u2014 Forgejo, Verdaccio, LAN DNS (dnsmasq 10.0.0.11:53), NFS/media",
|
|
"os": "linux",
|
|
"ssh_user": "lilith",
|
|
"wg": "10.9.0.4",
|
|
"lan": "10.0.0.11",
|
|
"public": null,
|
|
"mac": "b4:2e:99:30:a2:9a",
|
|
"identity": {
|
|
"url": "http://{ip}:3000/api/v1/version",
|
|
"markers": [
|
|
"version"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "fennel",
|
|
"aliases": [
|
|
"plum"
|
|
],
|
|
"class": "laptop",
|
|
"role": "MacBook Air M2 \u2014 roams (no fixed LAN IP), mesh client, runs the smart-lan-router daemon",
|
|
"os": "darwin",
|
|
"ssh_user": "natalie",
|
|
"wg": "10.9.0.3",
|
|
"lan": null,
|
|
"public": null,
|
|
"mac": "74:a6:cd:d4:b0:39",
|
|
"identity": null
|
|
},
|
|
{
|
|
"name": "lime",
|
|
"aliases": [
|
|
"lilith-store-backend",
|
|
"com.uvlava.ct.services"
|
|
],
|
|
"class": "cloud",
|
|
"role": "DigitalOcean backend node (nyc3, public IP 209.38.51.98 reached via ProxyJump yuzu / wg \u2014 no public app ports) \u2014 quinn.api INTERNAL (:3030), MCP gateways (:3910-3914), DO Managed PG (VPC), LISTEN/NOTIFY + private workers. Joins wg1 via phase-b-mesh-join.sh. IaC: uvlava/terraform/do.",
|
|
"os": "linux",
|
|
"ssh_user": "root",
|
|
"ssh_identity": "~/.ssh/id_ed25519_1984",
|
|
"segment": "nyc3",
|
|
"wg_pubkey": "f9ojTNSwvP4/4LxTyyZPG/KhqehQ7aWiSxhsU4dT10Q=",
|
|
"wg": "10.9.0.5",
|
|
"lan": null,
|
|
"public": null,
|
|
"mac": null,
|
|
"identity": {
|
|
"url": "http://{ip}:3030/healthz",
|
|
"markers": [
|
|
"ok"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"name": "redroid",
|
|
"aliases": [
|
|
"lilith-store-redroid",
|
|
"com.uvlava.ct.redroid"
|
|
],
|
|
"class": "cloud",
|
|
"role": "DigitalOcean redroid (containerized Android) host for screening automation (mr-number + whatsapp lookups). nyc3 under store vpc. wg leg 10.9.0.6. Services (adb:5555, ws-scrcpy:8000, wa ui:8011) locked to mesh only (ufw + bind to lo/wg IP; no public listeners). Reached direct from plum via key (ssh) or over mesh. Tray on plum provides secure tunneled localhost console UI. IaC: uvlava/terraform/do/redroid.tf.",
|
|
"os": "linux",
|
|
"ssh_user": "root",
|
|
"ssh_identity": "~/.ssh/id_ed25519_1984",
|
|
"wg": "10.9.0.6",
|
|
"lan": null,
|
|
"public": "45.55.191.82",
|
|
"mac": null,
|
|
"identity": null
|
|
},
|
|
{
|
|
"name": "yuzu",
|
|
"aliases": [
|
|
"vps",
|
|
"quinn-vps"
|
|
],
|
|
"class": "cloud",
|
|
"role": "1984 Hosting (Iceland) \u2014 WireGuard mesh hub, quinn production",
|
|
"os": "linux",
|
|
"ssh_user": "root",
|
|
"ssh_identity": "~/.ssh/id_ed25519_1984",
|
|
"wg": "10.9.0.1",
|
|
"lan": null,
|
|
"public": "89.127.233.145",
|
|
"mac": null,
|
|
"identity": null
|
|
},
|
|
{
|
|
"name": "citron",
|
|
"aliases": [
|
|
"com.uvlava.quinn.infra"
|
|
],
|
|
"class": "cloud",
|
|
"role": "DigitalOcean essential-services node (nyc3, store vpc) — DNS + WireGuard HUB for the DO/nyc3 segment (lime/redroid/artifacts peer to it; yuzu remains the Iceland-segment hub). dnsmasq + wg1 are OWNED by net-tools host-apply / wg-dns-sync (the cloud-init in uvlava/terraform/do/infra_host.tf is bootstrap only). Reserved hub endpoint 143.244.223.5:51820.",
|
|
"os": "linux",
|
|
"ssh_user": "root",
|
|
"ssh_identity": "~/.ssh/id_ed25519_1984",
|
|
"segment": "nyc3",
|
|
"wg_pubkey": "DgI6gbwkoiaUACl+RDkS5/lgcVbv6S/hCvDG3y/74xc=",
|
|
"wg": "10.9.0.7",
|
|
"lan": null,
|
|
"public": "143.244.223.5",
|
|
"mac": null,
|
|
"identity": null
|
|
}
|
|
],
|
|
"services": {
|
|
"_note": "Service vhosts hosted ON a fleet host \u2014 adopted from the loose hand-maintained /etc/hosts lines (quinn.* dev vhosts, lm/llm stack, forge/registry). Rendered at the host's CURRENT discovered IP.",
|
|
"apricot": [
|
|
"quinn.apricot.lan",
|
|
"www.quinn.apricot.lan",
|
|
"my.quinn.apricot.lan",
|
|
"admin.quinn.apricot.lan",
|
|
"ai.quinn.apricot.lan",
|
|
"api.quinn.apricot.lan",
|
|
"data.quinn.apricot.lan",
|
|
"m.quinn.apricot.lan",
|
|
"sso.quinn.apricot.lan",
|
|
"vip.quinn.apricot.lan",
|
|
"docs.quinn.apricot.lan",
|
|
"lm.apricot.lan",
|
|
"lm-api.apricot.lan",
|
|
"llm.apricot.lan",
|
|
"status.apricot.lan",
|
|
"redis.apricot.lan"
|
|
],
|
|
"pear": [
|
|
"forge.black.lan",
|
|
"registry.black.lan",
|
|
"forge.pear.lan",
|
|
"registry.pear.lan"
|
|
]
|
|
},
|
|
"dnsmasq": {
|
|
"_note": "Mesh DNS served by apricot's dnsmasq (bound 127.0.0.1 + 10.9.0.2), written to /etc/dnsmasq.d/wg-mesh.conf by bin/wg-dns-sync. Consumed by wg clients that set DNS=10.9.0.2 (phones). Renders the host .wg + .lan records from hosts[] \u2014 NOT platform service records. The old *.local platform domains are RETIRED (platform uses .com; infra uses .lan); they are deliberately NOT carried here.",
|
|
"listen_address": "127.0.0.1,10.9.0.2"
|
|
}
|
|
} |