With a head-assignment set, hydraheadflatscreen kills the active stream on macOS roughly every 60 seconds. The agent flips c.currentHost between the body's LAN IP and WireGuard IP on alternating ticks, then detects a "stream target changed" on the next tick and calls stopMoonlight() — which pkill -x HydraExperienceNet's the kiosk and any live stream subprocess.
5-minute stability test on peppy-dumpling-32 streaming rupelmonde-castle-viewer from boom-pickle-38 (LAN 10.110.0.51 / WG 10.10.100.7). Stream subprocess alive for ~15-30 seconds, then gone; stream status stays streaming in LocalAPI (stale) while the user sees the kiosk grid return. Log pattern repeats every 60s:
routing via LAN: 10.110.0.51
routing via LAN: 10.110.0.51
stream target changed: 10.110.0.51/rupelmonde-castle-viewer -> 10.10.100.7/rupelmonde-castle-viewer
pairing with Sunshine at 10.110.0.51...
launching kiosk: 10.110.0.51
pkg/client/client.go:147-211. The change-detection path uses stableHost (WireGuard IP) which is correct, but two fall-through assignments overwrite c.currentHost with the resolved (LAN-preferred) host:
c.currentHost = stableHost after launching.c.currentHost = host — the ResolvedHost, which will be the LAN IP when LAN is reachable.On the next tick host is still LAN but stableHost is WG. stableHost != c.currentHost → "stream target changed" → stopMoonlight() (which runs pkill -x HydraExperienceNet, killing both the kiosk and any stream subprocess).
Secondary issue: stopMoonlight() at pkg/client/moonlight_darwin.go:88-93 is the broad hammer that kills everything named HydraExperienceNet. On a no-op target change (LAN/WG flip), we should not be calling it at all; the fix above prevents that path being entered.
In pkg/client/client.go:209, change c.currentHost = host to c.currentHost = stableHost. Matches line 203 and the comparison at line 152.
Fix shipped: hydraheadflatscreen v2.0.25 (commit 50b9302). Verified on peppy-dumpling-32 streaming rupelmonde-castle-viewer from boom-pickle over LAN: 5-min stability test held the same stream PID (4784) from T+15s through T+303s with zero drops, zero stream-target-changed events, and the experience continued rendering interactive scenes to the end. Runbook updated with the symptom + version gate.