Skip to content

fix(runtime): an ephemeral listen_port must derive ephemeral ports (#52) - #53

Merged
oreofeolurin merged 1 commit into
devfrom
fix/ephemeral-derived-ports
Aug 29, 2026
Merged

fix(runtime): an ephemeral listen_port must derive ephemeral ports (#52)#53
oreofeolurin merged 1 commit into
devfrom
fix/ephemeral-derived-ports

Conversation

@oreofeolurin

Copy link
Copy Markdown
Contributor

Fixes #52. Second Linux-only failure found by the CI in #49.

The bug

listen_port = 0 means "bind an ephemeral port" — it isn't a base you can add an offset to. The helpers added theirs anyway:

Service Offset Derived from listen_port = 0
Metrics +1 1
Dashboard +2 2
Raft +500 500
Gossip +600 600

All privileged; all refused on Linux without CAP_NET_BIND_SERVICE.

Runtime: boot 2 shards and shutdown uses .listen_port = 0, and metrics_enabled / dashboard_enabled both default to true, so it tried to bind port 2:

src/node/dashboard/http_server.zig:88: try stdx.net.sysBind(...)
src/node/runtime.zig:633: try server.start();
error.BindFailed

The metrics exporter hits it first but survives — it got graceful bind-failure logging in #43. The dashboard has no such guard, so the whole runtime start fails.

Why every local run passed

macOS permits bind(0.0.0.0, <low port>) where Linux does not. I checked directly rather than assuming:

bind 0.0.0.0:500   → OK on macOS
bind 127.0.0.1:500 → Permission denied on macOS
bind 0.0.0.0:500   → EACCES on Linux

The fix

An unresolved base derives an unresolved port: the helpers return 0 when listen_port == 0. An explicitly configured port still wins in every case.

Also updates the ephemeral-port test I added in #47 — it asserted effectiveRaftPort() == 500, which pinned the buggy behaviour. Worth calling out: that test passed while documenting a bug, which is exactly the failure mode I've been flagging elsewhere in this codebase.

Sequence, for context

This is why the failure moved between CI runs rather than disappearing: #47's clusterListenerWanted() gate stopped the Raft bind, so the next privileged bind in line — the dashboard — became the failure. Same root cause, three services deep.

Verification

test-unit (1328 tests) and test-integration pass locally. Linux is covered by #49's CI once this lands.

`listen_port = 0` means "bind an ephemeral port", so it is not a base to add an
offset to. The helpers added theirs unconditionally and produced ports 1
(metrics), 2 (dashboard), 500 (Raft) and 600 (gossip) — all privileged, all
refused on Linux without CAP_NET_BIND_SERVICE.

`Runtime: boot 2 shards and shutdown` uses `.listen_port = 0`, and both
metrics_enabled and dashboard_enabled default to true, so it tried to bind port
2 and failed. The metrics exporter hit it first but survived — it got graceful
bind-failure logging in #43; the dashboard has no such guard and failed the
whole runtime start.

An unresolved base now derives an unresolved port: the helpers return 0 when
listen_port is 0. An explicitly configured port still wins.

macOS permits bind(0.0.0.0, <low port>) where Linux does not, which is why
every local run passed. Second Linux-only failure found by the CI in #49,
after #50.

Also updates the ephemeral-port test added in #47, which asserted
effectiveRaftPort() == 500 — that pinned the buggy behaviour.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

An ephemeral listen_port derives privileged ports 1, 2 and 500

1 participant