diff --git a/README.md b/README.md index c08b6a1c..c766c47b 100644 --- a/README.md +++ b/README.md @@ -80,13 +80,28 @@ To control this computer from your phone/tablet: ### 1. Configure Firewall Ensure your computer allows incoming connections on: -- **3000** (Frontend + Input Server) +- **3000/TCP** (Frontend and WebSocket signaling) +- **4000–4050/UDP** (WebRTC media and input channels) **Linux (UFW):** ```bash sudo ufw allow 3000/tcp +sudo ufw allow 4000:4050/udp ``` +**NixOS:** +```nix +networking.firewall = { + allowedTCPPorts = [ 3000 ]; + allowedUDPPortRanges = [ + { from = 4000; to = 4050; } + ]; +}; +``` + +Port `5004/UDP` is an internal loopback relay between GStreamer and Rein and +must not be exposed. + ### 2. Connect Mobile Device 1. Ensure your phone and computer are on the **same Wi-Fi network**. 2. On your computer, open the app (`http://localhost:3000/settings`). @@ -233,5 +248,3 @@ flowchart TD | **Client settings** | Sensitivity, scroll invert, and theme are stored in `localStorage` on the phone only — no server round-trip. | | **Server settings** | Port changes call `POST /api/config`, which writes `server-config.json`. The client redirects to the new port URL. The change is picked up on the next server start. | | **Input injection** | Input events arrive at the server via the DataChannel bridge, dispatched through `InputHandler` (throttle + validation), and injected at OS level via a virtual input device. | - - diff --git a/src/server/constants.ts b/src/server/constants.ts index 38c75069..19b36b69 100644 --- a/src/server/constants.ts +++ b/src/server/constants.ts @@ -22,5 +22,7 @@ export const MAX_KEY_LENGTH = 50 export const ACCEL_THRESHOLD = 1 export const ACCEL_FACTOR = 0.8 export const ACCEL_EXPONENT = 1.2 +export const ICE_PORT_MIN = 4000 +export const ICE_PORT_MAX = 4050 export const RTP_HOST = "127.0.0.1" export const RTP_PORT = 5004 diff --git a/src/server/webRTC.ts b/src/server/webRTC.ts index 75abeeb5..281bf70c 100644 --- a/src/server/webRTC.ts +++ b/src/server/webRTC.ts @@ -13,7 +13,7 @@ import type { InputMessage, InputConfig } from "./types" import fs from "node:fs" import { fileURLToPath } from "node:url" import { isKnownToken } from "./tokenStore" -import { RTP_HOST, RTP_PORT } from "./constants" +import { ICE_PORT_MAX, ICE_PORT_MIN, RTP_HOST, RTP_PORT } from "./constants" interface ClientSession { ws: WebSocket @@ -213,6 +213,7 @@ export class WebRTCManager { const pc = new RTCPeerConnection({ iceServers: [{ urls: "stun:stun.l.google.com:19302" }], + icePortRange: [ICE_PORT_MIN, ICE_PORT_MAX], codecs: { video: [ new RTCRtpCodecParameters({