WPA-PSK Networks

WPA-PSK (Wi-Fi Protected Access with Pre-Shared Key) is the most common security type for home and small-office Wi-Fi networks. You provide a password, and nmrs handles the WPA handshake.

Connecting with a Password

use nmrs::{NetworkManager, WifiSecurity};

#[tokio::main]
async fn main() -> nmrs::Result<()> {
    let nm = NetworkManager::new().await?;

    nm.connect("HomeWiFi", None, WifiSecurity::WpaPsk {
        psk: "my_secure_password".into(),
    }).await?;

    println!("Connected!");
    Ok(())
}

The WifiSecurity::WpaPsk variant works with WPA, WPA2, and WPA3 Personal networks. NetworkManager negotiates the strongest supported protocol automatically.

WPA3-Personal (SAE)

WpaPsk emits key-mgmt=wpa-psk, which WPA3-Personal access points reject when they advertise SAE without PSK. connect() detects that case from the access point's own security flags and switches to SAE for you, so the snippet above works unchanged on an SAE-only network.

Transition-mode access points advertise both SAE and PSK and accept WpaPsk, so those are left as-is. To request SAE explicitly — when building settings yourself, or to pin key management regardless of what the AP advertises — use the variant directly:

#![allow(unused)]
fn main() {
nm.connect("Wpa3Only", None, WifiSecurity::Sae {
    psk: "my_secure_password".into(),
}).await?;
}

Unlike WPA-PSK, SAE has no 8-character minimum passphrase length.

Password Requirements

  • A new profile requires a non-empty password. An empty PSK requests the stored password only when a saved profile already exists; otherwise nmrs returns ConnectionError::MissingPassword.
  • WPA-PSK passwords are typically 8–63 characters (ASCII passphrase) or exactly 64 hex characters (raw PSK)
  • nmrs passes the password directly to NetworkManager, which handles validation

Reading the Password at Runtime

Avoid hardcoding passwords. Read them from environment variables, user input, or a secrets manager:

use nmrs::{NetworkManager, WifiSecurity};

#[tokio::main]
async fn main() -> nmrs::Result<()> {
    let nm = NetworkManager::new().await?;

    let password = std::env::var("WIFI_PASSWORD")
        .expect("Set WIFI_PASSWORD environment variable");

    nm.connect("HomeWiFi", None, WifiSecurity::WpaPsk {
        psk: password,
    }).await?;

    Ok(())
}

Reconnecting to Saved Networks

After the first successful connection, NetworkManager saves the credentials in a connection profile. Request its stored password with an empty PSK:

#![allow(unused)]
fn main() {
let nm = NetworkManager::new().await?;

if nm.has_saved_connection("HomeWiFi").await? {
    nm.connect("HomeWiFi", None, WifiSecurity::WpaPsk {
        psk: String::new(),
    }).await?;
}
}

WifiSecurity::Open also reuses an existing saved profile. A non-empty PSK is not ignored: it asks nmrs to build a fresh profile with that password. If activation with the empty-PSK stored-secret request fails, the original saved profile is preserved.

Error Handling

The most common errors for WPA-PSK connections:

ErrorMeaning
ConnectionError::AuthFailedWrong password
ConnectionError::MissingPasswordEmpty password string and no saved profile to reuse
ConnectionError::NotFoundNetwork not in range
ConnectionError::TimeoutConnection took too long
ConnectionError::DhcpFailedConnected to AP but DHCP failed
#![allow(unused)]
fn main() {
use nmrs::{NetworkManager, WifiSecurity, ConnectionError};

let nm = NetworkManager::new().await?;

match nm.connect("HomeWiFi", None, WifiSecurity::WpaPsk {
    psk: "password".into(),
}).await {
    Ok(_) => println!("Connected!"),
    Err(ConnectionError::AuthFailed) => {
        eprintln!("Wrong password — check and try again");
    }
    Err(ConnectionError::MissingPassword) => {
        eprintln!("No saved password is available; provide a non-empty PSK");
    }
    Err(e) => eprintln!("Error: {}", e),
}
}

Next Steps