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

0 stars

Watchers

1 watching

Forks

Contributors