Skip to content

Vasak-OS/tauri-plugin-network-manager

Repository files navigation

Tauri Network Manager Plugin

Crates.io License Rust Tauri

Linux-first Tauri plugin to manage network state, Wi-Fi, and VPN through NetworkManager over D-Bus.


Table of Contents


Features

Wi-Fi

  • Scan & list nearby access points with signal strength and security detection
  • Connect to open / WEP / WPA-PSK / WPA2-PSK / WPA3-PSK / WPA-EAP networks
  • Disconnect from current Wi-Fi
  • List and delete saved Wi-Fi connections
  • Request explicit scans

VPN

  • List, create, update, delete VPN profiles (OpenVPN, WireGuard, L2TP, PPTP, SSTP, IKEv2, FortiSSL, OpenConnect, Generic)
  • Connect / disconnect VPN by UUID
  • Read current VPN status, gateway, IP configuration

Network State

  • Read current active network (Ethernet / Wi-Fi) — SSID, IP, MAC, signal, security
  • Enable / disable wireless and global networking
  • Check wireless hardware availability

Monitoring

  • Bandwidth stats (download/upload speed, total bytes, uptime)
  • Real-time events via Tauri's event system:
    • network-changed — network state transition
    • vpn-changed, vpn-connected, vpn-disconnected, vpn-failed

Architecture

flowchart TB
    subgraph Frontend["Tauri Frontend (TypeScript)"]
        TS["guest-js/index.ts<br/>invoke() / listen()"]
    end

    subgraph IPC["Tauri Bridge"]
        IPC_BRIDGE["Plugin IPC"]
    end

    subgraph Rust["Rust Plugin (tauri-plugin-network-manager)"]
        direction TB
        CMDS["commands.rs<br/>19 #[tauri::command]"]
        DESKTOP["desktop.rs<br/>D-Bus logic"]
        HELPERS["nm_helpers.rs<br/>security detection"]
        STATS["network_stats.rs<br/>bandwidth tracker"]
        LIB["lib.rs<br/>state mgmt · caching<br/>event emitter"]
        ERR["error.rs<br/>typed errors"]
        MODELS["models.rs<br/>data structures"]

        CMDS --> DESKTOP
        DESKTOP --> HELPERS
        DESKTOP --> STATS
        LIB --> CMDS
        DESKTOP --> ERR
        DESKTOP --> MODELS
    end

    subgraph NM["NetworkManager (D-Bus System Bus)"]
        NM_MAIN["org.freedesktop.NetworkManager<br/>AddAndActivateConnection<br/>ActivateConnection<br/>DeactivateConnection"]
        NM_SETTINGS["org.freedesktop.NetworkManager.Settings<br/>AddConnection · Update · Delete<br/>ListConnections"]
        NM_DEVICE["org.freedesktop.NetworkManager.Device<br/>DeviceType · HwAddress · Ip4Config"]
        NM_WIFI["org.freedesktop.NetworkManager.Device.Wireless<br/>AccessPoints · ActiveAccessPoint<br/>RequestScan"]
        NM_AP["org.freedesktop.NetworkManager.AccessPoint<br/>Ssid · Strength · Flags<br/>WpaFlags · RsnFlags · KeyMgmt"]
        NM_ACTIVE["org.freedesktop.NetworkManager.Connection.Active<br/>State · Type · Devices · Uuid"]
    end

    Frontend <--> IPC
    IPC <--> Rust
    DESKTOP -- "zbus 4 (blocking)" --> NM_MAIN
    DESKTOP --> NM_SETTINGS
    DESKTOP --> NM_DEVICE
    DESKTOP --> NM_WIFI
    DESKTOP --> NM_AP
    DESKTOP --> NM_ACTIVE
Loading

Key modules

Module Responsibility
lib.rs Tauri plugin entry point, state management (Arc<RwLock>), network change event emitter with 250 ms debounce
desktop.rs All D-Bus calls via zbus 4: network state, Wi-Fi scan/connect, VPN CRUD, signal listening
commands.rs 19 #[tauri::command] functions bridging IPC to desktop.rs
nm_helpers.rs Security type detection (KeyMgmt / WpaFlags / RsnFlags), SSID byte→string conversion, connectivity check
network_stats.rs Bandwidth tracker reading /sys/class/net/<iface>/statistics
error.rs Typed error enum with thiserror, D-Bus error mapping
models.rs All data structures: NetworkInfo, WiFiConnectionConfig, VpnProfile, etc.

Requirements

  • Linux with NetworkManager running on D-Bus system bus
  • Tauri 2 project
  • Rust 1.77.2+
  • System D-Bus libraries (libdbus-1-dev or equivalent)

