- streamer/windows: vash-broadcast.exe (setup/login/start/stop/status/doctor) with per-instance OBS, tosu and FFmpeg, a supervised worker, OCR room selection and OBS scene reconciliation; built and tested on Linux only - streamer/relay.ts shared with Vash TV; Cloudflare Access proxy for dev/staging - worker status validation and live score helpers for the site - STREAMING_HANDOFF.md and streamer/ALPHA_TEST.md record what is unverified Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
498 lines
38 KiB
Markdown
498 lines
38 KiB
Markdown
# Vash Esports: unattended broadcast setup and project handoff
|
||
|
||
Updated: 2026-09-29. This document records the work completed so far, the user's latest requirements, and the work still needed. **The unattended native Windows broadcaster is now implemented (`streamer/windows`, `vash-broadcast.exe`) but has not yet run on the Windows PC.** Section 0 describes it; sections 1–10 are the original handoff and still hold as requirements.
|
||
|
||
## 0. Implementation status (second session, 2026-09-29)
|
||
|
||
### What exists now
|
||
|
||
`bun run broadcast:build` produces `dist/vash-broadcast.exe` (cross-compiled from Linux). One program, driven over SSH:
|
||
|
||
```text
|
||
vash-broadcast setup --instance prod --server <url> --channel <id> --token <token> [--access-id … --access-secret …]
|
||
vash-broadcast login --instance prod # one-time spectator sign-in, via an SSH-tunnelled screen viewer
|
||
vash-broadcast login --instance dev --copy-from prod
|
||
vash-broadcast start|stop|restart|status|logs|snapshot|doctor --instance prod
|
||
vash-broadcast list; vash-broadcast remove --instance prod --yes
|
||
```
|
||
|
||
Design, as built (details in `streamer/README.md` section 3):
|
||
|
||
- **Per-instance everything** under `%LOCALAPPDATA%\Vash\Broadcast\instances\<name>`: portable OBS 32.2.2, tosu 4.26.2, FFmpeg 9.0.2 (all pinned by SHA-256 in `streamer/windows/install.ts`), a separate osu! folder (copied `osu!.exe`), own ports (block of 10 from 47100), logs, state. Stopping an instance kills only processes whose executable is inside its folder.
|
||
- **Desktop launch:** one scheduled task per instance (`Vash Broadcast (<name>)`, LogonType Interactive, normal priority, optional at-logon trigger) runs `bin\vash-broadcast-worker.exe task --instance <name>`. The worker copy is the same exe with the PE subsystem patched to GUI (no console window). `task` is a watchdog that restarts `worker` on crashes. CLI talks to the worker over `127.0.0.1:<control port>` with a per-run token (`runtime.json`).
|
||
- **Desktop helper:** `streamer/windows/desktop.ps1`, a long-running Windows PowerShell 5.1 process (C# via Add-Type): window enumeration by executable folder, focus, move, set title, screen capture, SendInput clicks/typing, Windows.Media.Ocr, locked-desktop detection, keep-awake.
|
||
- **Room selection:** OCR of osu!tourney's control panel (inverted, upscaled), click the line with the lobby's match id (room-name fallback only when unique), then confirm through tosu's `tourney.team.left/right`. Gameplay capture stays hidden in OBS until confirmed. The wiki screenshot shows panel lines as `<match id> : <room name> () n/16`.
|
||
- **OBS:** config files written before launch (user.ini FirstRun, websocket config.json, profile with Simple output `StreamEncoder=amd` → `h264_texture_amf`, service.json → local relay, crash sentinel cleared); scene reconciled over obs-websocket (window capture by exact unique title with `capture_audio`, browser overlay on top, desktop/mic muted, control panel cropped by 16:9 geometry). Encoder actually used is read from OBS's log after the stream starts; OBS's silent x264 fallback is reported as a warning.
|
||
- **Broadcast lifecycle:** `streamer/relay.ts` (extracted from Vash TV, now shared): Go live → session start → local RTMP listener → OBS StartStream; destinations + HLS recording/upload; End stream → OBS StopStream, final upload, session end.
|
||
- **Isolation limit found:** tosu scans every osu! process and uses the first tourney manager, so only one instance can run osu!tourney + tosu per PC. A host lock (`tourney.lock`) enforces it; others report `blocked`.
|
||
- **dev/staging behind Cloudflare Access:** Access service-token headers on API/websocket calls, plus a local overlay proxy (adds headers, injects `no-referrer` for the CDN's hotlink protection) because OBS's browser source cannot send headers.
|
||
- **Site:** agents may send `{type:'status', …}`; `src/lib/broadcast/worker.ts` validates it; the Broadcast list and Stream page show the worker state. The Stream page's Windows section now shows the `vash-broadcast` commands.
|
||
|
||
### Verified here (Linux)
|
||
|
||
- `bun run test:streamer`: 38 tests (room matching/OCR handling/tosu confirmation and launch rules against a fake desktop; OBS scene reconciliation, auth failure and timeouts against a fake obs-websocket; config isolation, ports, lock, supervisor backoff, PE patch, pinned download verification; relay lifecycle; VashLink; Access proxy; the worker's start → report → Go live → stop against a fake Vash).
|
||
- App: svelte-check 0/0, vitest 248 passed / 6 skipped, production build OK.
|
||
- Vash TV refactor run inside the existing `vash-tv` image: screen mode, relay mode, and the recording path (HLS segments, live + VOD playlists, poster, session start/status/end) all worked.
|
||
- The helper's C# compiled with Mono at `-langversion:5`; all PowerShell (helper + task scripts) parsed with PowerShell's parser. That is syntax only.
|
||
|
||
### Not verified (needs the Windows PC)
|
||
|
||
Everything Windows-specific has never run: PowerShell helper behaviour, scheduled-task start from SSH, tar/Expand-Archive extraction, OBS portable first start without dialogs, window capture of osu!tourney (WGC) with `capture_audio`, osu!tourney window layout/title stability, OCR accuracy on the real panel, clicks reaching osu!, AMD AMF, sign-in viewer, stay-awake, and the multi-hour soak. Also unconfirmed: osu! stable's login keys (`Username`/`Password` in `osu!.<user>.cfg`, used to detect a saved login), and whether `--copy-from` logins work across folders.
|
||
|
||
### Next steps on the real host
|
||
|
||
1. Build and copy: `bun run broadcast:build`, then `scp dist/vash-broadcast.exe stan@stan-win11:`.
|
||
2. On the PC over SSH: `setup` for one instance (prod needs no Access token; dev/staging need a Cloudflare Access service token). Set `OSU_TOURNEY_ACCOUNT` on the matching server to the spectator username.
|
||
3. `login`, then `doctor` (runs desktop checks through the task), then `start`, `status`, `snapshot`.
|
||
4. Run a real match; compare `snapshots/tourney-panel.png/.json` with what OCR read; fix whatever the first run shows, and record the results here.
|
||
|
||
## 1. The outcome the user wants
|
||
|
||
The user wants to run a program on their Windows PC, let it set up the broadcasting tools, and have it work automatically after that. They can start, stop, or restart it over SSH. They cannot spend time clicking through OBS, selecting osu! rooms, or repairing configuration for each stream.
|
||
|
||
Latest requirements, in the user's words:
|
||
|
||
> “I want a solution that I can just kinda run a program and it sets everything up.”
|
||
>
|
||
> “Once its started it needs to just work. This is also important once I move it over to the cloud.”
|
||
>
|
||
> “I dont mind having 3 instances to manage for dev, staging and prod. But I cant do any manual work per stream or anythign like that.”
|
||
|
||
**One-time setup is acceptable. Manual work per stream is not.** Do not continue by asking the user to manually create OBS scenes, select capture windows, switch tournament rooms, or start multiple helper applications for every match. Implement the missing automation.
|
||
|
||
The preferred operating model is:
|
||
|
||
1. Install/setup an environment once.
|
||
2. Supply its Vash server, channel, and agent token once.
|
||
3. Complete the spectator account's initial sign-in once if it cannot be automated appropriately.
|
||
4. Start one supervised worker for that environment.
|
||
5. The worker follows Vash, launches/restarts its dependencies, selects the correct lobby, supplies live scores, and handles the broadcast lifecycle automatically.
|
||
6. Operator controls and diagnostics are available through SSH and the Vash website.
|
||
|
||
The user is comfortable managing separate dev, staging, and production instances. This should mean separate instances of the same software with isolated configuration, rather than three bespoke setup procedures.
|
||
|
||
## 2. Hardware, access, and account decisions
|
||
|
||
### Streaming PC
|
||
|
||
| Item | Confirmed information |
|
||
| ------------------------ | ---------------------------------------------------------------------------- |
|
||
| OS | Windows 11, latest build according to the user; exact build was not supplied |
|
||
| CPU | Intel Core i9-12900KF |
|
||
| GPU | AMD Radeon RX 7900 XT |
|
||
| Hostname | `STAN-WIN11` / `stan-win11` |
|
||
| Desktop user | `STAN-WIN11\stan` |
|
||
| Desktop session | A user is signed in; confirmed using `Get-CimInstance Win32_ComputerSystem` |
|
||
| Current access | User is on a MacBook and can SSH into Windows |
|
||
| Graphical remote access | None currently available |
|
||
| Installed | OBS and normal osu! stable |
|
||
| Not installed/configured | A separate osu!tourney installation and tosu |
|
||
| Spectator account | **Use a separate spectator login**, explicitly chosen by the user |
|
||
|
||
The user declined WSL for this setup because they want straightforward access to their AMD GPU. **Native Windows is the current target.** OBS's AMD AMF hardware encoder is the intended starting point; verify actual encoder availability and successful output on the PC. Do not assume NVIDIA/CUDA tooling applies to this machine.
|
||
|
||
The desktop being signed in does not prove it is unlocked, has an active display, or can currently produce valid captures. Those conditions need a real host check. Launching GUI applications directly inside an SSH service session is not a substitute for launching them in the intended desktop session.
|
||
|
||
No working OBS launch, scene configuration, osu!tourney login, or gameplay capture on this Windows machine has been confirmed. A PowerShell example using an interactive scheduled task was provided, but the user has not reported running it successfully.
|
||
|
||
The user has not supplied the separate spectator account name or credentials. Do not copy their existing playing account's saved login into the broadcaster by default. osu!tourney needs an account with osu!supporter. Resolve the supported spectator-login arrangement for concurrent dev/staging/prod workers; do not assume the same account can stay independently logged into all three workers.
|
||
|
||
### Existing application server and repository
|
||
|
||
- Workspace: `/home/stan/dev/esports`.
|
||
- Branch: `dev`.
|
||
- Origin: `https://git.stanrunge.dev/stan/esports`.
|
||
- Linux server was renamed from `vash-prod` to `stan-server-hetzner`.
|
||
- User explicitly authorized `ssh ai@localhost` for Docker/sudo operations on this Linux server. This is **not** an SSH connection to the Windows PC.
|
||
- We do not currently have a confirmed route or login for connecting directly from this workspace to `stan-win11`. The user has Windows access from their MacBook.
|
||
- The development application runs with `bun run dev` on `127.0.0.1:3102`; it is not the production Docker container.
|
||
- During debugging, its terminal was stan's tmux session `main`, pane `%1`. Recheck before using this operational detail again.
|
||
|
||
| Environment | Website | Database |
|
||
| ----------- | --------------------------------------- | -------------- |
|
||
| Dev | `https://esports-dev.vash.software` | `vash_dev` |
|
||
| Staging | `https://esports-staging.vash.software` | `vash_staging` |
|
||
| Production | `https://esports.vash.software` | `vash` |
|
||
|
||
The streaming PC should connect to the selected existing website. It does not need a second copy of the Vash web server, the server's `.env`, or its IRC host credentials just to broadcast.
|
||
|
||
## 3. How we arrived here
|
||
|
||
### Match-playing page redesign
|
||
|
||
The original request was to take the lead on redesigning the match-playing page for a standard 1920×1080 desktop browser: clean, intuitive, playful, live, and clear about what each player should do.
|
||
|
||
Completed work includes:
|
||
|
||
- A compact desktop dashboard with a scoreboard, central phase-specific guidance, and progress through the match.
|
||
- Sidebar tabs for activity, maps, results, and picks, with less frequently needed controls behind other UI components.
|
||
- More compact map pool, match feed, and history components.
|
||
- Better final-result layout and winner wrapping.
|
||
- WebSocket-driven updates, visible connection status, and five-second snapshot polling while the socket is unavailable.
|
||
- Attention cues for a player's turn, including existing sound/notification behavior.
|
||
|
||
The desktop layout was visually checked in a 1920×1080 headless Chromium viewport earlier in this conversation and fit that viewport. Preserve this work while implementing broadcasting.
|
||
|
||
Key files include `src/lib/components/match/MatchDashboard.svelte`, its `Stage*` components, `Scoreboard.svelte`, `MapPool.svelte`, `MatchFeed.svelte`, `MapHistory.svelte`, `live.svelte.ts`, and `src/routes/(app)/matches/[id]/+page.svelte`.
|
||
|
||
### Streaming and cloud discussion
|
||
|
||
The user tried starting a stream and saw it waiting for a Docker container that never appeared in `docker ps`.
|
||
|
||
The important finding: the website's **Go live** action changes channel state; it does not provision a Docker container. Stream setup text was updated to explain this. Existing Windows/OBS and manual Docker paths were documented, but they still involve too much manual operation for the user's actual requirement.
|
||
|
||
Cloud hosting was explored, including Google Cloud, AWS, and Hetzner. The user pointed out that an osu! match can run for approximately five hours, so a short-lived 30-minute assumption or a one-hour GPU runtime limit is unsuitable. They then explicitly deferred cloud hosting and chose to use their Windows PC first.
|
||
|
||
Do not resume cloud provisioning now. Build an unattended worker with a reusable configuration and lifecycle so it can move to cloud infrastructure later. No cloud worker provisioning was completed. A preliminary Cloud Run integration was removed earlier in the work.
|
||
|
||
### Windows access clarification
|
||
|
||
Initial instructions assumed the user could operate the Windows desktop. They clarified they are on a MacBook with SSH access only. They confirmed `STAN-WIN11\stan` is signed in, then clarified the true requirement: a program that provisions and runs the broadcast stack automatically, with at most one-time manual setup.
|
||
|
||
The user has now explicitly chosen a **separate spectator login**. This is the latest account decision.
|
||
|
||
## 4. Repeated lobby creation: diagnosed and fixed
|
||
|
||
The user repeatedly created a real-lobby match against a bot. Bancho made multiple rooms until the account hit its four-room limit, while the match page continued saying it was waiting to open a lobby. Cancelling in Vash did not reliably clean up every room.
|
||
|
||
### Actual root cause
|
||
|
||
Logs showed successful room creation followed by `Joining #mp_<id> timed out`.
|
||
|
||
A controlled live Bancho probe established the exact ordering:
|
||
|
||
1. Vash sends `!mp make`.
|
||
2. Bancho automatically joins the host to the new `#mp_...` channel.
|
||
3. Bancho sends JOIN/NAMES responses.
|
||
4. Bancho sends the private “Created the tournament match” confirmation.
|
||
5. The old client discarded the earlier JOIN, sent another JOIN, and waited for a new echo.
|
||
6. Bancho ignores a redundant JOIN to a channel already joined, so the client timed out.
|
||
7. The old provider converted that failure into a capacity wait and the runtime created another room on retry.
|
||
|
||
An earlier partial fix also did not replace the live provider's old methods: the IRC provider is a process-wide singleton that survives Vite HMR. A real dev-server restart was required.
|
||
|
||
### Fixes now in the repository
|
||
|
||
- Track confirmed channel membership and remember Bancho's automatic JOIN.
|
||
- Make repeated/concurrent JOIN requests reuse the joined state or pending request.
|
||
- Accept end-of-NAMES as confirmation for an expected channel; clear membership appropriately on leave/disconnect.
|
||
- Make the fake Bancho server reproduce automatic JOIN before the creation PM and ignore redundant JOINs.
|
||
- Serialize `!mp make` per IRC account so one confirmation cannot resolve multiple concurrent requests.
|
||
- Reserve capacity for pending creation requests.
|
||
- Start the creation-response timeout after a queued command is actually sent.
|
||
- Stop further creation attempts on an uncertain result instead of making more rooms blindly.
|
||
- Preserve the configured room limit when Bancho reports full capacity; temporarily back off instead of permanently reducing the configured maximum.
|
||
- Reuse an already created/persisted room when saving or resuming setup is retried.
|
||
- Close a late-arriving room when cancellation wins the race with room creation.
|
||
- Wait for in-flight opening work when replacing a runtime during reload.
|
||
- Allow the due-match loop to resume a saved room even when no new room slots are free.
|
||
- Log actual opening errors distinctly from ordinary capacity waits.
|
||
|
||
### A second live bug: room closure
|
||
|
||
A real test found that sending `!mp close` and immediately leaving the IRC channel could leave the Bancho room open. The close path now waits for Bancho's closure acknowledgement before forgetting/leaving the room. An externally confirmed closure also releases local capacity.
|
||
|
||
The earlier broad “close every apparently orphaned room during runtime boot” cleanup was removed. Do not reintroduce it without handling startup/load failures and in-flight creation correctly; a failed actor load is not proof that its lobby is abandoned.
|
||
|
||
### Verification completed
|
||
|
||
- The updated provider opened a real Bancho test room in approximately 361 ms.
|
||
- Bancho acknowledged that room's closure.
|
||
- A fresh connection confirmed the closed room no longer existed.
|
||
- Known rooms from the cancelled test attempts were cleaned up during the controlled checks.
|
||
- The dev server was restarted and reported connected IRC, zero active matches, and four free slots on the configured account.
|
||
- Final pre-push suite: **246 passed, 6 skipped**, across 19 passing test files and one skipped file. The skipped tests were the database-backed auth integration suite.
|
||
- Streamer tests: **3 passed**.
|
||
- Svelte check: **0 errors, 0 warnings**.
|
||
- Production build passed; Sentry uploads were disabled for this local build.
|
||
- Windows and Linux streamer binaries compiled successfully.
|
||
|
||
These checks verify the lobby fix and existing build/tests. They do **not** constitute a real Windows gameplay/OBS broadcast test.
|
||
|
||
Relevant files:
|
||
|
||
- `src/lib/server/osu/irc-client.ts` and `irc-client.test.ts`
|
||
- `src/lib/server/osu/irc.ts` and `irc.test.ts`
|
||
- `src/lib/server/osu/fake-bancho.ts`
|
||
- `src/lib/server/match/runtime.ts`, `runtime.test.ts`, and `runtime.irc.test.ts`
|
||
- `src/lib/server/match/service.ts` and `create.ts`
|
||
|
||
**Restart the dev process after changing IRC client/provider methods. HMR does not refresh methods on an existing singleton instance.** This is documented in `README.md`.
|
||
|
||
## 5. Git state and completed commits
|
||
|
||
The user's request to commit all accumulated changes was completed. The broad change set included the match UI/runtime, broadcast functionality, authentication/account work, database migrations, development tools, and deployment configuration that were already in the workspace.
|
||
|
||
Both commits were pushed to `dev`:
|
||
|
||
| Commit | Description |
|
||
| --------- | ------------------------------------------------------------------------------------------------------------------------- |
|
||
| `9b9aafe` | `feat: redesign match play and add broadcast tooling` — broad accumulated change set, including the principal lobby fixes |
|
||
| `9d5a046` | `fix: resume saved lobbies when host capacity is full` — final recovery guard and regression test |
|
||
|
||
The workspace was clean and `dev` matched `origin/dev` immediately before adding this handoff document.
|
||
|
||
The initial HTTPS push failed because Git tried to use an unavailable VS Code askpass socket. The hostname change was not an obstacle to the successful SSH push. Gitea's SSH service is exposed on the Linux server at localhost port 2222. The successful push used:
|
||
|
||
```bash
|
||
git -c core.sshCommand='ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new' \
|
||
push ssh://git@localhost:2222/stan/esports.git dev
|
||
```
|
||
|
||
That localhost URL is specific to the existing Linux server. Do not tell the user to clone `localhost:2222` on their Windows PC or Mac. Their clone URL is:
|
||
|
||
```text
|
||
https://git.stanrunge.dev/stan/esports.git
|
||
```
|
||
|
||
Pushing `dev` did not deploy production. The current Gitea workflow deploys pushes to `staging`; production promotion is a separate manual workflow.
|
||
|
||
## 6. Existing broadcasting pieces and their limitations
|
||
|
||
### Vash website and director
|
||
|
||
- Broadcast channels follow one match, one tournament, or automatic match selection.
|
||
- Layouts and overlays are configurable; overlays are 1920×1080 browser pages.
|
||
- The director sends scene, match ID, lobby ID, room name, and team size to authenticated agents over `/ws/agent`.
|
||
- Agent tokens are generated from **Agent token on the Broadcast list page**. They are shown once; rotating one invalidates the old token.
|
||
- `src/lib/server/broadcast.ts` handles channels, agent connections, live score ingestion, and broadcast sessions.
|
||
- The API at `src/routes/api/broadcast/[id]/stream/+server.ts` provides desired live state, video settings, overlay URL, destinations, and recording configuration to an authorized worker.
|
||
- That API also accepts session start/status/end reports. Related endpoints handle recording uploads.
|
||
- `OSU_TOURNEY_ACCOUNT` in the server environment grants the configured spectator referee access during new-lobby setup. It has not been confirmed configured for the new separate spectator account.
|
||
|
||
### Existing Windows/Linux agent: `streamer/agent.ts`
|
||
|
||
What it currently does:
|
||
|
||
- Authenticates to a channel's `/ws/agent` connection.
|
||
- Receives which match/scene/lobby to follow.
|
||
- Reads tosu v2 WebSocket scores and forwards them to Vash, at most four times per second.
|
||
- Optionally switches OBS scenes through OBS WebSocket.
|
||
- `--setup-obs` creates a scene, an unconfigured window capture source, and an overlay browser source, then exits.
|
||
- Compiles into `dist/vash-streamer.exe` and `dist/vash-streamer` with `bun run streamer:build`.
|
||
|
||
What it **does not** currently do:
|
||
|
||
- Install tosu or provision an osu!tourney installation.
|
||
- Launch itself or dependencies into the Windows desktop from SSH.
|
||
- Automatically select the Windows osu!tourney room.
|
||
- Automatically choose the capture window created by `--setup-obs`.
|
||
- Configure OBS GPU output/streaming end to end.
|
||
- Start or stop OBS streaming based on the website's Go live state.
|
||
- Supervise/restart the entire native Windows process stack.
|
||
- Isolate three native Windows environments.
|
||
|
||
Do not advertise this executable as the finished unattended worker.
|
||
|
||
### OBS client: `streamer/obs.ts`
|
||
|
||
Implements OBS WebSocket 5 authentication and generic requests. It is small and reusable, but needs stronger lifecycle handling for unattended operation: authentication failure/close handling, connect/request timeouts, reliable readiness state, and reconnection. Currently `connected` is based on the underlying socket state rather than completed authentication.
|
||
|
||
### Tournament automation: `streamer/tourney.ts`
|
||
|
||
Currently **Wine/X11-specific**:
|
||
|
||
- Writes `tournament.cfg` with `TeamSize`, `acronym = VASH`, and capture height while the client is stopped.
|
||
- Launches osu! with Wine.
|
||
- Captures the lower room-selection panel using FFmpeg/X11.
|
||
- Uses Tesseract OCR to find the expected room name and xdotool to click it.
|
||
- Restarts the client when team size changes.
|
||
|
||
Its current process management, including `wineserver -k`, is not suitable for blindly managing multiple isolated workers on one host. Native Windows support and safe ownership of individual worker processes remain to be designed/implemented.
|
||
|
||
### Vash TV: `streamer/tv/main.ts`
|
||
|
||
- Existing standalone broadcast runner polls channel settings and follows Go live/End stream.
|
||
- Renders the Vash overlay through Chromium/Xvfb, or accepts an OBS RTMP ingest stream in relay mode.
|
||
- Encodes the rendered mode with CPU `libx264`; relay mode copies the incoming encoded stream.
|
||
- Sends to multiple destinations, reports status, and creates/uploads HLS recordings for site playback.
|
||
- The relay could be reused behind native OBS, but a native packaging/lifecycle path is not yet implemented.
|
||
- Linux assumptions such as `/tmp/vash-hls`, command names, process handling, and ingest binding need review before reuse on Windows or for multiple simultaneous instances.
|
||
|
||
### Docker images and docs
|
||
|
||
- `streamer/tv/Dockerfile`: Chromium/Xvfb/FFmpeg data-only renderer or OBS relay.
|
||
- `streamer/tourney/Dockerfile` and `streamer/tourney/start.sh`: Wine osu!tourney, tosu, virtual displays, VNC setup, agent, and compositor.
|
||
- The tourney container still needs a prepared persistent osu! installation, login, and tosu binary. Its complete real-gameplay path has not been validated on the user's host.
|
||
- These images currently have no configured AMD GPU acceleration path. Do not assume merely running the existing container on a GPU machine makes it use that GPU.
|
||
- `streamer/README.md`: existing manual Windows and Docker documentation.
|
||
- `streamer/WSL.md`: an earlier WSL-assisted setup guide. It remains in the repo but **is not the user's chosen path now**.
|
||
|
||
## 7. What must be built next
|
||
|
||
### A. One entry point for an environment
|
||
|
||
Provide a packaged native Windows launcher/worker with an understandable CLI. An illustrative interface is below; **these commands do not exist yet**:
|
||
|
||
```text
|
||
vash-broadcast.exe setup --instance dev --server <url> --channel <id>
|
||
vash-broadcast.exe login --instance dev
|
||
vash-broadcast.exe start --instance dev
|
||
vash-broadcast.exe status --instance dev
|
||
vash-broadcast.exe logs --instance dev
|
||
vash-broadcast.exe restart --instance dev
|
||
vash-broadcast.exe stop --instance dev
|
||
```
|
||
|
||
The exact names are a design choice. The requirement is a single runnable program, persistent setup, repeatable starts, actionable status, and no per-stream desktop work. Avoid making Bun/Git and a project build permanent operational requirements if a downloadable compiled release can do the job.
|
||
|
||
### B. Repeatable setup
|
||
|
||
- Detect OBS and the existing osu! stable installation, accepting explicit paths when detection fails.
|
||
- Provision a **separate** managed osu!tourney installation. Do not convert or overwrite the user's personal playing installation.
|
||
- Download/install a verified tosu release automatically.
|
||
- Install/package any additional capture, OCR, or relay dependencies actually needed by the selected implementation.
|
||
- Configure a managed OBS profile/scene collection or separate portable OBS installation without changing unrelated personal OBS settings.
|
||
- Set up authenticated local OBS control and persist per-instance settings/secrets locally.
|
||
- Create the overlay source and exact game capture, including audio, geometry/cropping, and output settings automatically.
|
||
- Select and verify a working hardware encoder on the RX 7900 XT. Report failure explicitly rather than silently claiming GPU encoding while using a software fallback.
|
||
- Re-running setup must preserve successful login/configuration and avoid duplicate scenes, tasks, sources, or processes.
|
||
|
||
### C. Resolve the one-time osu! login boundary
|
||
|
||
The user accepted a separate spectator login and tolerates genuinely one-time interaction. Official osu!tourney setup calls for signing into a separate stable installation, remembering the login, then adding `tournament.cfg` while osu! is stopped.
|
||
|
||
Determine how to offer that first sign-in from the user's current SSH-only position. Possibilities need evaluation; do not promise an unsupported fully headless sign-in. Provide a clearly bounded one-time remote setup path if graphical interaction is unavoidable. After sign-in, preserve the installation and login state between restarts.
|
||
|
||
Do not invent an osu! OAuth-to-stable-client login mechanism, store a spectator password in the Vash web server, or silently reuse the personal playing account.
|
||
|
||
### D. Desktop launch and supervision
|
||
|
||
- Use a supported interactive desktop launch mechanism, such as Task Scheduler with `LogonType Interactive`, for the signed-in Windows user.
|
||
- Make start/restart commands callable through SSH. Launch dependencies in the appropriate session with the correct working directories.
|
||
- Track processes owned by each instance. Stop/restart only those processes and their managed descendants.
|
||
- Restart failed OBS, tosu, tourney, agent, and relay components with bounded backoff.
|
||
- Detect missing/locked/unavailable desktop capture and present a useful status instead of looping forever or showing a false success.
|
||
- Ensure SSH disconnection does not stop the broadcast.
|
||
- Handle update/reboot recovery explicitly. “Auto-start at login” is not the same as “works after reboot before anyone logs in”; document and validate the chosen behavior.
|
||
|
||
### E. Automatic lobby following on Windows
|
||
|
||
This is a core missing capability. Investigate a supported room-selection/control path before choosing image-based automation. Initial research looked for tosu/tourney IPC or command support but did not establish such a path.
|
||
|
||
If Windows UI/capture/OCR automation is required:
|
||
|
||
- Target the managed osu!tourney window and the exact requested match.
|
||
- Use lobby ID when a supported interface exposes it. Room-name fallback must handle ambiguity; do not click the first available room.
|
||
- Handle room-list refreshes, startup delays, team-size changes, DPI scaling, window placement, and client restarts.
|
||
- Verify that the requested match is actually being spectated before reporting success.
|
||
- Retry with bounded, meaningful states and expose diagnostics/snapshots through the CLI.
|
||
- Do not make the user click a room for each match.
|
||
|
||
Vash already supplies `lobbyId`, `roomName`, and `teamSize` in the agent follow message. All Vash room names use `VASH: (Red) vs (Blue)` with each side's name truncated to 20 characters, matching the server's creation logic.
|
||
|
||
### F. Automatic OBS configuration and broadcast lifecycle
|
||
|
||
- Finish programmatic scene/input setup: select the right capture window, crop out the tourney control panel, position gameplay correctly under the overlay, and capture the intended game audio.
|
||
- Account for osu!tourney's multiple client windows and team sizes; verify the capture is the actual match layout, not just its controller window.
|
||
- Configure output profiles and the AMD encoder automatically once, then reconcile settings safely on later starts.
|
||
- Respect the channel's desired live state and existing stream configuration. Establish an explicit start/stop contract for the worker and the website.
|
||
- Support the existing recording/site playback/multistream requirements using a managed relay or another concrete implementation. A Windows agent connected to `/ws/agent` alone does not provide video to the site.
|
||
- Report worker readiness and failures clearly on the site. Distinguish worker offline, login required, waiting for lobby, spectating, streaming, reconnecting, and error.
|
||
- Handle End stream and process shutdown cleanly, preserving recordings and releasing resources.
|
||
|
||
The user should operate normal broadcasts through Vash; opening OBS should be a diagnostic or truly one-time action.
|
||
|
||
### G. Dev/staging/production isolation
|
||
|
||
Separate instances must have separate:
|
||
|
||
- Server origins, channel IDs, and agent tokens.
|
||
- Configuration/state directories and installation ownership.
|
||
- OBS profiles/collections or managed installations and WebSocket ports/passwords.
|
||
- tosu ports and reliable association with the intended osu! processes.
|
||
- Ingest endpoints if relays are used, recording folders, logs, process identifiers, and scheduled tasks.
|
||
- Restart/stop behavior that cannot affect another instance or the user's personal osu!/OBS session.
|
||
|
||
Do not assume giving tosu three different ports is sufficient. Verify that each tosu instance can be scoped to its corresponding tournament clients rather than reading every osu! process and mixing environments.
|
||
|
||
Concurrent spectator logins, matching lobby names across environments, and aggregate GPU/CPU/display requirements also need validation. Three separate workers are acceptable to the user; simultaneous operation must not be falsely advertised before these interactions are tested.
|
||
|
||
### H. Cloud migration later
|
||
|
||
Keep the desired-state protocol, channel authorization, status reporting, recording, and supervisor behavior portable. Keep platform-specific desktop/process/encoder handling behind a clear boundary.
|
||
|
||
Support matches lasting at least five hours without an arbitrary short timeout. Preserve setup and spectator login state appropriately. A future cloud VM may need a GPU driver, an interactive/virtual display strategy, and persistent state; containers alone do not solve those issues.
|
||
|
||
Choose cloud hosting and provisioning later. The immediate acceptance target is this native Windows AMD PC.
|
||
|
||
## 8. Useful research and verified interfaces
|
||
|
||
### Official references
|
||
|
||
- [osu!tourney setup](https://osu.ppy.sh/wiki/en/osu!_tournament_client/osu!tourney/Setup): separate installation, remembered login, supporter requirement, `tournament.cfg`, `TeamSize`, `acronym`, and capture height.
|
||
- [osu!tourney multiplayer usage](https://osu.ppy.sh/wiki/en/osu!_tournament_client/osu!tourney/Multiplayer_usage): room naming and client behavior.
|
||
- [OBS WebSocket protocol](https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.json).
|
||
- [OBS WebSocket README](https://github.com/obsproject/obs-websocket/blob/master/README.md): authentication and command-line overrides.
|
||
- [OBS launch parameters](https://obsproject.com/kb/launch-parameters).
|
||
- [OBS hardware encoding](https://obsproject.com/kb/hardware-encoding).
|
||
- [Microsoft scheduled task principals](https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/new-scheduledtaskprincipal): `Interactive` logon type.
|
||
- [tosu repository](https://github.com/tosuapp/tosu) and [wiki](https://github.com/tosuapp/tosu/wiki/).
|
||
|
||
OBS protocol requests checked during research include:
|
||
|
||
- `GetInputPropertiesListPropertyItems` for enumerating selectable capture targets.
|
||
- `SetInputSettings`, scene creation/item APIs, and `SetSceneItemTransform` for automatic capture/overlay setup.
|
||
- `SetProfileParameter` and `SetVideoSettings` for output configuration; validate when a restart/reload is required rather than assuming every setting applies immediately.
|
||
- `SetStreamServiceSettings` for RTMP service settings, and stream start/stop/status requests.
|
||
- `GetSourceScreenshot` for remote diagnostics.
|
||
- `GetVersion` for capability checks.
|
||
|
||
Do not assume these APIs alone solve room selection, stable account login, session isolation, or GPU readiness.
|
||
|
||
### tosu release observed during research
|
||
|
||
The official GitHub release API returned `v4.26.2` during this session:
|
||
|
||
- Windows asset: `tosu-windows-v4.26.2.zip`.
|
||
- URL: `https://github.com/tosuapp/tosu/releases/download/v4.26.2/tosu-windows-v4.26.2.zip`.
|
||
- Reported SHA-256: `d34c2bfc495959fcc79ff01f10b7dc9aea77eb508ed23c8f0d0fb8342773faa3`.
|
||
|
||
This is a research observation, not a completed installer implementation. Revalidate the chosen release when implementing, then pin and verify it rather than downloading an unexamined changing binary on every start.
|
||
|
||
Potential upstream source locations identified but not yet investigated fully:
|
||
|
||
- `packages/tosu/src/states/tourney.ts`
|
||
- `packages/server/utils/commands.ts`
|
||
- `packages/common/utils/config.ts`
|
||
- `packages/common/utils/config.types.ts`
|
||
|
||
Temporary downloaded reference files existed at `/tmp/vash-tosu-tree.json`, `/tmp/vash-tosu-release.json`, and `/tmp/vash-obs-protocol.json`. Treat these as disposable research artifacts, not project dependencies.
|
||
|
||
## 9. Acceptance criteria for the next implementation
|
||
|
||
The work is complete only when the actual Windows host demonstrates the following:
|
||
|
||
1. A repeatable setup command provisions missing dependencies and a separate tourney installation, with a clearly bounded one-time spectator sign-in if needed.
|
||
2. One SSH start command starts the intended worker in the correct desktop session and survives disconnecting SSH.
|
||
3. A real match gets exactly one lobby, and the broadcaster automatically selects it.
|
||
4. OBS captures the correct gameplay and audio, layers the Vash overlay, and actually uses the RX 7900 XT's hardware encoder.
|
||
5. Live scores reach Vash through tosu and the agent.
|
||
6. Starting/ending a broadcast follows the selected website control without desktop interaction.
|
||
7. The next match, including a different team size, is followed without manual room/window selection.
|
||
8. Component crashes and network interruptions recover automatically without duplicate workers or wrong-room capture.
|
||
9. Recording, destination status, and site playback work for the supported output path.
|
||
10. Dev/staging/prod instances do not share state, capture the wrong clients, or stop each other.
|
||
11. A multi-hour soak test covers the expected match duration and records actual CPU/GPU/memory behavior.
|
||
12. Status/logs clearly identify one-time setup requirements and recoverable failures through SSH or the website.
|
||
|
||
Useful existing verification commands:
|
||
|
||
```bash
|
||
bun run check
|
||
bun --bun vitest run
|
||
bun run test:streamer
|
||
bun run streamer:build
|
||
SENTRY_AUTH_TOKEN= bun run build
|
||
```
|
||
|
||
IRC tests open local TCP sockets. In the restricted agent sandbox, Vitest hung; running the tests with the proper host permissions worked. Git hooks run formatting/lint on commit and type checking/unit tests on push.
|
||
|
||
Use focused new tests for configuration isolation, recovery, process ownership, OBS request sequencing/timeouts, and room-selection confirmation. A successful Linux cross-compilation is not evidence that Windows desktop capture and GPU encoding work. Record exactly what was tested on the real host and what remains unverified.
|
||
|
||
## 10. Immediate next step
|
||
|
||
Resume implementation of the unattended Windows launcher and worker, starting with a concrete design for per-instance state, one-time spectator login, desktop launch, automatic room selection, and managed OBS output. Reuse the existing director, agent protocol, tosu parser, OBS client, and recording pipeline where appropriate.
|
||
|
||
At the point the user requested this handoff, research had begun, but **no new Windows launcher, bootstrap installer, native room automation, or process supervisor had been written**. Preserve the committed lobby/UI fixes and make the missing setup actually runnable. Avoid another sequence of instructions that leaves the user doing manual work for each stream.
|