Documentation Index
Fetch the complete documentation index at: https://mintlify.com/mullvad/mullvadvpn-app/llms.txt
Use this file to discover all available pages before exploring further.
Frontend Architectures
Mullvad VPN provides multiple frontend applications across different platforms, all communicating with the same core daemon. Each platform has unique requirements and communication patterns.
Communication Mechanisms
Desktop: gRPC over Unix Domain Socket
Platforms: Linux, macOS, Windows (named pipe)
The desktop Electron app and CLI communicate with the daemon via gRPC defined in mullvad-management-interface/proto/management_interface.proto.
┌──────────────────────┐
│ Electron/CLI App │
│ │
│ ┌────────────────┐ │
│ │ gRPC Client │ │
│ │ (Rust/Node.js) │ │
│ └────────┬───────┘ │
└───────────┼──────────┘
│ IPC
│ Unix Socket: /var/run/mullvad-vpn
│ Windows Pipe: \\.\pipe\mullvad-rpc
▼
┌──────────────────────┐
│ Mullvad Daemon │
│ ┌────────────────┐ │
│ │ Management │ │
│ │ Interface │ │
│ │ Server (gRPC) │ │
│ └────────────────┘ │
└──────────────────────┘
Key features:
- Bidirectional streaming for event subscriptions
- Multiple concurrent clients supported
- Connection-oriented (clients detect daemon restarts)
- Automatic reconnection handling in clients
Android: JNI (Java Native Interface)
Platform: Android
On Android, the daemon runs in the same process as the VpnService, with the Kotlin/Java UI layer calling into Rust via JNI.
┌────────────────────────────────────┐
│ Android App Process │
│ │
│ ┌──────────────────────────────┐ │
│ │ UI Layer (Kotlin) │ │
│ │ Activity, ViewModels, etc. │ │
│ └──────────────┬───────────────┘ │
│ │ │
│ │ Kotlin → Rust │
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ VpnService (Kotlin) │ │
│ │ Android VPN Service │ │
│ └──────────────┬───────────────┘ │
│ │ │
│ │ JNI Boundary │
│ ===============╪==================│
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ mullvad-jni (Rust) │ │
│ │ JNI wrapper functions │ │
│ └──────────────┬───────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ Mullvad Daemon (Rust) │ │
│ │ Core daemon actors │ │
│ └──────────────────────────────┘ │
└────────────────────────────────────┘
Communication pattern:
- Synchronous JNI calls from Java to Rust for commands
- Callbacks from Rust to Java for events (via JNI)
- Daemon initialized in VpnService.onCreate()
- Tunnel file descriptor passed from Android VpnService to Rust
Example JNI interface:
#[no_mangle]
pub extern "system" fn Java_..._MullvadDaemon_connect(
env: JNIEnv,
_class: JClass,
) {
let daemon = DAEMON_INSTANCE.lock();
daemon.send_command(DaemonCommand::Connect);
}
#[no_mangle]
pub extern "system" fn Java_..._MullvadDaemon_getState(
env: JNIEnv,
_class: JClass,
) -> JObject {
let daemon = DAEMON_INSTANCE.lock();
let state = daemon.get_state();
// Convert Rust state to Java object
state_to_jobject(env, state)
}
iOS: Custom Integration with WireGuard-Kit
Platform: iOS
iOS uses a different architecture where the tunnel is managed by Apple’s WireGuard-kit, with Mullvad logic providing account management and relay selection.
┌────────────────────────────────────────┐
│ iOS App │
│ │
│ ┌──────────────────────────────────┐ │
│ │ UI Layer (Swift/SwiftUI) │ │
│ └──────────────┬───────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────┐ │
│ │ PacketTunnelProvider (Swift) │ │
│ │ Network Extension │ │
│ │ │ │
│ │ ┌──────────────────────────┐ │ │
│ │ │ WireGuard-Kit (Swift) │ │ │
│ │ │ Tunnel management │ │ │
│ │ └──────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────┐ │ │
│ │ │ Mullvad Logic (Rust) │ │ │
│ │ │ - Account management │ │ │
│ │ │ - Relay selection │ │ │
│ │ │ - Settings │ │ │
│ │ └──────────────────────────┘ │ │
│ └──────────────────────────────────┘ │
└────────────────────────────────────────┘
Key differences from other platforms:
- No full daemon running; WireGuard-kit handles tunnel
- Mullvad Rust code provides relay selection and account logic
- Network extension runs in separate process from main app
- Uses iOS NEPacketTunnelProvider API
- Offline detection via NWPathMonitor
Frontend-Specific Architectures
Desktop Electron App
Location: desktop/packages/mullvad-vpn/
Architecture:
┌─────────────────────────────────────────────┐
│ Electron App │
│ │
│ ┌───────────────────────────────────────┐ │
│ │ Renderer Process (React) │ │
│ │ - UI Components │ │
│ │ - State management (Redux) │ │
│ │ - View logic │ │
│ └──────────────┬────────────────────────┘ │
│ │ IPC │
│ ▼ │
│ ┌───────────────────────────────────────┐ │
│ │ Main Process (Node.js) │ │
│ │ - Window management │ │
│ │ - System tray │ │
│ │ - Auto-updater │ │
│ │ - Daemon client │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ gRPC Client (Node bindings) │ │ │
│ │ └──────────────┬──────────────────┘ │ │
│ └─────────────────┼─────────────────────┘ │
└────────────────────┼─────────────────────────┘
│ Unix Socket/Named Pipe
▼
┌────────────────────┐
│ Mullvad Daemon │
└────────────────────┘
Key features:
- React for UI rendering
- Redux for state management
- Electron IPC between renderer and main process
- Main process maintains persistent gRPC connection to daemon
- Automatic reconnection on daemon restart
- System tray integration for quick access
- Auto-update using Mullvad’s update system
State synchronization:
- Main process subscribes to daemon event stream
- Events forwarded to renderer via Electron IPC
- Redux store updated with new state
- React components re-render
Android App
Location: android/
Architecture:
┌──────────────────────────────────────────────┐
│ Android Application │
│ │
│ ┌────────────────────────────────────────┐ │
│ │ UI Layer │ │
│ │ - Activities/Fragments (Kotlin) │ │
│ │ - Compose UI │ │
│ │ - ViewModels │ │
│ └───────────────┬────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────┐ │
│ │ Service Layer (Kotlin) │ │
│ │ - MullvadVpnService (VpnService) │ │
│ │ - ServiceConnection │ │
│ │ - Binders │ │
│ └───────────────┬────────────────────────┘ │
│ │ JNI │
│ ════════════════╪═══════════════════════════│
│ ▼ │
│ ┌────────────────────────────────────────┐ │
│ │ mullvad-jni (Rust) │ │
│ │ - JNI wrapper functions │ │
│ │ - Type conversions Java ↔ Rust │ │
│ └───────────────┬────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────┐ │
│ │ Mullvad Daemon (Rust) │ │
│ │ - In-process daemon │ │
│ │ - Tunnel management │ │
│ │ - API communication │ │
│ └────────────────────────────────────────┘ │
└──────────────────────────────────────────────┘
Android-specific considerations:
- VpnService.Builder API for creating VPN tunnel
- Always-on VPN support
- Split tunneling using Android app UIDs
- Per-app VPN configuration
- ConnectivityManager for offline detection
- Background service lifecycle management
- Notification requirement for foreground service
Tunnel file descriptor flow:
- Android VpnService.Builder creates tunnel interface
- Tunnel FD passed to Rust via JNI
- Rust daemon uses FD for WireGuard tunnel
- Traffic routed through VPN by Android system
iOS App
Location: ios/
Architecture:
┌──────────────────────────────────────────────┐
│ iOS Application │
│ │
│ ┌────────────────────────────────────────┐ │
│ │ Main App (Swift) │ │
│ │ - SwiftUI Views │ │
│ │ - Account management UI │ │
│ │ - Settings UI │ │
│ │ - Shared state with extension │ │
│ └────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────┐ │
│ │ Network Extension (Swift) │ │
│ │ │ │
│ │ PacketTunnelProvider │ │
│ │ │ │ │
│ │ ├─ WireGuard-Kit │ │
│ │ │ └─ Tunnel management (Go/Swift) │ │
│ │ │ │ │
│ │ └─ Mullvad Integration (Rust) │ │
│ │ └─ Relay selector │ │
│ │ └─ Account/device logic │ │
│ └────────────────────────────────────────┘ │
└──────────────────────────────────────────────┘
iOS-specific architecture:
- Separate process for network extension
- App Groups for shared data between app and extension
- WireGuard-kit provides tunnel implementation
- Mullvad Rust code compiled as static library
- NWPathMonitor for network reachability
- Keychain for secure credential storage
Communication between app and extension:
- Shared app group container for settings
- File-based communication for configuration
- XPC for limited IPC
CLI (Command-Line Interface)
Location: mullvad-cli/
Architecture:
┌──────────────────────┐
│ mullvad CLI │
│ │
│ ┌────────────────┐ │
│ │ Argument │ │
│ │ Parser (clap) │ │
│ └────┬───────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ Command │ │
│ │ Handlers │ │
│ └────┬───────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ gRPC Client │ │
│ └────┬───────────┘ │
└───────┼──────────────┘
│ Unix Socket
▼
┌──────────────────┐
│ Mullvad Daemon │
└──────────────────┘
Features:
- Complete feature parity with GUI
- Scriptable interface
- JSON output mode for programmatic use
- Same gRPC interface as desktop app
- Single command execution (not interactive)
Example commands:
mullvad connect
mullvad status
mullvad relay set location se got
mullvad account login 1234567890123456
mullvad tunnel wireguard key rotate
Event Subscription Pattern
All frontends follow a similar pattern for receiving state updates:
Desktop/CLI (gRPC)
// Subscribe to daemon events
let mut event_stream = client.events_listen(()).await?.into_inner();
// Process events as they arrive
while let Some(event) = event_stream.next().await {
match event?.event {
Some(daemon_event::Event::TunnelState(state)) => {
update_ui_state(state);
}
Some(daemon_event::Event::Settings(settings)) => {
update_settings(settings);
}
Some(daemon_event::Event::RelayList(_)) => {
reload_relay_list();
}
// ... other event types
}
}
Android (JNI Callbacks)
// Kotlin side registers callback
interface DaemonStateListener {
fun onTunnelStateChange(state: TunnelState)
fun onSettingsChange(settings: Settings)
}
MullvadDaemon.registerListener(listener)
// Rust side invokes callback via JNI
fn notify_tunnel_state_change(state: TunnelState) {
let env = get_jni_env();
let listener = get_registered_listener();
env.call_method(
listener,
"onTunnelStateChange",
state_to_jobject(state)
);
}
Desktop
- System tray integration
- Launch at startup
- Auto-updater
- Local network sharing settings
- Split tunneling (Linux, Windows, macOS)
Android
- Always-on VPN
- Per-app VPN
- Tile for quick connect
- Work profile support
- Split tunneling by app
iOS
- On-demand VPN rules
- VPN configuration profile
- Siri shortcuts
- Widget support
CLI
- Scriptable automation
- JSON output format
- Batch operations
- Server administration