Installation

Cargo

[dependencies]
tauri-plugin-network-manager = { git = "https://github.com/Vasak-OS/tauri-plugin-network-manager" }

NPM (guest bindings)

bun add @vasakgroup/plugin-network-manager
# or
npm install @vasakgroup/plugin-network-manager
# or
pnpm add @vasakgroup/plugin-network-manager

Register the plugin

// src-tauri/src/lib.rs
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_network_manager::init())
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

Permissions

By default the plugin allows read + Wi-Fi connect/disconnect operations. VPN mutations are opt-in.

Default permission set

// src-tauri/capabilities/default.json
{
  "identifier": "default",
  "windows": ["main"],
  "permissions": [
    "network-manager:default"
  ]
}

The default set includes:

  • get-network-state, list-wifi-networks, rescan-wifi
  • connect-to-wifi, disconnect-from-wifi
  • get-saved-wifi-networks, delete-wifi-connection
  • toggle-network-state, get-wireless-enabled, set-wireless-enabled
  • is-wireless-available, list-vpn-profiles, get-vpn-status

VPN management permission

{
  "permissions": [
    "network-manager:vpn_management",
    // or individually:
    "network-manager:allow-connect-vpn",
    "network-manager:allow-disconnect-vpn",
    "network-manager:allow-create-vpn-profile",
    "network-manager:allow-update-vpn-profile",
    "network-manager:allow-delete-vpn-profile"
  ]
}

Quick Start

Wi-Fi — scan and connect

import {
  listWifiNetworks,
  connectToWifi,
  disconnectFromWifi,
  WiFiSecurityType,
} from '@vasakgroup/plugin-network-manager';

// 1. Scan available networks
const networks = await listWifiNetworks();
console.log(networks.map(n => `${n.ssid} (${n.signal_strength}%)`));

// 2. Connect to a WPA2-PSK network
await connectToWifi({
  ssid: 'MyNetwork',
  password: 'supersecret',
  securityType: WiFiSecurityType.WPA2_PSK,
});

// 3. Disconnect
await disconnectFromWifi();

VPN — create, connect, and listen

import {
  createVpnProfile,
  connectVpn,
  getVpnStatus,
  disconnectVpn,
  deleteVpnProfile,
} from '@vasakgroup/plugin-network-manager';
import { listen } from '@tauri-apps/api/event';
import type { VpnEventPayload } from '@vasakgroup/plugin-network-manager';

// Listen for VPN state changes
const unlisten = await listen<VpnEventPayload>('vpn-changed', (e) => {
  console.log('VPN state:', e.payload.status.state);
});

// Create and connect
const profile = await createVpnProfile({
  id: 'Office VPN',
  vpn_type: 'wire-guard',
  settings: { endpoint: 'vpn.example.com:51820' },
  secrets: { private_key: '***' },
});

await connectVpn(profile.uuid);
console.log(await getVpnStatus());

// Cleanup
await disconnectVpn(profile.uuid);
await deleteVpnProfile(profile.uuid);
unlisten();

Network monitoring

import { getNetworkStats } from '@vasakgroup/plugin-network-manager';

const stats = await getNetworkStats();
console.log({
  download: `${stats.download_speed} B/s`,
  upload: `${stats.upload_speed} B/s`,
  totalDown: `${(stats.total_downloaded / 1e6).toFixed(1)} MB`,
  uptime: `${stats.connection_duration}s`,
});

API Reference

getCurrentNetworkState(): Promise<NetworkInfo>

Returns the currently active network connection info, or a default if disconnected.

listWifiNetworks(options?: ListWifiNetworksOptions): Promise<NetworkInfo[]>

Scans and returns all visible access points, deduplicated by SSID, sorted by signal strength.

Option Type Default Description
forceRefresh boolean false Bypass in-memory cache
ttlMs number 3000 Cache TTL in milliseconds (250–30000)

rescanWifi(): Promise<NetworkInfo[]>

Triggers a RequestScan D-Bus call on all wireless devices and returns fresh results.

connectToWifi(config: ConnectToWifiInput | WiFiConnectionConfig): Promise<void>

Accepts both camelCase (frontend-friendly) and snake_case (Rust wire) formats.

Field Type Required Description
ssid string yes Network SSID
password string no PSK / password
securityType / security_type WiFiSecurityType yes See WiFiSecurityType
username string no WPA-EAP identity

Supported security types:

