mux web: two hubs silently share the same port (reuse_address) — refuse like mux d refuses a taken socket #27
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Observed during the 0x0 browser smoke: a stale muxweb and a fresh one both bound 127.0.0.1:7681 (ss showed two LISTEN rows for the port), and browser connections landed on either at random — the page showed the stale hub's wall. muxd refuses a socket path another daemon owns; muxweb should refuse a port another hub owns instead of silently sharing it. Repro: start muxweb twice; both report 'serving http://127.0.0.1:7681'.
Migrated from git-collab issue
bd0f8c7d7e4d4f82e1a902649a1387c7d7a41dd2.Original author: a73x dev@a73x.sh.
Close reason: fixed in
28780537: mux web refuses a port another hub listens on; e2e_06_web taken-port leg pins it on both OSes via os_oracle tcp_listeners (99b86336)2026-08-29: still true — addr.listen(.{ .reuse_address = true }) at src/cli/webhub_main.zig:173.
[claude 2026-09-02] Picking this up from forge-4. Approach: the sharing is SO_REUSEPORT, not SO_REUSEADDR. Zig's std.net.Address.listen sets BOTH for any non-unix family when reuse_address is true (deps/zig/lib/std/net.zig:279-294), and SO_REUSEPORT is exactly the option that lets several processes hold one addr:port while the kernel load-balances new connections across them -- two LISTEN rows, browsers landing on either. Measured on this box: two plain SO_REUSEADDR binds of 127.0.0.1:P are refused EADDRINUSE, so REUSEPORT is the whole of it. Fix: bind the hub's port by hand in webhub.zig (socket + SO_REUSEADDR only + bind + listen, wrapped in std.net.Server) so REUSEADDR keeps doing the one job it is for -- a hub restarting while its old accepted connections sit in TIME_WAIT still gets its port back -- while a port a live hub owns comes back AddressInUse. webhub_main then answers that with muxd's own one-liner shape ('mux d: a daemon is already running on PATH' -> 'mux web: a hub is already running on 127.0.0.1:PORT', rc 1), which is also what decisions.md already records for muxd's AddressInUse case. Tests: unit test in webhub.zig that the second bind of a port is refused and that the fd carries REUSEADDR without REUSEPORT (asked of the descriptor, not of the call), plus an e2e leg in e2e_06_web.sh starting a second real hub on the live hub's port.