Skip to content

feat(networking): allow dialing peers over unicast via EXO_ZENOH_CONNECT - #2243

Open
Bawan-Wang wants to merge 3 commits into
exo-explore:mainfrom
Bawan-Wang:feat/zenoh-connect-endpoints
Open

feat(networking): allow dialing peers over unicast via EXO_ZENOH_CONNECT#2243
Bawan-Wang wants to merge 3 commits into
exo-explore:mainfrom
Bawan-Wang:feat/zenoh-connect-endpoints

Conversation

@Bawan-Wang

Copy link
Copy Markdown

Peer discovery relies solely on IPv6 link-local multicast to the group ff12::e0a1:de89. Many Wi-Fi access points isolate wireless clients and do not forward link-local multicast between them, so two nodes on the same subnet never discover each other even though unicast between them works fine. There is currently no fallback: --bootstrap-peers raises "Bootstrap peers has been temporarily removed".

Multicast is only used to learn a peer's address; the actual link is a unicast TCP connection established by connect_peer(). This adds an optional EXO_ZENOH_CONNECT environment variable holding a comma-separated list of zenoh endpoints (e.g. "tcp/192.168.1.2:52414") that is injected into the session's connect/endpoints, letting an operator dial known peers directly and bypass discovery entirely.

The variable is opt-in: when unset, configuration is unchanged. Only one side needs to set it, since the TCP link is bidirectional and scouting/gossip/multihop is already enabled.

Verified on two WSL2 nodes over Wi-Fi where discovery had never worked: tcpdump on udp/52413 showed only locally originated multicast and no inbound packets from the peer. With EXO_ZENOH_CONNECT set on one node only, both nodes reported nodes: 2 with a populated connections map, and the link remained stable.

Motivation

Changes

Why It Works

Test Plan

Manual Testing

Automated Testing

Bawan-Wang and others added 3 commits July 30, 2026 16:39
Peer discovery relies solely on IPv6 link-local multicast to the group
ff12::e0a1:de89. Many Wi-Fi access points isolate wireless clients and do
not forward link-local multicast between them, so two nodes on the same
subnet never discover each other even though unicast between them works
fine. There is currently no fallback: --bootstrap-peers raises
"Bootstrap peers has been temporarily removed".

Multicast is only used to learn a peer's address; the actual link is a
unicast TCP connection established by connect_peer(). This adds an
optional EXO_ZENOH_CONNECT environment variable holding a comma-separated
list of zenoh endpoints (e.g. "tcp/192.168.1.2:52414") that is injected
into the session's connect/endpoints, letting an operator dial known
peers directly and bypass discovery entirely.

The variable is opt-in: when unset, configuration is unchanged. Only one
side needs to set it, since the TCP link is bidirectional and
scouting/gossip/multihop is already enabled.

Verified on two WSL2 nodes over Wi-Fi where discovery had never worked:
tcpdump on udp/52413 showed only locally originated multicast and no
inbound packets from the peer. With EXO_ZENOH_CONNECT set on one node
only, both nodes reported nodes: 2 with a populated connections map, and
the link remained stable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…EP_SIZE

The prefill step size was hardcoded to 4096. On a small card the shard's
weights leave little room: with a 14B 4-bit model split across two 8GB GPUs
each runner holds ~4.15GB of weights and has only 2.5-3.4GB left, and a
4096-token prefill chunk exceeds that. The request then dies with

    auto_parallel.py:167 mx.eval(output)
    RuntimeError: cudaMallocAsync(&data, size, stream) failed: out of memory

which kills the runner and tears the instance down, so a client that sends
long prompts (an agent system prompt plus a repo map) never reaches decode.

Reading the value from the environment lets an operator trade prefill
throughput for headroom without patching the source on every node. Unset
keeps upstream behaviour.

Measured on that 2x8GB setup: lowering it is not by itself a fix — 512 still
OOMs on a ~1000-token prompt, so the dominant term is elsewhere. The knob is
still worth having on machines where the shard leaves more room.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…uster

Both nodes need a handful of environment variables to start exo at all, and
the two sets genuinely differ, so keeping them in one file in git is what
stops the machines drifting apart — a split we have now paid for twice, once
with mlx-cpu vs mlx-cuda and once with mlx-cuda12 vs mlx-cuda13.

Shared: LD_PRELOAD (anaconda ships libstdc++ 3.4.29, transformers loads it
first and libmlx then fails on GLIBCXX_3.4.30) and OVERRIDE_MEMORY_MB (exo
reports system RAM but CUDA runs out of VRAM, so placement over-assigns
layers without it).

MSI only: EXO_ZENOH_CONNECT, since the Wi-Fi AP drops IPv6 link-local
multicast and one side dialling the other over unicast is enough.

APU-TPNB04 only: CUDA_HOME and LD_LIBRARY_PATH, because its /usr/local/cuda
is 12.6 — that cuda_fp8.h has no __nv_fp8_e8m0 so nvrtc cannot JIT mlx
kernels, and its lib64 only carries libcublasLt.so.12.

The script refuses to run on an unknown host rather than guessing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant