<# .SYNOPSIS Add (or remove) the inbound Windows Firewall rules MeshBay needs. .DESCRIPTION Two independent things need an inbound allow, and Windows prompts "Allow access" for each the first time it happens if there is no rule: 1. WebRTC (MeshBay.exe and meshbay-node.exe). It binds an ephemeral UDP port per connection, and the browser always dials the node -- aioice cannot resolve the peer's mDNS `.local` candidate to call back -- so the node (and, during ICE connectivity checks, the client) must accept unsolicited inbound UDP. There is no fixed port, so these rules are scoped by PROGRAM with no LocalPort restriction, which is the Windows- native way to say "this binary, any port it happens to bind" -- the Linux packaging (packaging/firewall/) has to approximate the same thing with a broad 1024-65535/udp port range because netfilter has no equivalent program scoping. 2. LAN casting (MeshBay.exe only): the HTTP relay to a Chromecast/Smart TV (src/cast-relay.js, fixed TCP 19550-19553) and mDNS device discovery (src/cast-chromecast.js via bonjour-service, UDP 5353). Both scoped by program AND port -- narrower than the WebRTC rules, since these ports are fixed and known. Matches packaging/firewall/*/meshbay-cast.xml. The installer runs this once, elevated, so the user answers one UAC prompt instead of up to four dialogs spread across first use of chat, downloads and casting. Declining the installer's offer is fine -- the dialogs are the fallback, one per program/port combination as each is first used. Shipped as an extraResource at \resources\firewall.ps1, so it locates the two executables from its own path and takes no arguments beyond the action. add/remove run elevated and windowless, leaving a trace at %TEMP%\meshbay-firewall.log; check is a plain read (no admin needed -- Get-NetFirewallRule does not require it, only New/Remove do) that the installer runs first, unelevated, so re-running setup with the rules already in place skips the prompt and the UAC entirely. .PARAMETER Action add (default) create/replace the rules remove delete them check exit 0 if all rules already exist, 1 otherwise -- no admin needed #> [CmdletBinding()] param( [ValidateSet("add", "remove", "check")] [string]$Action = "add" ) $log = Join-Path $env:TEMP "meshbay-firewall.log" "[{0}] {1}" -f (Get-Date -Format s), $Action | Add-Content $log # This script sits at \resources\firewall.ps1. $resources = $PSScriptRoot $install = Split-Path -Parent $resources $GROUP = "MeshBay" $client = Join-Path $install "MeshBay.exe" $node = Join-Path $resources "node-runtime\meshbay-node.exe" # Program-scoped, any port: the ephemeral-UDP-port problem WebRTC always has. # Port-scoped as well: the two fixed, known ports LAN casting actually uses. $rules = @( @{ Name = "MeshBay"; Path = $client; Protocol = "UDP"; LocalPort = "Any" } @{ Name = "MeshBay Node"; Path = $node; Protocol = "UDP"; LocalPort = "Any" } @{ Name = "MeshBay Cast"; Path = $client; Protocol = "TCP"; LocalPort = "19550-19553" } @{ Name = "MeshBay Cast Discovery"; Path = $client; Protocol = "UDP"; LocalPort = "5353" } ) if ($Action -eq "check") { $missing = $rules | Where-Object { -not (Get-NetFirewallRule -DisplayName $_.Name -ErrorAction SilentlyContinue) } if ($missing) { " check: missing $(($missing | ForEach-Object { $_.Name }) -join ', ')" | Add-Content $log exit 1 } " check: all rules present" | Add-Content $log exit 0 } $ErrorActionPreference = "Stop" try { foreach ($r in $rules) { # Idempotent: clear any existing rule of this name first. Remove-NetFirewallRule -DisplayName $r.Name -ErrorAction SilentlyContinue if ($Action -eq "add") { if (-not (Test-Path $r.Path)) { " skip $($r.Name): $($r.Path) not found" | Add-Content $log continue } New-NetFirewallRule -DisplayName $r.Name -Group $GROUP ` -Direction Inbound -Action Allow ` -Program $r.Path -Protocol $r.Protocol -LocalPort $r.LocalPort ` -Profile Any | Out-Null " allowed $($r.Name) ($($r.Protocol) $($r.LocalPort), $($r.Path))" | Add-Content $log } else { " removed $($r.Name)" | Add-Content $log } } } catch { " ERROR: $_" | Add-Content $log throw }