From 4ab5dd89a15288acf4280c7913ffb24ec4bedb0a Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Mon, 21 Sep 2026 14:17:55 -0700 Subject: [PATCH] Preserve configured Deskflow transport addresses across recovery Resolve fleet peers through explicit transport mappings and keep a controller's pinned address when the watchdog refreshes its state. Retain LAN lookup when no transport override is configured. --- slab/deskflow-handoff/README.md | 15 +++++++++------ slab/deskflow-handoff/deskflow-resolve-ipv4 | 17 +++++++++++++++++ slab/deskflow-handoff/deskflow-role-watchdog | 8 +++++++- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/slab/deskflow-handoff/README.md b/slab/deskflow-handoff/README.md index 53d98428d6..cc26a1fff1 100644 --- a/slab/deskflow-handoff/README.md +++ b/slab/deskflow-handoff/README.md @@ -69,8 +69,8 @@ mechanisms keep it honest, both in `deskflow-role-watchdog` (45s): against a dead address only loops. It acts only on a genuine change, so a server that is really offline does not get its conf rewritten every tick. - A **server** keeps the `address` in `~/.config/slab/deskflow-handoff.json` - matched to its live interface, so a trackpad claim fans out a reachable address - rather than the one it happened to hold at install time. + matched to its explicit `transportAddress`, or its live LAN interface when + none is configured, so a trackpad claim fans out the intended address. `serverName` is threaded through `deskflow-set-role`'s optional 4th argument by `claim-control`, `yield-control`, `retarget-client`, and `install.sh --server-name`. @@ -79,13 +79,16 @@ mechanisms keep it honest, both in `deskflow-role-watchdog` (45s): `grep remoteHost ~/Library/Deskflow/Deskflow-client-role.conf` on a client against `ipconfig getifaddr en0` on the server. -`deskflow-resolve-ipv4` prefers the `.local` form for bare names on purpose — via -MagicDNS a bare name can return the tailnet address of a long-offline node — and -discards loopback answers, since mDNS resolves a machine's own name to 127.0.0.1. +`deskflow-resolve-ipv4` first checks the handoff config's `transportPeers` map +(machine name without `.local` → explicit IPv4). Without a mapping, it prefers +the `.local` form for bare names — MagicDNS can return a long-offline namesake — +and discards loopback answers. Deskflow transport uses each machine's stable Tailscale address. On the Fuser Wi-Fi this keeps Chicken and Panda pointer latency far steadier than the direct -access-point route. Role-control SSH uses those addresses too; Bonjour `.local` +access-point route. `transportAddress` pins the local address and `transportPeers` +pins name resolution; both must stay aligned with the fleet registry. Role-control +SSH uses those addresses too; Bonjour `.local` resolution can select a stalled link-local IPv6 route after wake. Neo's existing `computer.aesthetic.deskflow-tailscale-ensure` agent heals a stopped tailnet before it can strand the clients. The peer controller is switched synchronously; diff --git a/slab/deskflow-handoff/deskflow-resolve-ipv4 b/slab/deskflow-handoff/deskflow-resolve-ipv4 index 711a7b0b4c..3c8e0ea48a 100755 --- a/slab/deskflow-handoff/deskflow-resolve-ipv4 +++ b/slab/deskflow-handoff/deskflow-resolve-ipv4 @@ -18,6 +18,23 @@ if [[ -z "$NAME" ]]; then exit 64 fi +# Explicit seat transport addresses take precedence over DHCP/mDNS. They are +# recorded per host, so a stale offline MagicDNS namesake is never selected. +configured=$(/usr/bin/python3 - "$HOME/.config/slab/deskflow-handoff.json" "$NAME" <<'PY' 2>/dev/null || true +import ipaddress, json, sys +with open(sys.argv[1]) as f: + peers = json.load(f).get("transportPeers", {}) +name = sys.argv[2].lower().removesuffix(".local") +address = peers.get(name, "") +if address: + print(ipaddress.IPv4Address(address)) +PY +) +if [[ -n "$configured" ]]; then + printf '%s\n' "$configured" + exit 0 +fi + # Screen names are inconsistent across the fleet — neo records `neo` while # blueberry records `blueberry.local` — so a bare name has to be tried both ways. # diff --git a/slab/deskflow-handoff/deskflow-role-watchdog b/slab/deskflow-handoff/deskflow-role-watchdog index 34927633b0..290d8a6fd4 100755 --- a/slab/deskflow-handoff/deskflow-role-watchdog +++ b/slab/deskflow-handoff/deskflow-role-watchdog @@ -29,7 +29,13 @@ SERVER_NAME=$(/usr/bin/python3 -c 'import json,sys; print(json.load(open(sys.arg # outage at the next handoff. Keep it matched to the live interface. refresh_local_address() { local live current - live=$(ipconfig getifaddr en0 2>/dev/null || ipconfig getifaddr en1 2>/dev/null || true) + # A configured transport address (e.g. Tailscale) must survive LAN changes. + live=$(/usr/bin/python3 -c \ + 'import json,sys; print(json.load(open(sys.argv[1])).get("transportAddress", ""))' \ + "$HANDOFF" 2>/dev/null || true) + if [[ -z "$live" ]]; then + live=$(ipconfig getifaddr en0 2>/dev/null || ipconfig getifaddr en1 2>/dev/null || true) + fi [[ -n "$live" ]] || return 0 current=$(/usr/bin/python3 -c \ 'import json,sys; print(json.load(open(sys.argv[1])).get("address", ""))' \ -- 2.51.2