Overview
The offline monitor detects when the device has no network connectivity, allowing the tunnel state machine to avoid futile connection attempts and properly inform the user. Each platform uses different mechanisms to achieve reliable offline detection without generating network traffic. Reference:talpid-core/src/offline/mod.rs, docs/architecture.md:218-269
Design Principles
No Network Traffic
The offline monitor must not send any network traffic to determine offline state:- Cannot perform ping tests
- Cannot attempt DNS lookups
- Cannot connect to remote servers
- No information leakage during offline detection
- No interference with firewall policies
- Reliable operation in secured states
docs/architecture.md:220-223
Platform-Specific Implementation
Each platform provides different APIs and mechanisms:- Windows: Route table monitoring + power state tracking
- Linux: Route queries via Netlink with firewall mark
- macOS: System Configuration framework with synthetic offline periods
- Android: ConnectivityManager network callbacks
- iOS: NWPathMonitor (via WireGuard Kit)
talpid-core/src/offline/mod.rs:9-23
Windows Implementation
Detection Strategy
Connectivity is inferred if:- A default route exists, AND
- The machine is not suspended
docs/architecture.md:226-229
Route Monitoring
talpid-core/src/offline/windows.rs:79-99
NotifyRouteChange2 API:
Listens for default route changes via Windows networking API:
- Added
- Removed
- Modified
talpid-core/src/offline/windows.rs:69-77, NotifyRouteChange2 docs
Power State Tracking
The machine is considered offline after suspend until a grace period expires:talpid-core/src/offline/windows.rs:46-67, docs/architecture.md:230-236
State Tracking
talpid-core/src/offline/windows.rs:35-39
Linux Implementation
Detection Strategy
Connectivity is inferred by checking if a route exists to a public IP address:- Query: “Is there a route to
193.138.218.78(Mullvad API)?” - Route query uses exclusion firewall mark
- Without the mark, query would always succeed in connected state (route via tunnel)
docs/architecture.md:239-245
Netlink Route Query
talpid-core/src/offline/linux.rs:24-100
Firewall Mark Coupling
The offline monitor is coupled to routing and split tunneling on Linux:- Uses the same firewall mark as excluded processes
- Route query bypasses tunnel routing table
- Ensures accurate offline detection even when tunnel is connected
docs/architecture.md:242-245
Change Listener
talpid-core/src/offline/linux.rs:35-71
macOS Implementation
Detection Strategy
Detects offline state usingSCDynamicStore to check for active network services:
talpid-core/src/offline/macos.rs:64-81, SCDynamicStore docs
Synthetic Offline Period
A major issue on macOS: the app can get stuck offline after network changes due to DNS blocking. The Problem: After waking from sleep or switching networks:- macOS performs connectivity checks (including DNS queries)
- Mullvad’s firewall blocks DNS (security policy)
- macOS’s checks time out, delaying route publication
- Network reachability callback not invoked until timeouts complete
- App thinks it’s offline for extended period
docs/architecture.md:252-259, talpid-core/src/offline/macos.rs:1-10
The Solution:
Synthesize a brief offline state between network transitions:
- Prevents DNS blocking from delaying connectivity
- Allows macOS connectivity checks to complete
- Ensures default route is available before connecting
talpid-core/src/offline/macos.rs:24,92-131
Route Listener
UsesRouteManagerHandle::default_route_listener() to observe default route changes:
- Receives
DefaultRouteEvent::Updatedwith IPv4/IPv6 route information - No active polling required
- Efficient, event-driven design
talpid-core/src/offline/macos.rs:71, RouteManagerHandle::default_route_listener docs
Android Implementation
Detection Strategy
Relies on Android’sConnectivityManager API:
talpid-core/src/offline/android.rs:1-32, ConnectivityManager docs
Connectivity Listener
TheConnectivityListener (Kotlin/Java) registers callbacks with Android:
NET_CAPABILITY_INTERNET: Network provides internet connectivityNET_CAPABILITY_NOT_VPN: Excludes VPN networks (only physical networks)
docs/architecture.md:261-264
iOS Implementation
Detection Strategy
The iOS app uses WireGuard Kit’s offline detection, which internally usesNWPathMonitor:
docs/architecture.md:266-269, NWPathMonitor docs
Path Status
NWPathMonitor provides:
satisfied: Default route exists, connectivity likelyunsatisfied: No default routerequiresConnection: Connection needs to be established
satisfied.
Connectivity Type
The offline monitor reports connectivity as:talpid-types/src/net.rs
IPv4 vs IPv6
Most platforms track IPv4 and IPv6 connectivity separately:- Allows IPv6-only or IPv4-only selection
- Prevents connection attempts when protocol unavailable
- Informs relay selection algorithm
Tunnel State Machine Integration
The offline monitor sends connectivity updates to the tunnel state machine:talpid-core/src/offline/mod.rs:43-72
State Machine Response
When the tunnel state machine receives offline notification:- Connecting state: Stop attempting connection, enter offline wait
- Connected state: Depends on offline timeout settings
- Error state: May stay in error (already blocking)
- Disconnecting/Disconnected: No action needed
- Resume connection attempts
- Retry with current relay selection
docs/architecture.md:167-170
Fail-Open Strategy
All platform implementations “fail open” when uncertain:- Prevents blocking the user when offline detection fails
- Connectivity attempts may still succeed
- User experience is better than being stuck
- Security is not compromised (firewall still active)
talpid-core/src/offline/linux.rs:86-92, windows.rs:86-87,94-97
Disabling Offline Monitor
For debugging, the offline monitor can be disabled:- Always reports
Connectivity::PresumeOnline - Tunnel state machine never enters offline wait
- Useful for debugging connectivity issues
talpid-core/src/offline/mod.rs:26-30,49-51
API Communication Coordination
The offline monitor also affects API communication:- API requests blocked when offline
- Prevents wasted connection attempts
- Resumes when connectivity restored
docs/architecture.md:61-62
Performance Considerations
Event-Driven Design
All implementations use event-driven approaches:- No polling loops
- Low CPU usage
- Immediate response to connectivity changes
Minimal Overhead
- No network traffic generated
- Uses platform-native APIs
- Efficient state tracking
Testing and Debugging
Simulating Offline State
Network disconnect:Monitoring Events
Enable debug logging to see offline detection events:Common Issues
macOS Stuck Offline
If the app remains offline on macOS after network changes:- Check if DNS is being blocked by firewall
- Verify synthetic offline period is working
- Consider allowing macOS network check
docs/allow-macos-network-check.md
Linux False Positives
If offline detection incorrectly reports online:- Verify firewall mark is set correctly
- Check if split tunneling configuration is correct
- Ensure routing tables are properly configured
Windows Sleep/Wake Issues
If connections fail after waking from sleep:- Check if grace period is sufficient (default 5 seconds)
- Verify power management events are being received
- Consider increasing grace period for slow hardware
Related Documentation
- Mullvad vs Talpid Layers
- API Communication
- Architecture Overview (source repository)
- Allow macOS Network Check (source repository)