HydraIssues

Kiosk overlay covers experience render after cold-boot stream start (boom-pickle 2026-04-24)
open bug Project: hydrabody Reporter: claude (cederik) 29 Apr 2026 18:53

Description

Symptom

On 2026-04-24 (Friday morning), starting the rupelmonde-castle-viewer stream on peppy → boom-pickle showed only the Hydra ExperienceNet branded splash on peppy, never the castle. /api/v1/stream/status reported streaming the whole time. Manually killing kioskoverlay.exe on boom-pickle (Stop-Process -Name kioskoverlay -Force) revealed the rendered castle immediately, proving Rupelmonde was rendering correctly and the kiosk overlay was the sole blocker.

The same flow worked on Wed evening (2026-04-22). The difference: today's attempt was a cold boot of boom-pickle.

Root cause

hydrabody's kiosk overlay has no mechanism to step aside when an experience is streaming. cmd/kioskoverlay/main.go:1017 places the overlay's main window at HWND_BOTTOM with WS_EX_TRANSPARENT (click-through), assuming Rupelmonde's new top-level window will naturally draw above it. That assumption holds when an operator interacts with Rupelmonde during a dev session (Wed) but not when Sunshine launches it cold via CreateProcessAsUserW in an autologin session — the new SDL window can land in background z-order against the long-lived, already-painted fullscreen overlay.

The stream webhooks POST /api/v1/stream/started and /stream/ended (pkg/provider/httpserver.go:54-104) call streamProvider.OnStreamStarted / OnStreamEnded but never touch the kiosk controller — pure design gap.

Verified on boom-pickle:

  • kioskoverlay.exe was launched without --debug (the topmost debug-window path is not involved).
  • Rupelmonde processes have MainWindowHandle=0 from .NET's perspective, but EnumWindows from Session 0 is blind to Session 2 — the window exists, just doesn't appear in cross-session queries.

Secondary bug

cmd/kioskoverlay/main.go:539 minimizeAllWindows() runs at overlay startup and SW_HIDEs every visible non-system window. If KioskController.Tick() (pkg/provider/kiosk_windows.go:180-186) ever relaunches the overlay during a stream — crash, session transition, manual kill — Rupelmonde's window gets SW_HIDE'd with no recovery (restoreDesktop only runs on graceful exit via the HydraBodyKioskStop named event, not on force-kill).

Proposed fix

  1. Add two named Win32 events symmetric to HydraBodyKioskStop: Global\HydraBodyKioskHide and Global\HydraBodyKioskShow. In cmd/kioskoverlay/main.go a goroutine waits on each and calls ShowWindow(overlayHwnd, SW_HIDE) / SetWindowPos(...HWND_BOTTOM...) + ShowWindow(SW_SHOWNOACTIVATE) on the main overlay hwnd. Store the overlay hwnd in a package-level var when createOverlayWindow creates it.
  2. KioskController.HideOverlay() / ShowOverlay() in pkg/provider/kiosk_windows.go that signal the events. No-op stubs in kiosk_other.go.
  3. Wire HideOverlay from handleStreamStarted (pkg/provider/httpserver.go:65) and ShowOverlay from handleStreamEnded (line 97). Mirror the call from the sunshine log-watcher path so both stream-detection paths trigger the same hook.
  4. minimizeAllWindows skip rule: skip windows whose owning exe is under experiencesBaseDir (C:\experiences\) — uses the constant already in httpserver.go:17.
  5. (Optional, preserves loading-screen branding) Don't hide on stream/started immediately. Hide only after Rupelmonde's first window appears: poll for non-zero MainWindowHandle on the experience process, or watch for Sunshine's first-frame log line. This way users still see the splash during the ~30s Unreal load.

Validation now possible

With the new GET /api/v1/debug/screenshot endpoint on the body (shipped in hydrabody v1.11.21), validation can be done end-to-end. Rerun the same stream-start and screenshot the body before/during/after — expect splash → castle → splash on the body's pixels. Without that endpoint, only peppy-side pixels were observable.

Open question

Confirm there's a single OnStreamStarted / OnStreamEnded hook point inside the sunshine provider so HTTP webhook + log watcher don't both fire HideOverlay redundantly.

Repro

  • node-74f9fbf2 (boom-pickle-38), bxl1/rupelmonde — currently offline (was the reproduction host)
  • node-4c2be4b0 (cosmic-pretzel-98), bxl1/cloud-seven — also affected; reproduces with the same kiosk_mode_enabled=true config (and currently has a stuck Windows Firewall popup for UnrealGame visible via the new screenshot endpoint)
  • hydrabody v1.11.20 at the time of incident; v1.11.21 ships the validation endpoint but does NOT fix the underlying overlay bug
  • HydraExperienceNet on peppy: bundle from Wed 2026-04-22 22:07 (Info.plist reports 6.1.0 but mtime confirms Wed-evening build)

Plan file

~/.claude/plans/enumerated-cuddling-micali.md (the body screenshot plan that just shipped) contains the full follow-up issue body and the implementation checklist for this fix.

Custom Fields

affected_repos
hydrabody
environment
production
severity
high