How the Remote Web Panel Works in Wand‑Enhancer: LAN Control Architecture

The Remote Web Panel in Wand‑Enhancer is a LAN‑only control interface that combines an HTTP server listening on TCP port 3223, a WebSocket protocol defined in web-contract.json, and an Electron IPC bridge to synchronize installed games and execute remote trainer commands from any device on your local network.

The Remote Web Panel in the k1tbyte/Wand‑Enhancer repository enables users to control the Wand application from any device on the same local network, such as a smartphone or tablet. This feature operates through a sophisticated three‑layer architecture that bridges the .NET patcher, Electron renderer process, and a web‑based UI. Understanding how this Remote Web Panel functions requires examining its HTTP server implementation, WebSocket contract, and the bridge scripts that facilitate inter‑process communication.

Three‑Layer Architecture Overview

The implementation is divided into three distinct layers that cooperate at runtime:

  • HTTP Server & WebSocket Bridge – An Express‑style server started by the .NET patcher that listens on TCP port 3223 and serves the compiled UI from web-panel/dist/renderer-scripts. It manages WebSocket connections following the contract in web-panel/protocol/web-contract.json.

  • Electron ↔ Wand Bridge – A TypeScript bundle (web-panel/dist/bridge.cjs) running inside Wand’s renderer process that uses electron.ipcRenderer to expose IPC channels for bidirectional communication between the remote UI and Wand’s internal services.

  • Remote Panel Scripts – JavaScript modules bundled into web-panel/dist/renderer-scripts that implement game synchronization, trainer status publishing, remote command handling, and UI cleanup logic.

HTTP Server and WebSocket Contract

Server Initialization in MainWindowVm.cs

The server lifecycle originates in WandEnhancer/View/MainWindow/MainWindowVm.cs. When the patcher initializes, it starts a lightweight HTTP server that binds to the LAN address on TCP port 3223 (the default remote-panel port). This server serves static files produced by the Vite build located in web-panel/dist/renderer-scripts, making the UI accessible via any browser on the local network.

Simultaneously, the server opens a WebSocket endpoint that adheres strictly to the protocol defined in web-panel/protocol/web-contract.json. The TypeScript type definitions in web-panel/protocol/messages.ts enforce message structures across the bridge.

Message Protocol Structure

The WebSocket contract supports several distinct message types that facilitate real‑time state synchronization:

  • hello – Transmits the initial snapshot containing the complete list of installed applications and the current trainer state upon client connection.
  • remote_command – Carries execution requests from the remote client to launch or stop specific trainers, identified by gameId.
  • game_status – Pushes real‑time lifecycle updates including game-launched, game-ended, and trainer visibility changes.

Electron Bridge and IPC Communication

Bridge Compilation and Injection

The bridge is compiled from TypeScript source using pnpm run build:bridge and output to web-panel/dist/bridge.cjs. When injected into Wand’s renderer process, it resolves critical services—including the installed‑apps service, trainer service, and store—via the Aurelia dependency injection container and Webpack’s runtime (runtime.js).

The bridge installs a polling timer controlled by SYNC_INTERVAL_MS (approximately 5 seconds) that periodically triggers syncInstalledApps() to rebuild the state snapshot. All error handling routes through WandEnhancer.log to ensure uncaught exceptions never crash the host application.

IPC Channel Bindings

The bridge establishes three primary IPC channels using electron.ipcRenderer:

  • BIND_CHANNEL – Registers a "set‑value" handler in Wand that the remote UI can invoke to modify application state.
  • SYNC_CHANNEL – Receives the snapshot constructed by syncInstalledApps() and forwards it over the WebSocket to connected clients.
  • COMMAND_REQUEST_CHANNEL – Intercepts remote execution requests and routes them to the trainer service via remote-commands.js.

The core entry point installInstalledAppsSync(WandEnhancer) in web-panel/bridge/scripts/default/installed-apps-sync/index.js orchestrates the bootstrap sequence. It implements a GLOBAL_FLAG check (globalThis.__wandInstalledAppsSync) to ensure the bridge installs exactly once, even if the injection script runs multiple times.

Remote Panel Scripts and Logic

Installed Apps Synchronization

The script web-panel/bridge/scripts/default/installed-apps-sync/installed-data.js collects raw installed applications from installedAppsService.installedApps and resolves missing metadata via Wand’s CDN. The buildSnapshot function aggregates this data with the Redux store’s myGames state to produce a comprehensive payload that includes diagnostic counts of raw versus catalogued games.

Game Status Monitoring

Defined in web-panel/bridge/scripts/default/installed-apps-sync/game-status.js, this module subscribes to Wand’s internal wand-remote-game-status channel. It translates native game lifecycle events into WebSocket game_status messages, ensuring the remote panel reflects trainer launch and termination in real time.

Remote Command Execution

The web-panel/bridge/scripts/default/installed-apps-sync/remote-commands.js module handles remote_command messages. When receiving a "play" request, it constructs a trainer launch request using trainerLaunchRequestCtor, instantiates it with the target gameId, and invokes trainerService.launch(...) to execute the trainer within Wand.

UI Cleanup and Artwork

