Skip to content

Network

P3DROVFX edited this page Oct 12, 2026 · 1 revision

Network, VPN and Tailscale

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.

Wi-Fi and Ethernet

  • 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 (lan icon, 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 nmcli sees 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 the nmcli path 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), NetworkFallback reads the same facts from nmcli so 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 nmcli seed 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 nmcli runs; Bluetooth discovery and hotspot probes wait for the first frame.

Hotspot

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).

VPN

VpnService manages several backends behind one toggle and dialog:

  • NetworkManager — import .ovpn and WireGuard .conf files (with a native file picker, falling back to nmcli connection import), list and connect profiles.
  • NordVPN and Proton VPN (nordvpn / protonvpn CLIs) — 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 its settings.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.

Tailscale

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.

DNS over TLS

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).

Speed test

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).

Network speed

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).

Under the hood

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

Related pages

← Back to Home

Clone this wiki locally