Skip to content

Improve LAN direct-path discovery and connection reliability - #853

Merged
ndizazzo merged 4 commits into
mainfrom
micn/direct-lan-improvements
Jun 21, 2026
Merged

Improve LAN direct-path discovery and connection reliability#853
ndizazzo merged 4 commits into
mainfrom
micn/direct-lan-improvements

Conversation

@michaelneale

@michaelneale michaelneale commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

Makes mesh-llm find and use fast LAN direct paths more reliably, so peers on the same network connect directly instead of falling back to the relay.

What changed

  • Auto-pin QUIC to a real private LAN IPv4 so direct-path repair lands on the fast path.
  • Discover LAN direct paths in mDNS mode regardless of which node is hosting (mDNS reverse-dial + LAN beacon).
  • Tear down the LAN bootstrap loops (mDNS publisher, reverse-dial, beacon) cleanly on shutdown so they release their sockets and stop dialing.
  • Refresh stale dial-back targets: when a peer restarts/rebinds under the same id, update its address instead of unicasting to the dead socket.

Validation

  • cargo fmt / cargo clippy --all-targets -D warnings — clean
  • cargo test -p mesh-llm-host-runtime --lib mesh + network suites pass (incl. new remember_join_target_updates_address_on_peer_rebind test)

Summary by CodeRabbit

  • New Features

    • Added relay-less direct LAN peer connectivity using mDNS reverse-dial with advertised endpoint addressing.
    • Introduced LAN beaconing and dial-back over reachable LAN IPs (including join-target addresses).
    • Improved multi-homed LAN behavior by pinning discovery/publishing to the selected interface and preventing LAN advertisement collisions.
    • Enhanced LAN address handling for join/dial flows with join-target tracking and private-LAN IPv4 detection.
  • Tests

    • Added coverage for endpoint-address round-tripping, LAN IP filtering, and join-target update/dial gating behavior.
  • Chores

    • Added networking dependency support for LAN discovery utilities.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 27ad87d9-9eff-4fd6-ba28-7a205a6baab5

📥 Commits

Reviewing files that changed from the base of the PR and between 4519e75 and 5f7bcf5.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • crates/mesh-llm-host-runtime/Cargo.toml
  • crates/mesh-llm-host-runtime/src/mesh/lan_bootstrap.rs
  • crates/mesh-llm-host-runtime/src/mesh/mod.rs
  • crates/mesh-llm-host-runtime/src/mesh/tests.rs
  • crates/mesh-llm-host-runtime/src/network/discovery.rs
  • crates/mesh-llm-host-runtime/src/network/lan_beacon.rs
  • crates/mesh-llm-host-runtime/src/network/lan_bootstrap.rs
  • crates/mesh-llm-host-runtime/src/network/mdns_reverse_dial.rs
  • crates/mesh-llm-host-runtime/src/network/mod.rs
  • crates/mesh-llm-host-runtime/src/runtime/mod.rs
🚧 Files skipped from review as they are similar to previous changes (10)
  • crates/mesh-llm-host-runtime/Cargo.toml
  • crates/mesh-llm-host-runtime/src/network/mod.rs
  • crates/mesh-llm-host-runtime/src/network/lan_bootstrap.rs
  • crates/mesh-llm-host-runtime/src/network/mdns_reverse_dial.rs
  • crates/mesh-llm-host-runtime/src/mesh/tests.rs
  • crates/mesh-llm-host-runtime/src/mesh/lan_bootstrap.rs
  • crates/mesh-llm-host-runtime/src/runtime/mod.rs
  • crates/mesh-llm-host-runtime/src/network/lan_beacon.rs
  • crates/mesh-llm-host-runtime/src/mesh/mod.rs
  • crates/mesh-llm-host-runtime/src/network/discovery.rs

📝 Walkthrough

Walkthrough

Adds relay-less LAN peer bootstrapping to mesh-llm-host-runtime via three parallel mechanisms: LAN IPv4 auto-detection, mDNS advertisement of iroh::EndpointAddr in TXT records for reverse-dial, and a UDP multicast beacon module. New Node APIs track join targets and expose peer connectivity state. All background loops are wired into the runtime with coordinated shutdown.

Changes

LAN Direct-Path Bootstrapping