Value D-Bus key-mgmt Notes
None Open network
Wep none Static WEP with wep-key0
WpaPsk wpa-psk WPA1-PSK
Wpa2Psk wpa-psk + proto=["rsn"] WPA2-PSK
Wpa3Psk sae WPA3-SAE
WpaEap wpa-eap Enterprise WPA-EAP

disconnectFromWifi(): Promise<void>

Deactivates the active Wi-Fi connection.

getSavedWifiNetworks(): Promise<NetworkInfo[]>

Returns saved Wi-Fi profiles (known networks) from NM settings, with SSID and detected security type.

deleteWifiConnection(ssid: string): Promise<void>

Deletes the first saved connection matching the given SSID.

toggleNetwork(enabled: boolean): Promise<boolean>

Enables or disables all networking. Returns the new state.

getWirelessEnabled(): Promise<boolean>

Returns whether Wi-Fi radio is enabled.

setWirelessEnabled(enabled: boolean): Promise<void>

Enables or disables Wi-Fi radio.

isWirelessAvailable(): Promise<boolean>

Returns true if at least one Wi-Fi adapter is present.

listVpnProfiles(): Promise<VpnProfile[]>

Returns all VPN profiles sorted by ID.

getVpnStatus(): Promise<VpnStatus>

Returns the current active VPN state, profile info, gateway, and IP.

connectVpn(uuid: string): Promise<void>

Activates a VPN by UUID. Throws VpnAlreadyConnected if already connected to the same profile.

disconnectVpn(uuid?: string): Promise<void>

Deactivates a VPN. If uuid is omitted, deactivates the currently active VPN (VpnNotActive if none).

createVpnProfile(config: VpnCreateInput): Promise<VpnProfile>

Creates a new VPN profile in NM settings. Requires vpn_management permission.

Field Type Required Description
id string yes Profile display name
vpn_type VpnType yes See VpnType
autoconnect boolean no Auto-connect on boot
username string no VPN username
gateway string no Remote gateway address
ca_cert_path string no CA certificate path
user_cert_path string no User certificate path
private_key_path string no Private key path
private_key_password string no Private key passphrase
settings Record<string, string> no Custom VPN settings
secrets Record<string, string> no Custom VPN secrets (e.g. psk, private_key)

updateVpnProfile(config: VpnUpdateInput): Promise<VpnProfile>

Updates an existing VPN profile. All fields except uuid are optional.

Field Type Description
uuid string Profile UUID to update (required)
id string New display name
autoconnect boolean New auto-connect preference
username string New username
gateway string New remote address
ca_cert_path string New CA cert path
user_cert_path string New user cert path
private_key_path string New private key path
private_key_password string New passphrase
settings Record<string, string> Merged into existing settings
secrets Record<string, string> Merged into existing secrets

deleteVpnProfile(uuid: string): Promise<void>

Deletes a VPN profile by UUID.

getNetworkStats(): Promise<NetworkStats>

Returns bandwidth statistics for the active interface.

interface NetworkStats {
  download_speed: number;    // bytes/sec
  upload_speed: number;      // bytes/sec
  total_downloaded: number;  // bytes
  total_uploaded: number;    // bytes
  connection_duration: number; // seconds
  interface: string;         // e.g. "wlan0"
}

getNetworkInterfaces(): Promise<string[]>

Returns all non-loopback network interface names.


Event System

The plugin emits Tauri events when network state changes, with a 250 ms debounce window to coalesce rapid transitions.

Events

Event Payload When
network-changed NetworkInfo Any network state transition
vpn-changed VpnEventPayload VPN state transition
vpn-connected VpnEventPayload Transition to Connected
vpn-disconnected VpnEventPayload Transition to Disconnected
vpn-failed VpnEventPayload Transition to Failed

Example

import { listen } from '@tauri-apps/api/event';
import type { NetworkInfo, VpnEventPayload } from '@vasakgroup/plugin-network-manager';

// Network changes
const unlistenNet = await listen<NetworkInfo>('network-changed', (event) => {
  const n = event.payload;
  const status = n.is_connected ? `connected to ${n.ssid}` : 'disconnected';
  console.log(`[${n.connection_type}] ${status} (${n.ip_address})`);
});

// VPN failures
const unlistenFail = await listen<VpnEventPayload>('vpn-failed', (event) => {
  console.error(`VPN failed: ${event.payload.reason}`);
});

// Cleanup when leaving component
// unlistenNet();
// unlistenFail();

Error Handling

All commands throw a typed NetworkManagerError with a code property:

import { connectToWifi, NetworkManagerErrorCode } from '@vasakgroup/plugin-network-manager';

