From 6d9407a61f945dbefbbaa1ee7902af302934596b Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Sun, 10 May 2026 10:01:32 -0400 Subject: [PATCH] Add LAN-reachable mode to control surface and OSC bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the new ControlSurfaceLanReachable preference is on, both the REST/WebSocket control surface and the OSC bridge bind to all interfaces (http://+:port/ via HttpListener wildcard, IPAddress.Any for OSC) instead of loopback. The settings VM persists the toggle, restarts both surfaces when flipped, and surfaces a ControlSurfaceUrl computed from the first non-loopback IPv4 + a Copy button so operators can paste the URL onto a control PC. Use case: a headless host PC runs Teams + TeamsISO; a thin client on the same LAN drives it via /ui or a Stream Deck. Closed-network deployment, no auth — documented as a trusted-LAN-only mode in docs/CONTROL-SURFACE.md, including the one-time 'netsh http add urlacl url=http://+:9755/ user=Everyone' requirement and the firewall rule. --- docs/CONTROL-SURFACE.md | 54 +++++--- src/TeamsISO.App/MainWindow.xaml | 29 ++++- .../Services/ControlSurfaceServer.cs | 37 +++++- src/TeamsISO.App/Services/OscBridge.cs | 22 +++- src/TeamsISO.App/Services/UIPreferences.cs | 3 +- .../ViewModels/GlobalSettingsViewModel.cs | 123 ++++++++++++++++-- 6 files changed, 224 insertions(+), 44 deletions(-) diff --git a/docs/CONTROL-SURFACE.md b/docs/CONTROL-SURFACE.md index b7377d6..0d8cc7d 100644 --- a/docs/CONTROL-SURFACE.md +++ b/docs/CONTROL-SURFACE.md @@ -9,18 +9,42 @@ node-RED flows, command-line scripts) can drive it without a UI binding. 1. Open TeamsISO → Settings → DISPLAY tab. 2. Tick "Control surface (Stream Deck / Companion)". 3. Default port is **9755**; change it via the port textbox if needed. -4. The server binds to `127.0.0.1` only — it is NOT reachable from the LAN. - If you need LAN access (e.g. a Stream Deck on a separate control PC), - front it with `ssh -L 9755:127.0.0.1:9755` or a localhost TCP bridge. +4. By default the server binds to `127.0.0.1` only — it is NOT reachable + from the LAN. +5. To allow other machines on the same network to drive TeamsISO (the + "headless host PC + thin client" scenario), tick the nested + "LAN-reachable" checkbox underneath. The settings panel will display + the LAN URL (e.g. `http://192.168.1.42:9755/ui`) with a Copy button. When enabled, the toast confirms `Control surface listening on -http://127.0.0.1:9755/`. +http://127.0.0.1:9755/` (or the all-interfaces equivalent in LAN mode). + +### One-time setup for LAN-reachable mode + +Windows requires elevated permission to bind a non-loopback HTTP listener. +If you turn on LAN-reachable mode and don't see a connection from another +machine, run this **once** in an Administrator PowerShell (replace `9755` +if you've changed the port): + +```powershell +netsh http add urlacl url=http://+:9755/ user=Everyone +``` + +Also confirm the Windows Firewall is letting inbound traffic to that port +through — `New-NetFirewallRule -DisplayName "TeamsISO Control Surface" -Direction Inbound -Protocol TCP -LocalPort 9755 -Action Allow` +in an elevated PowerShell, or add it through Windows Defender Firewall → +Advanced Settings → Inbound Rules. ## Authentication -None. The localhost-only bind is the security model. Any process on the -operator's machine can hit these endpoints, which is the same threat model -as a Stream Deck's USB connection. +None — by design. In localhost-only mode, the loopback bind is the +security model: any process on the operator's machine can hit these +endpoints, the same threat model as a Stream Deck's USB connection. + +In LAN-reachable mode, the assumption is a closed/trusted network (a +production-control LAN, a dedicated show subnet, a private vlan). Any +machine that can route to the host on the listener port can drive +TeamsISO. **Do not enable LAN-reachable mode on an untrusted network.** ## Response shape @@ -222,7 +246,9 @@ Client→server messages are ignored for v1 — all commands go through REST. Same command surface, different transport. Enable the OSC bridge in the DISPLAY tab (default port **9000** — TouchOSC's default). Bound to -`127.0.0.1` only. +`127.0.0.1` by default; honors the same LAN-reachable toggle as the REST +surface — when LAN mode is on, OSC binds to `0.0.0.0` so a TouchOSC tablet +on the same network can talk to the host directly. Address vocabulary: @@ -266,11 +292,7 @@ on the appropriate endpoint above. ## Future work -- **OSC bridge** over UDP 9000 with `/teamsiso/iso {id} {0|1}` etc. — same - command surface, different transport. Adapter sits in front of the REST - handlers. -- **Bidirectional state** via WebSocket — push `participants` updates so - controllers can light a button when an ISO is live without polling. -- **REST apply-preset** — duplicate the dialog's apply logic into - `IIsoController.ApplyPreset(name)` so the `/presets/{name}/apply` - endpoint becomes a real action. +- **HTTPS / token auth** — for deployments that don't have a closed + network, layer TLS termination + a shared bearer token in front of the + HttpListener. Out of scope for v1; the LAN-reachable mode is a + trusted-network feature only. diff --git a/src/TeamsISO.App/MainWindow.xaml b/src/TeamsISO.App/MainWindow.xaml index 8d0a2f3..40d006c 100644 --- a/src/TeamsISO.App/MainWindow.xaml +++ b/src/TeamsISO.App/MainWindow.xaml @@ -1235,9 +1235,34 @@ - + ToolTip="Start an HTTP server so external controllers (Bitfocus Companion, Stream Deck plugins, Bome MIDI Translator) or a thin-client browser can drive TeamsISO."/> + + + + + + + + + +