Layer / File(s) Summary
Dependencies and LAN IPv4 detection
crates/mesh-llm-host-runtime/Cargo.toml, src/mesh/lan_bootstrap.rs, src/mesh/mod.rs
Adds socket2 and if-addrs deps; introduces detect_primary_lan_ipv4() (default-route probe + interface scan fallback) with LAN classification helpers; adjusts bind_mesh_endpoint to clear IP transport defaults in relay-disabled IPv4 mode; adds RFC1918/CGNAT unit tests.
Node join-target tracking and LAN peer APIs
src/mesh/mod.rs, src/mesh/tests.rs
Adds join_targets field to Node; records resolved EndpointAddr via remember_join_target() in join() and join_with_retry(); exposes advertised_endpoint_addr, dial_peer_addr, connected_peer_ids, known_peer_lan_ipv4, and join_target_lan_ipv4 public methods; adds rebind replacement and LAN filtering tests.
mDNS advertisement of endpoint addresses
src/network/discovery.rs
Adds endpoint_addr_b64 to LanMeshAdvertisement and endpoint_addr to LanDiscoveredMesh; encodes/decodes via base64url JSON helpers; emits ep_addr TXT property; adds interface pinning via restrict_daemon_to_interface; introduces discover_lan_on_interface; fixes per-node lan_instance_name; adds round-trip unit tests.
mDNS reverse-dial loop
src/network/mdns_reverse_dial.rs
New run_loop entrypoint periodically calls reverse_dial_tickbrowse_landial_target (filters self and connected peers) → dial_back (node.dial_peer_addr).
UDP multicast LAN beacon
src/network/lan_beacon.rs
New module with BeaconMessage JSON wire format; spawn binds multicast listener and runs tick loop; emit_beacon multicasts and unicasts to known peer LAN IPs; handle_beacon and parse_beacon validate, filter self/mesh-id, and dial new peers.
Runtime wiring and lifecycle
src/network/lan_bootstrap.rs, src/network/mod.rs, src/runtime/mod.rs
Introduces effective_quic_bind_ip() with priority fallback (explicit bind-ip → auto-detect → None); LanBootstrapTasks guard holds handles for all three loops; spawn_mdns_reverse_dial conditionally spawns publish, reverse-dial, and beacon tasks; shutdown calls lan_bootstrap_tasks.abort(); both MCP and auto paths use the new bind IP selection.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(173, 216, 230, 0.5)
    Note over Runtime,Node: Startup
    Runtime->>Runtime: effective_quic_bind_ip() → detect_primary_lan_ipv4()
    Runtime->>Node: start(bind_ip)
    Runtime->>spawn_mdns_reverse_dial: spawn(options, node)
    spawn_mdns_reverse_dial->>publish_lan_loop: spawn (ep_addr in TXT)
    spawn_mdns_reverse_dial->>mdns_reverse_dial: run_loop spawn
    spawn_mdns_reverse_dial->>lan_beacon: spawn (multicast listener)
  end

  rect rgba(144, 238, 144, 0.5)
    Note over lan_beacon,Node: Beacon path
    lan_beacon->>lan_beacon: emit_beacon (multicast + unicast to peer LAN IPs)
    lan_beacon->>lan_beacon: handle_beacon (parse, validate, mesh-id check)
    lan_beacon->>Node: dial_peer_addr(EndpointAddr)
  end

  rect rgba(255, 218, 185, 0.5)
    Note over mdns_reverse_dial,Node: mDNS reverse-dial path
    mdns_reverse_dial->>discover_lan_on_interface: browse (interface-pinned)
    discover_lan_on_interface-->>mdns_reverse_dial: LanDiscoveredMesh[].endpoint_addr
    mdns_reverse_dial->>Node: dial_peer_addr(EndpointAddr)
  end

  rect rgba(255, 182, 193, 0.5)
    Note over Runtime,lan_bootstrap_tasks: Shutdown
    Runtime->>lan_bootstrap_tasks: abort()
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • ndizazzo
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Improve LAN direct-path discovery and connection reliability' clearly and concisely summarizes the main objectives of the changeset, which focuses on enhancing direct LAN connections between peers through improved discovery mechanisms and better connection handling.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch micn/direct-lan-improvements

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (3)
crates/mesh-llm-host-runtime/src/network/lan_beacon.rs (3)

128-161: 💤 Low value

spawn_blocking panics are silently discarded.

