Under the hood
Helm is a multi-app desktop + cloud system: a player-facing launcher kiosk, an operator console, an agent on every station, and a shared cloud receiver. Below is the actual shape of what ships, the gates that protect it, and the gaps we have not closed yet. We would rather show this page than have you find the gaps on your own.
Component map
Four runtime components, one event spine, one brand pipeline.
┌─ Launcher (kiosk per station) │ WPF · .NET 8 self-contained │ Tile grid, game detail, ranks + challenges, leaderboard, │ membership checkout, recovery UI │ Pulls its game catalog from the cloud at start (offline cache) │ Emits: GameLaunchAttempt / GameLaunched / GameLaunchFailed / │ AbandonedLaunch / MemberIdle / PerformanceSample / ... │ ├─ FrontDesk operator console (one per venue) │ WPF · .NET 8 · 5 pillars (Time / Members / Licenses │ / Software / Payment) + Overview │ Drill-down drawers, smart segments, settlement view, │ inline confirm strip for destructive ops │ ├─ Floor View + Crew Tools (wall display / crew screens) │ Live station board with morning-check self-heal summary; │ per-player profiles with real match history + crew notes. │ Both label sample data as sample data - never a faked floor. │ ├─ Agent (per station, runs as SYSTEM) │ PowerShell + scheduled tasks │ Cleanup, reconcile, telemetry sync, autologin guard, │ HMAC-signed job surface with replay protection │ └─ Cloud (multi-tenant) ASP.NET Minimal API · SQLite (Postgres path planned) /events ingest · /matches · game catalog + venue packs Ranks, challenges + seasons, licensing pool, members, billing, venue pricing
Event spine
Every meaningful action emits a typed event. The launcher writes
to a local SQLite event store; a background uploader batches to
the cloud's POST /events. The cloud serves the
operator console (live activity feed) and downstream dashboards
off the same store.
Event types in flight today:
- Launch lifecycle:
GameLaunchAttempt→GameLaunched|GameLaunchFailed→GameSessionEnded. All four share anAttemptIdjoin key so the cloud can compute per-attempt success rate without inferring from timing. - Silent-crash detector:
AbandonedLaunchfires when an attempt produced no process within 90 s - catches the gap a typed failure can't see. - Member presence:
MemberCheckedIn,MemberCheckedOut,MemberIdle,MemberReturned. - UI interaction:
TileClicked,AppLaunched,ForegroundAppChanged. - Platform health:
PerformanceSample(CPU, memory, every 60 s),ControllerConnected/Disconnected/BatteryLow(XInput-driven, 5-min cadence, edge-debounced),NetworkSample,LauncherStarted/ShuttingDown. - Day-rollups (cloud-side): a background runner
scans the event stream into
event_projections_dailyunder three projection kinds today -event_counts_daily(per-station per-type counters),station_utilization(sessions + minutes played + members seated), andgame_popularity(per-game launches + minutes + abandoned). The Insights tab in the operator console renders all three. - Crew signal:
CoachAttentionRequested- raised when a player clicks "Tell the crew" on the launcher's recovery overlay, with GameId + Source. The operator console surfaces these in the Needs-Attention rail. - Match results: per-game adapters (replay-file
watcher, official game APIs, on-screen reading) publish typed
match summaries through one relay. Every adapter feeds the same
sink: local
matches-*.jsonljournal, thenPOST /matchesto the cloud, where results move ranks, challenges, and the venue leaderboard. No manual score entry anywhere in the pipeline.
Brand pipeline (the white-label seam)
Every WPF app reads VALHALLAN_BRAND at startup and
merges a brand-specific ResourceDictionary before the window
constructs. Palette, wordmark, logo, even motion accents come
from there. The same binary runs Valhallan or Helm; nothing in
the apps hardcodes a venue color or copy string. One source
file (Shared.Design/DesignLanguage.cs) is
link-compiled into every WPF project so motion durations, easing
curves, and depth shadows stay identical across the suite.
Verify gates
Hard gates run before every release, as scripts anyone on the team can run:
Verify-Demo.ps1- invariants on a cold-start demo cloud (members seeded, licenses queued, refund flow, tenant isolation).Verify-Operator-Console.ps1- static checks on the operator console's surface area (every drawer wired, every handler present, no MessageBox, cmd-K palette + dispatch, stats card, all five pillar tabs, build clean).Verify-Launcher-Catalog.ps1- catalog gates on the seeded pack (every tile resolves to a runnable adapter, every platform launcher configured, override modes valid).Verify-Telemetry.ps1- hard + soft gates on the live event pipeline (every expected event type fired today, per-PC freshness, cloud ingestion latency).
Unit tests
1,887 tests green across five .NET projects (run fresh for this update, July 3, 2026):
Valhallan.Cloud.Tests- 593. Game licensing state machine, billing, refunds, tenant isolation, game catalog + venue packs, match ingest, ranks + challenge progress, seasons, fleet power scheduling, pricing rules, projection runner.Valhallan.Launcher.Tests- 750. Launch service (typed outcomes, dedupe, process probe), game-session tracker, match-telemetry relay + per-game adapters, ranks and leaderboard VMs, membership checkout, idle presence watcher, abandoned-launch watcher, settings VM.Valhallan.Shared.Tests- 313. Event wire-shape round-trips, match summary shapes, challenge models, request-signing + nonce (replay) protection.Valhallan.FrontDesk.Tests- 141. Pillar tabs, command palette, settlement + licensing flows.Valhallan.Agent.Tests- 90. Heartbeat, status reporting, job surface auth + replay protection.
What we have not built yet
We would rather you know now than discover later:
- Strict auth is an ops flip, not yet flipped. Every client signs requests (HMAC) and every signer mints nonces; the agent's job surface rejects replays. What remains is operational: provision per-tenant secrets on every box, deploy one build, then enforce. Until that flip, the cloud accepts unsigned requests inside the venue LAN.
- Payments are live for membership, simulated for the rest. Membership checkout at the seat tokenizes the card straight to Stripe (the card number never touches our cloud) and has run a real charge end to end. Time top-ups and franchise revenue share are built and audited against a simulated gateway while connected-account onboarding lands.
- HQ rollup is not built. The cloud is multi-tenant - every request carries a tenant, isolation is enforced in the data layer and tested - and one cloud serves a venue today. The cross-venue dashboard that aggregates a whole franchise is designed but not shipped.
- Database is SQLite. Schema migrations run on cloud startup. A Postgres provider path is wired but not switched on - switching is one DI rebind.
- No CI pipeline yet. Verify gates and the 1,887 tests exist as scripts anyone can run; they have not been wired into a hosted CI yet.
- Phone view of Floor. Wall display and front desk are shipped; a phone-sized floor view is on the roadmap.
- Console curation of the game catalog. The catalog lives in the cloud and every station pulls it, but adding or toggling games is an API call today; the operator console screen for it is in progress.
If you want to run it
The portable demo bundles the full suite into a single
self-extracting helm-demo.exe (~469 MB). It runs
a self-contained cloud + console + launcher with seeded data on
a clean Windows path. Email
founders@runhelm.app
for a build and the walkthrough script.