Description
## Problem
When a Moonlight client disconnects ungracefully (e.g. head goes offline mid-stream), Sunshine on the body continues encoding with no receiver. The operator Stop button in the streaming monitor appears to succeed (stream_status clears to idle in hydracluster memory) but Sunshine keeps running.
Root cause: the manual Stop path goes streaming monitor → hydracluster DELETE /api/v1/nodes/{id}/stream → exec curl localhost:47991/api/v1/stream/stop on the body. If the hydranode exec channel is unresponsive or slow, the exec call times out. hydracluster always clears its in-memory stream_status regardless of exec outcome, so the UI shows idle while Sunshine keeps encoding — GPU stays pegged until the stale-session watchdog in hydrabody fires (~2 minutes).
Observed 2026-05-20 on chunky-turnip-23 (bxl1-test-3/ad6): GPU at 98% with stream idle/0 sessions. Exec timed out on the body. Stream self-cleared after ~4 minutes via the watchdog.
## The stale-session watchdog already handles this
hydrabody already has stale_session_watchdog_windows.go: if stream_status is streaming but no ESTABLISHED connection exists on Sunshine ports 47984/47989/48010 for more than 2 minutes, it calls OnStreamEnded and self-heals. This is the fallback that cleaned up the session.
The Stop button is only needed to clear the session faster than the 2-minute watchdog window.
## Potential solution (needs investigation)
Instead of going through exec, hydracluster handleNodeStreamStop (pkg/api/handlers_head.go) could call hydrabody's HTTP endpoint directly over WireGuard:
http://<body.WireguardIP>:47991/api/v1/stream/stop
Bodies already carry a wireguard_ip in their node record. This path bypasses the hydranode exec channel entirely and does not depend on the WebSocket being healthy.
Required changes:
- hydrabody: bind /api/v1/stream/stop on the WireGuard interface in addition to localhost, with bearer token auth
- hydracluster: handleNodeStreamStop tries the direct WireGuard HTTP call first; falls back to exec if wireguard_ip is empty
Needs investigation: whether the WireGuard IP is reliably populated for all body nodes at the time Stop is called, and what auth token to use for the direct call.