The .ok() on line 159 silently drops any panic from the blocking task. While this is acceptable for best-effort emission, consider at least logging join errors for debugging multi-homed host issues:

-    tokio::task::spawn_blocking(move || emit_blocking(mcast, &peers, &payload))
-        .await
-        .ok();
+    if let Err(err) = tokio::task::spawn_blocking(move || emit_blocking(mcast, &peers, &payload)).await {
+        tracing::trace!("LAN beacon spawn_blocking failed: {err}");
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/network/lan_beacon.rs` around lines 128 -
161, The spawn_blocking task result is being silently discarded with .ok() at
the end of the emit_beacon function, which hides any errors or panics from the
emit_blocking closure. Replace the .ok() call with proper error handling that
logs failures using an appropriate logging mechanism (such as the pattern used
elsewhere in the codebase for debug or warn level logging), while still
returning Ok(()) from emit_beacon to maintain the best-effort emission behavior.
This will provide visibility into multi-homed host issues during debugging
without changing the overall function semantics.

32-46: 💤 Low value

Note: BEACON_GROUP reuses the mDNS multicast address.

224.0.0.251 is the IANA-assigned mDNS multicast address. While using a different port (47654 vs 5353) makes this technically distinct, some network equipment or firewalls may treat this address specially for mDNS. This appears intentional based on the comment about link-local routing, but worth documenting explicitly that mDNS's address is being reused (which may actually be beneficial for firewall traversal).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/network/lan_beacon.rs` around lines 32 - 46,
The comment for the BEACON_GROUP constant does not explicitly document that
224.0.0.251 is the IANA-assigned mDNS multicast address. Update the comment
block above the BEACON_GROUP definition to explicitly mention that this
multicast address is the same as mDNS's address (224.0.0.251), and clarify that
while a different port (47654 versus mDNS's 5353) ensures technical
distinctness, the intentional address reuse is a deliberate design choice that
may benefit firewall traversal.

163-189: 💤 Low value

Socket-per-send is intentional but costly.

Creating a new socket for every multicast and unicast send is documented as necessary to avoid EHOSTUNREACH on multi-homed macOS hosts. This is fine for correctness, but creates significant overhead at the 5-second beacon interval with multiple peers.

If performance becomes a concern, consider caching the send socket and only recreating on send failure. For now this is acceptable given the low beacon frequency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/network/lan_beacon.rs` around lines 163 -
189, The review comment notes that creating a new socket for every multicast and
unicast send in the emit_blocking, send_multicast, and send_unicast functions is
intentional for correctness on multi-homed macOS hosts but has performance
implications. While acceptable for now given the low beacon frequency, add
comprehensive code comments explaining this deliberate design trade-off and
document the potential optimization approach of caching the send socket and only
recreating on send failure for future reference when performance becomes a
concern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/mesh-llm-host-runtime/src/mesh/mod.rs`:
- Around line 4851-4865: The function known_peer_lan_ipv4 currently returns all
IPv4 candidates from peer gossip without filtering, which causes public and STUN
addresses to be included in the beacon results. This allows mesh metadata and
beacon traffic to be sent off-LAN. Modify the function to filter the results
using is_private_lan_ipv4 to only include RFC1918 private LAN addresses before
adding them to the output vector. The same filtering should also be applied to
the similar helper function at lines 5008-5021.
- Around line 507-522: The first_private_lan_interface_ipv4 function currently
filters for any RFC1918 private address but incorrectly includes Docker and CNI
bridge addresses (such as 172.* ranges) which are host-local and reused across
machines. This causes QUIC to pin to docker0/cni0 instead of the actual LAN NIC,
breaking direct dialing. Add an additional filter in the chain after the
is_private_lan_ipv4 check to explicitly exclude container bridge address ranges
(such as the 172.17.0.0/16 range used by Docker and similar ranges used by other
container runtimes). This filter should reject these ranges while still
accepting genuine LAN interfaces.
- Around line 447-522: Create a semantically-named submodule for LAN bootstrap
functionality to address the excessive size and mixed responsibilities in
mesh/mod.rs. Move the LAN IP detection functions (detect_primary_lan_ipv4,
is_private_lan_ipv4, default_route_source_ipv4,
first_private_lan_interface_ipv4) from lines 447-522 and their associated tests
to a new module file (e.g.,
crates/mesh-llm-host-runtime/src/mesh/lan_bootstrap.rs). Similarly, move the
join-target tracking code and tests from lines 642-686, 2405-2408, and 4821-5021
into appropriately named submodules or consolidate them based on semantic
coherence. Update all references in mesh/mod.rs to use the new module paths, and
declare these new modules in mesh/mod.rs via mod statements so they remain
accessible to the rest of the codebase.
- Around line 4829-4835: The dial_peer_addr method calls connect_to_peer which
returns early for peers still in the dead_peers collection, preventing freshly
advertised EndpointAddrs from being retried within the 5-minute dead peer TTL.
Before calling connect_to_peer in dial_peer_addr, clear the dead peer gate by
removing the peer from the dead_peers collection (or calling the appropriate
override method) just like join and join_with_retry already do, so that freshly
advertised peers can bypass the dead peer TTL check.

In `@crates/mesh-llm-host-runtime/src/mesh/tests.rs`:
- Around line 3156-3192: The test
`remember_join_target_updates_address_on_peer_rebind` is being added to an
already oversized tests.rs file, which violates the coding guideline to split
files that exceed 1,000 lines and contain multiple separable responsibilities.
Extract this test into a new semantically named test module (such as
`join_target_tests` or `dial_target_tests`) that groups join-target and
dial-target behavior concerns together. Move the entire test function and any
shared test utilities it depends on into this new module, then remove it from
the root test file to improve navigation and ownership clarity.

In `@crates/mesh-llm-host-runtime/src/runtime/mod.rs`:
- Around line 6064-6084: Extract the QUIC bind IP selection and LAN bootstrap
subsystem from runtime/mod.rs by moving the effective_quic_bind_ip function
(shown at lines 6064-6084) and the related LAN bootstrap orchestration code
(referenced at lines 7962-8032, including spawn_mdns_reverse_dial and associated
task/publisher/reverse-dial/beacon wiring) into a new semantically named module
under crates/mesh-llm-host-runtime/src/network/. Update all imports and
references in runtime/mod.rs to point to the new module, and consider renaming
spawn_mdns_reverse_dial to better reflect its expanded responsibilities once
extracted from the runtime context.
- Around line 7967-7977: The `LanBootstrapTasks` struct with its `abort()`
method only cancels handles when `shutdown_run_auto_runtime()` is called, but
early returns or drops from error paths (like those after
`build_run_auto_node_setup()` or `RunAutoModelSelection::Shutdown` early return)
leave mDNS/beacon tasks running with active sockets. Implement the `Drop` trait
for `LanBootstrapTasks` to automatically call `abort()` whenever the struct is
dropped, ensuring handles are properly cleaned up on every exit path regardless
of how the scope is exited.

---

Nitpick comments:
In `@crates/mesh-llm-host-runtime/src/network/lan_beacon.rs`:
- Around line 128-161: The spawn_blocking task result is being silently
discarded with .ok() at the end of the emit_beacon function, which hides any
errors or panics from the emit_blocking closure. Replace the .ok() call with
proper error handling that logs failures using an appropriate logging mechanism
(such as the pattern used elsewhere in the codebase for debug or warn level
logging), while still returning Ok(()) from emit_beacon to maintain the
best-effort emission behavior. This will provide visibility into multi-homed
host issues during debugging without changing the overall function semantics.
- Around line 32-46: The comment for the BEACON_GROUP constant does not
explicitly document that 224.0.0.251 is the IANA-assigned mDNS multicast
address. Update the comment block above the BEACON_GROUP definition to
explicitly mention that this multicast address is the same as mDNS's address
(224.0.0.251), and clarify that while a different port (47654 versus mDNS's
5353) ensures technical distinctness, the intentional address reuse is a
deliberate design choice that may benefit firewall traversal.
- Around line 163-189: The review comment notes that creating a new socket for
every multicast and unicast send in the emit_blocking, send_multicast, and
send_unicast functions is intentional for correctness on multi-homed macOS hosts
but has performance implications. While acceptable for now given the low beacon
frequency, add comprehensive code comments explaining this deliberate design
trade-off and document the potential optimization approach of caching the send
socket and only recreating on send failure for future reference when performance
becomes a concern.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d413d65d-69f7-4f73-9a52-234038558cf3

📥 Commits

Reviewing files that changed from the base of the PR and between 247dd7e and 40ea19d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • crates/mesh-llm-host-runtime/Cargo.toml
  • crates/mesh-llm-host-runtime/src/mesh/mod.rs
  • crates/mesh-llm-host-runtime/src/mesh/tests.rs
  • crates/mesh-llm-host-runtime/src/network/discovery.rs
  • crates/mesh-llm-host-runtime/src/network/lan_beacon.rs
  • crates/mesh-llm-host-runtime/src/network/mdns_reverse_dial.rs
  • crates/mesh-llm-host-runtime/src/network/mod.rs
  • crates/mesh-llm-host-runtime/src/runtime/mod.rs

Comment thread crates/mesh-llm-host-runtime/src/mesh/mod.rs Outdated
Comment thread crates/mesh-llm-host-runtime/src/mesh/mod.rs Outdated
Comment thread crates/mesh-llm-host-runtime/src/mesh/mod.rs
Comment thread crates/mesh-llm-host-runtime/src/mesh/mod.rs
Comment thread crates/mesh-llm-host-runtime/src/mesh/tests.rs Outdated
Comment thread crates/mesh-llm-host-runtime/src/runtime/mod.rs Outdated
Comment thread crates/mesh-llm-host-runtime/src/runtime/mod.rs Outdated
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has not been updated in at least 5 days. It will be closed after 7 days of inactivity to keep the active review queue current. Please update it within 2 days if the changes are still moving forward.

@github-actions github-actions Bot added the stale label Jun 20, 2026
@ndizazzo ndizazzo removed the stale label Jun 21, 2026
@ndizazzo ndizazzo assigned ndizazzo and unassigned michaelneale Jun 21, 2026
michaelneale and others added 4 commits June 21, 2026 17:46
Auto-pin QUIC to the primary private LAN interface so relay-less direct
paths land on the fast path, add an mDNS reverse-dial loop and a UDP
multicast LAN beacon so peers find each other regardless of which node
hosts, and advertise the endpoint address over mDNS for dial-back.

Tear down the LAN bootstrap loops on shutdown so they release sockets
and stop dialing, and refresh stale dial-back targets when a peer
rebinds to a new address under the same id.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@ndizazzo
ndizazzo force-pushed the micn/direct-lan-improvements branch from 4519e75 to 5f7bcf5 Compare June 21, 2026 21:53
@ndizazzo
ndizazzo merged commit 4de3336 into main Jun 21, 2026
32 checks passed
@ndizazzo
ndizazzo deleted the micn/direct-lan-improvements branch June 21, 2026 22:24
michaelneale added a commit that referenced this pull request Jun 23, 2026
* origin/main:
  update guides for dev loop (#895)
  Add native MTP generation metadata to layer packages (#888)
  upgrade iroh to 1.0 (#894)
  fix(runtime): support relocating shared libs
  Improve LAN direct-path discovery and connection reliability (#853)
  Add GLM chat template fallback in llama (#890)

# Conflicts:
#	crates/mesh-llm-config/src/model/built_in_schema.rs
#	crates/mesh-llm-config/src/validate.rs
#	crates/mesh-llm-host-runtime/src/inference/skippy/deployment.rs
#	crates/mesh-llm-host-runtime/src/inference/skippy/materialization.rs
#	crates/mesh-llm-host-runtime/src/inference/skippy/resolver/speculative.rs
#	crates/mesh-llm-host-runtime/src/inference/skippy/resolver/translation.rs
#	crates/mesh-llm-host-runtime/src/inference/skippy/stage/mod.rs
#	crates/mesh-llm-host-runtime/src/inference/skippy/stage/tests.rs
#	crates/mesh-llm-host-runtime/src/inference/skippy/stage/types.rs
#	crates/mesh-llm-host-runtime/src/mesh/mod.rs
#	crates/mesh-llm-host-runtime/src/mesh/tests.rs
#	crates/mesh-llm-host-runtime/src/runtime/local.rs
#	crates/skippy-protocol/proto/stage.proto
#	crates/skippy-server/src/binary_transport.rs
#	crates/skippy-server/src/frontend.rs
#	crates/skippy-server/src/frontend/embedded_execution.rs
#	crates/skippy-server/src/frontend/embedded_generation.rs
#	crates/skippy-server/src/frontend/generation_flow.rs
#	crates/skippy-server/src/frontend/prefix_cache.rs
#	docs/skippy/CONFIGURATION.md
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.

2 participants