Two scripts handle presentation logic:

  • remote-popup-cleanup.js – Executes on the UI side to rewrite tooltip URLs and QR code links. It hides the Pro‑onboarding card while preserving Wand’s existing QR renderer functionality.
  • artwork.js – Selects optimal game artwork by preferring CDN URLs in the format https://api-cdn.wemod.com/steam_community/<steamAppId>/client_icon/96.webp.

End‑to‑End Communication Flow

  1. Startup Phase – The patcher calls StartRemotePanel() in MainWindowVm.cs, initializing the HTTP server on port 3223. The Vite build serves the static UI bundle, and the bridge script injects into Wand’s renderer via installInstalledAppsSync.

  2. Client Handshake – A mobile device navigates to http://<pc-ip>:3223. The WebSocket connection establishes, and the client sends a hello request. The bridge responds immediately with the current installed_apps snapshot and game_status.

  3. Synchronization Loop – Every SYNC_INTERVAL_MS (≈5 seconds), the bridge polls Wand’s services, rebuilds the snapshot through buildSnapshot, and pushes updates via the SYNC_CHANNEL WebSocket frame.

  4. Command Execution – When the user taps "Play" on the remote device, the client emits a remote_command message. The bridge receives this on COMMAND_REQUEST_CHANNEL, creates the proper trainer launch object, and calls trainerService.launch(...). Lifecycle events propagate back through game_status messages.

  5. UI Maintenance – remote-popup-cleanup.js continuously sanitizes the interface, removing Pro upsell elements and ensuring QR codes render using Wand’s native utilities.

All communication remains confined to the local area network; no authentication tokens or personal data transmit externally. The lightweight design maintains compatibility with the ASAR‑patch pipeline.

Implementation Code Examples

Starting the HTTP Server

// In WandEnhancer/View/MainWindow/MainWindowVm.cs
private void StartRemotePanel()
{
    // Starts the LAN HTTP/WebSocket server on port 3223.
    RemotePanel.Start(port: 3223);
}

Injecting the Bridge Script

// Executed inside Wand's renderer process
import { installInstalledAppsSync } from "./bridge/scripts/default/installed-apps-sync/index.js";

if (typeof globalThis.__wandInstalledAppsSync === "undefined") {
  globalThis.__wandInstalledAppsSync = true;
  installInstalledAppsSync(WandEnhancer);
}

Processing Remote Play Commands

// web-panel/bridge/scripts/default/installed-apps-sync/remote-commands.js
export async function handleRemoteCommandRequest(state, _event, request) {
  if (request.type !== "play") return;

  const trainerCtor = state.trainerLaunchRequestCtor;
  const launchRequest = new trainerCtor(request.gameId);
  await state.trainerService.launch(launchRequest);
  state.log("info", "Trainer launched remotely", request.gameId);
}

Building the Application Snapshot

// web-panel/bridge/scripts/default/installed-apps-sync/installed-data.js
export function buildSnapshot(state) {
  const apps = state.installedAppsService?.installedApps ?? [];
  const catalogGames = state.storeRef?.getState()?.myGames ?? [];
  return {
    apps,
    catalogGames,
    diagnostics: {
      rawInstalledApps: apps.length,
      catalogGames,
    },
  };
}

Summary

  • The Remote Web Panel operates on TCP port 3223 and is strictly LAN‑accessible, requiring no external authentication.
  • The architecture consists of three layers: an HTTP/WebSocket server in MainWindowVm.cs, an Electron IPC bridge compiled to bridge.cjs, and specialized sync scripts in web-panel/bridge/scripts/.
  • IPC channels BIND_CHANNEL, SYNC_CHANNEL, and COMMAND_REQUEST_CHANNEL manage the flow of control messages, state snapshots, and execution commands.
  • The bridge uses a 5‑second polling interval (SYNC_INTERVAL_MS) to keep remote clients synchronized with Wand’s installed games and trainer status.
  • Remote commands execute through trainerService.launch() after constructing request objects via trainerLaunchRequestCtor, with full error isolation via WandEnhancer.log.

Frequently Asked Questions

What port does the Remote Web Panel use by default?

The Remote Web Panel binds to TCP port 3223 by default. This is hardcoded in the server initialization within WandEnhancer/View/MainWindow/MainWindowVm.cs, though the underlying implementation may support configuration changes if modified in the source.

Is the Remote Web Panel accessible from the internet?

No, the Remote Web Panel is intentionally LAN‑only. The HTTP server binds to the local network interface (127.0.0.1 or the machine’s local IP), and the implementation does not include authentication mechanisms or external routing capabilities. It is designed specifically for controlling Wand from devices on the same physical or Wi‑Fi network.

How does the bridge prevent duplicate script execution?

The bridge utilizes a global flag pattern via globalThis.__wandInstalledAppsSync. Before installation, the script checks if this property exists on the global object. If undefined, it sets the flag to true and proceeds with installInstalledAppsSync(WandEnhancer). If the flag exists, the installation skips, preventing duplicate IPC channel registrations and redundant polling timers.

How often does the panel synchronize game data?

The bridge maintains a synchronization loop using SYNC_INTERVAL_MS, which defaults to approximately 5 seconds. This timer triggers syncInstalledApps() to poll Wand’s services and push updated snapshots through the WebSocket, ensuring the remote UI reflects trainer launches, game terminations, and newly installed applications with minimal latency.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →