refactor(desktop): use port 0 for ephemeral port discovery instead of PortPool reservation

Replace the PortPool-based port reservation system (9120-9199 range) with OS-assigned ephemeral ports via --port 0.

Before: Desktop probed a hardcoded port range, reserved ports in-process to close TOCTOU races, and passed the chosen port to the dashboard via CLI arg.

After: Desktop spawns dashboard with --port 0, parses the actual port from a stdout announcement line (HERMES_DASHBOARD_READY port=<N>), and uses that for WebSocket connections.

Changes:
- web_server.py: add --port 0 support with SO_REUSEADDR pre-bind + announcement; add EADDRINUSE preflight for explicit ports
- main.cjs: remove PortPool, PORT_FLOOR/CEILING, pickPort(), isPortAvailable(); add waitForDashboardPort() stdout parser
- Delete port-pool.cjs and port-pool.test.cjs (106 lines removed)

Net effect: eliminates the entire TOCTOU-mitigation reservation infrastructure and arbitrary port range constraints. OS handles port allocation natively.
This commit is contained in:
ethernet
2026-06-12 14:02:19 -04:00
parent 8044bf0206
commit 1e25358a8f
9 changed files with 305 additions and 270 deletions
+51 -8
View File
@@ -106,17 +106,60 @@ def test_should_require_auth_truth_table(host, allow_public, expected):
def _stub_uvicorn_run(monkeypatch):
"""Replace uvicorn.run with a no-op recorder so start_server returns
immediately (rather than blocking on the event loop). Returns the dict
that will capture the keyword args."""
"""Replace uvicorn.Config/Server with no-op fakes so start_server
returns immediately (rather than blocking on the event loop). Returns the dict
that will capture the keyword args.
"""
import asyncio
import contextlib
import uvicorn
captured: dict = {}
captured: dict = {"kwargs": {}}
def _fake_run(*args, **kwargs):
captured["args"] = args
captured["kwargs"] = kwargs
class _FakeConfig:
loaded = True
host = "127.0.0.1"
port = 8000
monkeypatch.setattr(uvicorn, "run", _fake_run)
def __init__(self, *args, **kwargs):
captured["kwargs"] = kwargs
def load(self):
pass
class lifespan_class:
should_exit = False
state: dict = {}
def __init__(self, *a, **kw):
pass
async def startup(self):
pass
async def shutdown(self):
pass
class _FakeServer:
should_exit = False
started = True
servers: list = []
lifespan = None
@staticmethod
def capture_signals():
return contextlib.nullcontext()
async def startup(self, sockets=None):
pass
async def main_loop(self):
pass
async def shutdown(self, sockets=None):
pass
monkeypatch.setattr(uvicorn, "Config", _FakeConfig)
monkeypatch.setattr(uvicorn, "Server", lambda config: _FakeServer())
return captured