| Path | Trigger | Event emitted | Body idle in |
|---|---|---|---|
| User taps Exit (iPad) | notifyStreamStopped → TerminatePending |
terminate_pending (amber, Success:true) |
≤5s |
| Admin stops body | DELETE /api/v1/nodes/{id}/stream |
stream_stop |
≤15s |
| Session watchdog | body stale >60s OR head-only stale >120s | terminate_pending (red, Success:false) |
≤125s |
| Sunshine self-closes (clean Moonlight disconnect) | body tick: streaming→idle | none — silent | ≤5s after Sunshine closes |
| Body goes offline while streaming | offline detection loop | none — silent | n/a (body is gone) |
closeSession calls emit no eventIn pkg/api/handlers_body.go:
closeSession(bodyID, "body_idle") — fires when body self-reports streaming→idle (Sunshine closed after clean Moonlight disconnect). No event.closeSession(bodyID, "body_offline") — fires when offline detection loop sees a streaming body go offline. No event.Fix: emit stream_stop at both call sites.
// after closeSession(nc.node.ID, "body_idle") — line 346
s.emitEvent(EventRecord{
Type: EventStreamStop,
BodyID: nc.node.ID,
BodyName: nc.node.Name,
Message: "Sunshine session ended — body self-reported idle",
Success: true,
})
// after closeSession(node.ID, "body_offline") — line 1020
s.emitEvent(EventRecord{
Type: EventStreamStop,
BodyID: node.ID,
BodyName: node.Name,
Message: "body went offline while streaming",
Success: false,
})
TCP port watchdog (polling 47984/47989/48010) was removed in hydrabody v2.0.59 due to NAT table false positives. No body-side mechanism now detects an orphaned stream where Sunshine is still running but no Moonlight client is connected. Fully dependent on hydracluster 120s head watchdog as fallback.
hydraheadipad v0.2.150 adds Heartbeat failed: <error> logging. Once deployed to fleet and a stream runs, logs will confirm whether silent heartbeat failures are causing premature watchdog kills. Check the log viewer (step 5c of testbook) after any session that ends unexpectedly.