Bind firmware downloads to active network interface#99
Merged
Conversation
Binary.Downloader was opening Mint connections with no interface binding, so downloads routed through the OS default (often ppp0 at metric 0) even after NetworkMonitor switched to a higher-priority interface like eth0. When ppp0 lost internet the download stalled; retries went back to ppp0. On each connect attempt, ask NetworkMonitor for the current bound interface and pass bind_to_device to Mint.HTTP.connect/4. Retries after an idle timeout now bind to whichever interface is active at that moment. Guards against NetworkMonitor not being started (test environment).
Two tests covering the bound_interface_transport_opts/0 logic: - returns empty opts when NetworkMonitor has no bound interface (default test environment state) - returns [bind_to_device: ifname] when an interface is bound, verified by temporarily updating NetworkMonitor state via :sys.replace_state
There was a problem hiding this comment.
Pull request overview
This PR updates the firmware download path to bind each HTTP connection attempt to the currently selected active network interface (as determined by Peridiod.Cloud.NetworkMonitor), preventing downloads from sticking to the OS default route when the preferred interface changes.
Changes:
- Passes
transport_opts: [bind_to_device: ifname]intoMint.HTTP.connect/4for each download (and retry) connect attempt based onNetworkMonitor’s bound interface. - Adds
Downloader.bound_interface_transport_opts/0helper to translate the currently bound interface into Mint transport options. - Adds unit tests covering the “no bound interface” and “bound interface present” transport opts cases.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
lib/peridiod/binary/downloader.ex |
Binds Mint HTTP connections to the currently bound network interface via bind_to_device. |
test/peridiod/binary/downloader_test.exs |
Adds unit tests for the new interface-binding transport options helper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Replace Process.whereis + GenServer.call with a try/catch :exit so the downloader returns [] gracefully if NetworkMonitor dies or restarts between the existence check and the call. Adds a test that kills the process mid-flight to verify the exit-safety path.
lee-reinhardt
approved these changes
May 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Binary.Downloaderwas opening Mint connections with no interface binding, so the OS default route (oftenppp0at metric 0) was used for all downloads regardless of which interfaceNetworkMonitorhad selectedNetworkMonitorswitched to a higher-priority interface (eth0), in-flight and retried download connections stayed onppp0; ifppp0then lost internet the download stalledNetworkMonitorfor the currently bound interface and passesbind_to_devicetoMint.HTTP.connect/4— the same mechanism already used byCloud.SocketFixes: https://linear.app/peridio/issue/ENG-1861
Test plan
mix test)"interface binding"describe block:[bind_to_device: "eth0"]when NetworkMonitor has a bound interfacedisconnect_on_higher_priority), trigger a firmware update while on ppp0, then bring up eth0 — download should migrate to eth0 after the next retry rather than stalling when ppp0 drops