try {
  await connectToWifi({ ssid: '...', password: '...', securityType: 'wpa2-psk' });
} catch (e) {
  if (e.code === NetworkManagerErrorCode.CONNECTION_FAILED) {
    showToast('Connection failed. Check password.');
  } else if (e.code === NetworkManagerErrorCode.UNSUPPORTED_SECURITY) {
    showToast('This security type is not supported.');
  } else {
    showToast(`Unexpected error: ${e.message}`);
  }
}

Error codes

Code Meaning
NOT_INITIALIZED Plugin not initialized
NO_CONNECTION No active network
PERMISSION_DENIED Capability not granted
UNSUPPORTED_SECURITY Security type not implemented
CONNECTION_FAILED D-Bus connection call failed
NETWORK_NOT_FOUND SSID not in saved networks
OPERATION_FAILED Generic failure
VPN_PROFILE_NOT_FOUND UUID not in settings
VPN_ALREADY_CONNECTED Already connected to that profile
VPN_AUTH_FAILED Secret/authentication error
VPN_INVALID_CONFIG Bad profile parameters
VPN_ACTIVATION_FAILED ActivateConnection D-Bus error
VPN_PLUGIN_UNAVAILABLE NM plugin not found
VPN_NOT_ACTIVE No active VPN to disconnect
UNKNOWN Fallback

Caching

Wi-Fi scan results are cached in-memory for 3 seconds by default to avoid redundant D-Bus calls.

// Bypass cache
const fresh = await listWifiNetworks({ forceRefresh: true });

// Custom TTL (500 ms)
const fast = await listWifiNetworks({ ttlMs: 500 });

// rescanWifi always invalidates cache and returns fresh data

Types

WiFiSecurityType

type WiFiSecurityType =
  | 'none'
  | 'wep'
  | 'wpa-psk'
  | 'wpa-eap'
  | 'wpa2-psk'
  | 'wpa3-psk';

VpnType

type VpnType =
  | 'open-vpn'
  | 'wire-guard'
  | 'l2tp'
  | 'pptp'
  | 'sstp'
  | 'ikev2'
  | 'fortisslvpn'
  | 'open-connect'
  | 'generic';

VpnConnectionState

type VpnConnectionState =
  | 'disconnected'
  | 'connecting'
  | 'connected'
  | 'disconnecting'
  | 'failed'
  | 'unknown';

NetworkInfo

interface NetworkInfo {
  name: string;
  ssid: string;
  connection_type: string;      // "wifi" | "Ethernet" | "Unknown"
  icon: string;                  // icon name for the UI
  ip_address: string;            // "0.0.0.0" when disconnected
  mac_address: string;
  signal_strength: number;       // 0–100
  security_type: WiFiSecurityType;
  is_connected: boolean;
}

VpnProfile

interface VpnProfile {
  id: string;
  uuid: string;
  vpn_type: VpnType;
  interface_name: string | null;
  autoconnect: boolean;
  editable: boolean;
  last_error: string | null;
}

VpnStatus

interface VpnStatus {
  state: VpnConnectionState;
  active_profile_id: string | null;
  active_profile_uuid: string | null;
  active_profile_name: string | null;
  ip_address: string | null;
  gateway: string | null;
  since_unix_ms: number | null;
}

VpnEventPayload

interface VpnEventPayload {
  status: VpnStatus;
  profile: VpnProfile | null;
  reason: string | null;  // populated on 'vpn-failed'
}

Package Exports

The NPM package provides ESM, CJS, and TypeScript declarations:

{
  "main": "./dist-js/index.cjs",
  "module": "./dist-js/index.js",
  "types": "./dist-js/index.d.ts",
  "exports": {
    "types": "./dist-js/index.d.ts",
    "import": "./dist-js/index.js",
    "require": "./dist-js/index.cjs"
  }
}

Testing & Build

# Build the Rust crate
cargo build

# Run Rust tests
cargo test

# Build NPM guest package
bun run build

# Run smoke tests (requires Tauri project)
bun run test

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feat/my-feature)
  3. Commit your changes (git commit -am 'feat: add ...')
  4. Push (git push origin feat/my-feature)
  5. Open a Pull Request

Development tips

  • Use RUST_LOG=debug or RUST_LOG=trace to enable D-Bus call logging
  • Monitor NM D-Bus traffic: busctl monitor org.freedesktop.NetworkManager
  • Test Wi-Fi connection flow: nmcli dev wifi list then nmcli connection add ...

License

GPL-3.0-or-later — see LICENSE.

About

Administrar las redes puede ser tedioso, por eso mismo creamos este plugin de tauri para poder hacer mas facil las tareas en nuestro sistema

Topics

Resources

Stars

Watchers

Forks

Contributors