Repository navigation
Network
Everything about getting and staying connected lives in the shell: Wi-Fi, Ethernet, hotspot, saved profiles, a VPN manager (NetworkManager OpenVPN/WireGuard, NordVPN, Proton VPN, Cloudflare WARP), Tailscale, DNS over TLS, a speed test, and live network speed readouts. Almost all of it is in Settings → Network (tabs: Wi-Fi, Bluetooth, Hotspot, Wired), Settings → Privacy & Content (VPN and Tailscale subpages), the Dashboard's quick toggles and dialogs, and the launcher.
Wi-Fi and wired state come from Quickshell's native NetworkManager D-Bus backend: no polling, no text parsing — signal strength and connectivity stay right without a timer. Writes that need a settings dictionary go through nmcli/NetworkManager commands, and Network.qml is the single facade the bar, sidebars and mode conditions bind to.
-
Dashboard dialog — the Wi-Fi quick toggle opens an inline dialog (networks, saved profiles, connect with password or enterprise credentials). When a cable is connected the dialog starts with a wired card (
lanicon, profile name, link speed) with inline Auto (autoconnect on cable) and Managed (hand the port to / take it back from NetworkManager) toggles that write straight to the device; at the bottom a connection details grid shows profile, transport, download, upload, link strength/speed and IPv4. The details always follow the active interface — a connected cable takes priority over Wi-Fi. - Settings → Network → Wi-Fi — scan, connect, forget, hidden networks, WPA2/WPA3-Enterprise (802.1X), a profile editor for saved connections. Wired has the same for Ethernet ports and saved wired connections.
-
Scanning only while looked at. NetworkManager scans rarely on its own, so the native backend's list can hold a handful of networks while
nmclisees many more. Surfaces that show a network list ask for a scan while they are on screen (Network.setWifiScanHolder(key, bool)— holders are keyed, not counted, so a surface that asks twice or releases something it never held can't leave the radio scanning forever). On thenmclipath a silent 10-second rescan runs while held, without a spinner. -
A fallback for odd machines. If the native backend enumerates no adapter although NetworkManager is healthy (an older NetworkManager, or the backend resolved before NetworkManager was on the bus and can't re-resolve),
NetworkFallbackreads the same facts fromnmcliso you still get Wi-Fi in the toggle and the settings page. It sleeps whenever the backend works. -
Cold start. The backend needs a second or two to list devices; until then a one-shot
nmcliseed keeps the bar from claiming "no adapter". -
Performance of the settings page. Detail queries (Wi-Fi details, saved profiles, IP/DNS) start only when the Wi-Fi tab asks for them after its first frame; long lists are virtualized with loaders that reserve the collapsed height; the first profile read is delayed 350 ms and refreshes are coalesced while one
nmcliruns; Bluetooth discovery and hotspot probes wait for the first frame.
Settings → Network → Hotspot: share your connection as a Wi-Fi access point — name (SSID, optionally not broadcast), password (8–63 characters, WPA2 or WPA3; an Open hotspot warns that anyone in range can read everyone's traffic), band (2.4 GHz reaches further, 5 GHz is faster and quieter), and a list of connected devices. It's stored in NetworkManager, not the shell's config, so it is the same hotspot nmcli sees.
The page tells you what won't work before you try, because NetworkManager happily accepts a shared connection on a machine that can't serve one: sharing spawns dnsmasq (not a NetworkManager dependency — without it devices join and get no address or DNS), needs iptables or nftables for NAT, needs an adapter that can act as an access point, and, on a single radio, the hotspot must use the channel of the network it shares (the band choice is ignored while that network is connected, and a radio that can't do both will drop the current connection). Listing connected devices needs iw. Install them with the hotspot feature (ii-p3drovfx deps add hotspot, see Installer and CLI).
VpnService manages several backends behind one toggle and dialog:
-
NetworkManager — import
.ovpnand WireGuard.conffiles (with a native file picker, falling back tonmcli connection import), list and connect profiles. -
NordVPN and Proton VPN (
nordvpn/protonvpnCLIs) — country or server as the default location; Proton adds Secure Core (routes through a Secure Core server first; the default location becomes the exit country) and exposes Proton's own kill switch (the setting is Proton's — the shell reads and writes itssettings.json, and the CLI only accepts changes while disconnected). -
Cloudflare WARP (
warp-cli).
Operations run through an ordered, non-blocking queue; the toggle shows the pending target state immediately while the tunnel opens or closes. Options (Config.options.vpn): enabled, autoConnect (connect the default profile at shell start or when the network appears), defaultProvider, defaultProfile, defaultLocation, protonSecureCore, disconnectOnDisable, killSwitch, blockLan (shown as unsupported by backend where it is), enableDiagnostics.
Stale-connection reset (optional hook). Connections opened before a VPN connects, disconnects or switches server keep their old local address; the new route drops their packets silently and apps hang until their own timeout, which they report as "no internet". The shell offers a NetworkManager dispatcher hook (scripts/vpn/90-ii-vpn-stale-sockets) that, on vpn-up/vpn-down, destroys (ss -K) the established TCP connections whose local address no longer belongs to the interface their peer now routes through, so apps reconnect at once. It runs as root, so NetworkManager only accepts a root-owned copy: the Privacy page shows the install command (sudo install -Dm755 … /etc/NetworkManager/dispatcher.d/90-ii-vpn-stale-sockets), detects when the installed copy is out of date and offers to update or remove it. II_DRY_RUN=1 lists what it would reset without doing it. Optional features: vpn-proton, vpn-nord, vpn-warp in the installer.
TailscaleService reads tailscale status --json and shows the state (Running, Stopped, NeedsLogin), your tailnet peers (optionally hidden), and offers: connect / disconnect, an interactive login (when the backend needs one the shell extracts the URL and opens it in your browser), exit nodes (pick a peer; remembered in tailscale.exitNode), advertise this device as an exit node, advertise subnet routes (comma-separated CIDRs), Accept MagicDNS, Tailscale SSH, Shields-up (block incoming), netcheck and ping diagnostics, and an option to stop tailscaled entirely when the integration is turned off (via polkit). If tailscale needs operator permission the error offers sudo tailscale set --operator=$USER. Settings: Privacy & Content → Tailscale; the quick toggle's dialog shows the connected node as a primary-colour pill with a disconnect button, and the bar's dashboard button can show reveal-on-active vpn_lock and hub indicators.
A quick toggle (and a Settings section) that sets per-link DNS-over-TLS on top of systemd-resolved: enabling points the default-route link at the configured resolver (resolvectl dns <link> <ip>#<name>) and turns DoT on for it; disabling runs resolvectl revert <link>, restoring whatever NetworkManager/DHCP handed out — saved connection profiles are never touched. Because per-link settings are runtime-only, the shell re-applies them when the default route changes or the link reconfigures (reapplyOnNetworkChange). Presets: AdGuard (default), AdGuard Family, Cloudflare, Cloudflare Security, Quad9 and Mullvad; strict means the server certificate name is verified (yes), otherwise opportunistic. resolvectl needs polkit authorisation: the repository ships scripts/polkit/49-resolve1-wheel.rules, which lets wheel members change only per-link DNS, DoT and search domains without a password so the toggle is instant (sudo install -Dm644 … /etc/polkit-1/rules.d/49-resolve1-wheel.rules; no restart). Config: dnsOverTls.* (enabled, preset, serverName, serverAddress, fallbackAddress, strict, routeAllQueries, reapplyOnNetworkChange).
Type % (or search speed) in the launcher: a built-in test against Cloudflare's anycast edge (speed.cloudflare.com, with a fallback host for download) measuring ping, jitter, download and upload with live graphs and peaks, in Mbps or MB/s, for 10 s per direction by default (5, 10 or 15; search.speedTest.duration, mode: both/download/upload, unit) — see Launcher & Search for its keys. It keeps running after the launcher closes and shows as a background job in the Dynamic Island; cancellable; offline and connection-lost errors are distinguished. No third-party CLI needed (scripts/speedtest/speedtest_runner.py, standard library, streams NDJSON).
NetworkSpeed samples /proc/net/dev once a second for the active interface — one file reload, no child process — shared (reference-counted by name) by the Wi-Fi dialog and the bar's Network speed widget (download/upload with a short history graph; see Bar & Dynamic Island).
| Piece | Role |
|---|---|
services/NetworkState.qml, NetworkCommands.qml, Network.qml
|
Native-backend state, writes, the facade |
services/NetworkFallback.qml, NetworkProfiles.qml
|
nmcli fallback; saved profiles |
services/Hotspot.qml |
Capability probes and the hotspot profile |
services/VpnService.qml, TailscaleService.qml, DnsOverTls.qml
|
Privacy services |
services/SpeedTestService.qml, NetworkSpeed.qml
|
Measurements |
modules/settings/configs/network/, widgets/VPNConfig.qml, widgets/TailscaleConfig.qml
|
Settings |
scripts/vpn/, scripts/polkit/, scripts/speedtest/
|
Hook, polkit rule, runner |
- System Integration
- Sidebar Dashboard — the quick toggles and dialogs
- Launcher & Search — speed test
- Bar & Dynamic Island
- Known Issues