Neutron Documentation Wiki
Welcome to the Neutron technical documentation and developer wiki.
Neutron is a high-performance WireGuard manager for Linux written in Rust, leveraging NetworkManager as the system-native networking control plane.
Wiki Contents
1. Usage Guide (TUI & CLI)
Complete interactive guide to the Terminal User Interface (TUI), keybindings, modal navigation, and scriptable CLI command reference.
2. System Architecture & Design Principles
Detailed overview of the decoupled subsystem architecture, module boundaries, data flows, thread model, and comparative analysis of UI frontends (CLI, TUI, GUI, Electron).
3. NetworkManager Integration
How Neutron interfaces with NetworkManager, nmcli command execution, batch error aggregation, profile discovery,
interface comment extraction, and connection lifecycle.
4. Security Architecture (Kill Switch & Lockdown)
Comprehensive explanation of the two security layers: Layer 3 NetworkManager policy routing (fwmark, exclusive DNS
priorities) and Netfilter/Firewalld always-on OUTPUT filtering.
5. Split Tunneling (IP & Domain Routing)
Architecture of global split tunneling, Include vs. Exclude routing modes, CIDR normalization (/32 & /128),
client-side DNS resolution, and NetworkManager route injection.
6. NAT-PMP Port Forwarding Engine
Pure Rust UDP implementation of the NAT-PMP protocol (RFC 6886), tunnel gateway derivation, mapping request framing, lease renewal timers, and clipboard integration.
7. User Configuration & Theming (config.toml)
Specification for the human-readable TOML configuration, managed profile drop directory (profiles/) auto-sync,
built-in themes (Osaka Jade, Catppuccin, Nord, Gruvbox, Monochrome), and color customization.
8. Packaging & Universal Distribution
Packaging guides and distribution models for Homebrew tap formulas, Arch Linux AUR, and static musl compilation for headless servers.
9. Implementation Notes
Startup selection, checked activation, configuration persistence, and saved versus effective policy state.
10. Testing & Quality Checks
Formatting, Clippy, host tests, and disposable NetworkManager/firewall sandbox tests.
Core Invariants
- NetworkManager as Single Source of Truth: All WireGuard profile parameters and secrets remain stored exclusively inside NetworkManager profile storage.
- Decoupled Business Logic: No domain logic (routing, firewall, config, port forwarding) is coupled to GTK or terminal drawing code.
- Apply-Before-Persist: Network operations are executed first; persisted application configurations are only updated if the underlying network mutation succeeds.
- Fail-Safe Security: Kill Switch and Lockdown teardowns are strictly surgical and guaranteed never to lock the user out permanently.
Usage Guide (TUI & CLI)
Neutron provides both an interactive, full-featured Terminal User Interface (TUI) and a comprehensive, scriptable Command-Line Interface (CLI).
1. Interactive Terminal User Interface (TUI)
The TUI is the primary, most user-friendly way to interact with Neutron. It provides real-time telemetry, zero-latency profile browsing, interactive security toggles, and live configuration management.
Starting the TUI
Launch the TUI simply by running:
neutron
# or explicitly:
neutron tui
TUI Screen Layout
- Header (Top):
- Status Panel: Live active profile, connection indicator, public IP, ping latency, download/upload throughput rates (
/proc/net/dev), and active NAT-PMP forwarded port. - Policies Panel: Real-time status pills for Kill Switch, Lockdown Firewall, Split Tunneling, Port Forwarding, and Auto-Connect at login.
- Status Panel: Live active profile, connection indicator, public IP, ping latency, download/upload throughput rates (
- Main Body:
- Left (Profile Browser): Lists all NetworkManager WireGuard profiles with active checkmarks (
●), favorite stars (★), and pool exclusion badges (⊘). - Right (Details Pane): Full connection diagnostics including remote peer endpoint, allowed IPs, latest handshake age, cumulative RX/TX transfer counters, persistent keepalive, and assigned interface IP.
- Left (Profile Browser): Lists all NetworkManager WireGuard profiles with active checkmarks (
- Footer: Quick keybinding shortcuts and status messages.
TUI Keybindings
Every action below is also searchable by name from the Command Palette (Ctrl+P or :).
| Key | Action | Description |
|---|---|---|
↑ / ↓ (or p / n) | Navigate | Move cursor up / down through the profile list |
Space / Enter | Connect / Disconnect | Connect to the selected profile, or disconnect if already active |
s | Switch Profile | Instantly switch active tunnel to the selected profile |
f | Toggle Favorite | Star/unstar profile for quick tray indicator access |
e | Toggle Eligibility Pool | Include or exclude selected profile from random boot connection pool |
a | Auto-Connect at Login | Toggle automated random profile connection on login |
t | Split Tunneling | Open interactive Split Tunneling manager (Domains & Subnets) |
k | Kill Switch | Toggle NetworkManager-native routing kill switch |
l | Lockdown Mode | Toggle always-on Netfilter firewall (requires pkexec root) |
o | Port Forwarding | Toggle NAT-PMP dynamic port leasing and renewal |
r | Sync Drop Directory | Scan ~/.config/neutron/profiles/ and batch-import new .conf files |
d / Delete | Delete Profile | Permanently remove selected profile from NetworkManager (with confirmation) |
Ctrl+P / : | Command Palette | Searchable fuzzy popup for all commands and actions |
Ctrl+T | Theme Picker | Switch themes live (nord, osaka-jade, catppuccin, gruvbox, monochrome) |
? / h | Help | Display keybindings help modal |
q / Esc | Quit | Exit the TUI cleanly |
Interactive Split Tunneling (t)
Pressing t opens the side-by-side Split Tunneling manager:
- Mode Selector (Top): Use
←/→to move between[ Disabled ],[ Include ], and[ Exclude ], andSpace/Enterto activate. - Domains (Left Column): Type a domain (e.g.
github.com) into the+ Add Domainbox and pressEnter. - Subnets / CIDRs (Right Column): Type an IPv4/IPv6 CIDR (e.g.
10.0.0.0/8or192.168.1.0/24) and pressEnter. - Navigation:
Tabcycles between Mode, Domains, and Subnets. Arrow keys (←,→,↑,↓) navigate between panels and list items. - Deletion: Select any entry in the list and press
Delorx. - Automatic Non-Blocking Apply: All changes apply and persist immediately in the background without freezing the UI.
- Lockdown Notice: If the Lockdown Firewall is active, a clear banner reminds you that all non-tunnel traffic is dropped when disconnected.
Command Palette (Ctrl+P or :)
Press Ctrl+P or : to open the Command Palette. Type any keyword (e.g. “kill”, “split”, “theme”, “sync”) to filter actions, then press Enter to execute.
Theme Picker (Ctrl+T)
Press Ctrl+T to switch between calibrated color palettes live without restarting the app:
2. Command Line Interface (CLI)
All operations can also be run directly from terminal commands or shell scripts.
Profile Connections & Status
# List all WireGuard profiles with active status and eligibility
neutron list
# Connect to a profile by name or UUID
neutron connect "Home-Server"
# Switch connection directly to another profile
neutron switch "Work-VPN"
# Disconnect active tunnel
neutron disconnect
Profile Ingestion & Drop Directory
# Batch-import new or updated *.conf files from ~/.config/neutron/profiles/
neutron sync
# Manage random-on-boot eligibility pool
neutron eligible list
neutron eligible add "Home-Server"
neutron eligible remove "Test-Server"
Global Split Tunneling
# Check current split tunneling status and active routes
neutron split-tunnel status
# Set routing mode
neutron split-tunnel set-mode include
neutron split-tunnel set-mode exclude
neutron split-tunnel set-mode disabled
# Add destination subnets or domains
neutron split-tunnel add-cidr 10.0.0.0/8
neutron split-tunnel add-cidr 192.168.1.0/24
neutron split-tunnel add-domain internal.corp
neutron split-tunnel add-domain gitlab.company.com
# Remove destinations
neutron split-tunnel remove-cidr 10.0.0.0/8
neutron split-tunnel remove-domain internal.corp
# Clear all split tunnel routes
neutron split-tunnel clear
Security & Firewalls
# Inspect or toggle NetworkManager-native routing kill switch
neutron kill-switch status
neutron kill-switch enable
neutron kill-switch disable
# Inspect or toggle always-on Netfilter lockdown firewall (requires polkit/pkexec)
neutron lockdown status
neutron lockdown enable
neutron lockdown disable
NAT-PMP & qBittorrent Dynamic Port Sync
# Check qBittorrent integration status, WebUI connectivity, and active ports
neutron qbit status
# Test connection to qBittorrent WebUI
neutron qbit test
# Immediately sync active NAT-PMP port to qBittorrent
neutron qbit sync
# Enable / disable automated sync
neutron qbit enable
neutron qbit disable
# Configure WebUI connection parameters
neutron qbit config --url http://127.0.0.1:8080 --bind true
System Tray AppIndicator & Background Daemon
Neutron includes a pure-Rust D-Bus StatusNotifierItem and DBusMenu system tray indicator (src/service/indicator.rs via zbus) that monitors link health and provides quick desktop controls:
# Run persistent D-Bus system tray AppIndicator daemon
neutron indicator
# Terminate running background instances and launch fresh session
neutron restart
# Run one-shot random profile connection (used by boot/login automation)
neutron startup-random
System Architecture & Design Principles
Neutron is designed with a strictly decoupled architecture where all networking, security, and state logic exist in pure Rust modules that can be driven by any UI frontend (CLI, TUI, or GUI).
High-Level Architecture Diagram
┌─────────────────────────────────────────────────────────────────────────────┐
│ User Interface Layer │
│ ┌───────────────────────┐ ┌───────────────────────┐ ┌─────────────────┐ │
│ │ CLI (clap) │ │ GUI (Adwaita) │ │ TUI (ratatui) │ │
│ │ (Scripting & Headless)│ │ (GNOME Desktop Window)│ │ (Terminal UI) │ │
│ └───────────┬───────────┘ └───────────┬───────────┘ └────────┬────────┘ │
└──────────────┼──────────────────────────┼───────────────────────┼───────────┘
└──────────────────────────┼───────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Core Decoupled Engine │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ ┌─────────────┐ │
│ │ NetworkManager (nm/) │ │ Firewall (firewall/) │ │ NAT-PMP │ │
│ │ • Profile Discovery │ │ • Lockdown Netfilter │ │ (portforward│ │
│ │ • Policy Kill Switch │ │ • Surgical Teardown │ │ • UDP Lease │ │
│ │ • Split Tunnel Routes │ │ • pkexec Orchestration │ │ • Auto-Renew│ │
│ └─────────────────────────┘ └─────────────────────────┘ └─────────────┘ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ Config (config/) │ │ Service (service/) │ │
│ │ • Atomic Persistence │ │ • Random Boot Selector │ │
│ │ • Unix Mode 0600 │ │ • Autostart Unit │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
Subsystem Responsibilities
1. nm/ — NetworkManager Control Plane
- Trait
NmClientprovides an abstract interface for listing, connecting, disconnecting, switching profiles, setting kill-switch properties, and applying split-tunneling routes. CliNmClientinteracts with NetworkManager vianmcliwith a strict 30-second execution deadline.- Submodule
nm::split_tunnelvalidates and normalizes CIDRs, resolves domain names to IP addresses, and formatsipv4.routes/ipv6.routes/never-defaultarguments. - Submodule
nm::kill_switchconfigures kernel policy routing (wireguard.ip4-auto-default-route) and negative DNS priorities (-1500).
2. firewall/ — Always-On Lockdown Netfilter Engine
- Trait
FirewallClientmanages permanent directOUTPUTchain rules infirewalld. - Uses mangle OUTPUT allow-list rules and a final DROP before filter-table established accepts; see Security & Kill Switch.
- All rules are tagged with a unique comment (
neutron-lockdown) ensuring surgical removal without modifying user-defined firewall rules. - Privilege escalation is consolidated into one
pkexecshell batch, with permanent fail-closed guards during rebuilds.
3. portforward/ — NAT-PMP Dynamic Port Leasing & App Integrations
- Implements RFC 6886 NAT-PMP client directly over
std::net::UdpSocket. - Derives the gateway address from the local tunnel IPv4 address (
10.x.x.x/100.x.x.x). - Acquires dynamic UDP/TCP port mappings and schedules automatic lease renewals before expiration.
- Integrates
portforward::qbittorrentWeb API bridge to automatically synchronize dynamic listening ports to qBittorrent (native, Flatpak, containerized).
4. config/ — Configuration & State Persistence
- Manages
AppConfigserialized as TOML in~/.config/neutron/config.toml. - Implements atomic file writes (
fs::renamewith fallback across filesystem boundaries) with strict0o600permissions. - Stores policy intent, startup eligibility, favorites, theme settings, and integration settings. Narrow updates are serialized by a sidecar file lock.
5. service/ — Boot-Time Automation
- Implements the one-shot random profile selector for login / boot.
- Manages XDG desktop autostart entries (
~/.config/autostart/io.github.pandabytez.neutron.desktop). - Prevents immediate profile repeats and respects user-defined eligibility exclusion sets.
Frontend & Resource Comparison Matrix
| Metric | GTK4 / Libadwaita (GUI)UNRELEASED | Pure Rust TUI (ratatui) | Background Daemon / CLI | Electron / Web Clients |
|---|---|---|---|---|
| Binary Size | ~15–30 MB (or AppImage bundle) | ~3–5 MB (Static musl binary) | ~3 MB | 150–250 MB |
| Active RAM (RSS) | ~70 – 110 MB | ~10 – 15 MB | ~3 – 6 MB | 250 – 450 MB |
| Idle CPU Usage | 0.1% – 0.5% | 0.0% (sleeps on epoll) | 0.0% | 0.5% – 2.0% |
| Startup Time | ~150–300 ms | < 10 ms (instantaneous) | < 2 ms | 1.5 – 3.0 seconds |
| System Dependencies | GTK4, Libadwaita, Mesa/Wayland | Zero (100% static musl) | Zero | Node, Chromium, X11/Wayland |
| Primary Environments | GNOME Desktop Workstations | Servers, SSH, Hyprland, Sway, i3 | Automation, Cron, Systemd | Legacy Cross-Platform |
| Distribution Channels | AppImage, Distro Packages | Homebrew, Cargo, AUR, Static Musl | Homebrew, System Package | Custom Installers |
NetworkManager Integration
Neutron relies on NetworkManager as the source of truth for all WireGuard network configurations.
Why NetworkManager?
Direct usage of wg-quick creates ad-hoc network interfaces and routing tables outside the system networking daemon, often causing conflicts with system DNS (systemd-resolved), connection reconnects, Wi-Fi switching, and desktop status integration.
By integrating directly with NetworkManager:
- System Consistency: Profiles integrate cleanly with GNOME Shell, desktop networking indicators, and D-Bus network monitors.
- Key Security: Private keys remain stored securely within NetworkManager profile storage (
/etc/NetworkManager/system-connections/) rather than an unencrypted app database. - Hardware & Power Management: Sleep, resume, and interface roaming are handled natively by the Linux kernel and NetworkManager daemon.
Technical Details & Command Flow
1. Profile Discovery
Profiles of type wireguard are enumerated via:
nmcli -t -f NAME,UUID,TYPE connection show
nmcli -t -f NAME,UUID,TYPE connection show --active
The output is parsed into WireguardProfile structs with active/inactive states.
2. Timeouts & Concurrency
NetworkManager command helpers use process::run_with_timeout with a 30-second deadline (NMCLI_TIMEOUT). The long-lived nmcli monitor process is managed separately.
- Standard output and standard error pipes are drained concurrently on separate worker threads to prevent pipe buffer deadlocks.
- If a command exceeds the deadline, the child process is terminated and an explicit
AppError::CommandFailederror is returned. Failed commands surface their exit status.
3. Error Aggregation (apply_to_every_profile)
Autoconnect normalization processes every profile and aggregates failures. Other policy sweeps can stop on a failure and leave mixed profile settings; callers report that possible partial state. See Implementation Notes.
4. WireGuard Comment Ingestion
When importing .conf files via nmcli connection import type wireguard file <path>, comments inside the [Interface] section (often containing provider metadata, server features, or notes) are extracted and saved in profile-info.json beside the application settings, keyed by profile UUID. AppConfig.profile_custom_info remains the in-memory view used by the CLI and TUI.
Security Architecture (Kill Switch & Lockdown)
Neutron combines NetworkManager routing/DNS policy with an optional always-on firewall.
Defense Tiers Overview
┌─────────────────────────────────────────────────────────────────────────────┐
│ Defense Tier 1: │
│ Kill Switch (Layer 3 Routing Plane) │
│ • Active while WireGuard tunnel is UP │
│ • NetworkManager Policy Routing (`wireguard.ip4-auto-default-route = yes`) │
│ • Dedicated routing table + `fwmark` + `suppress_prefixlength 0` │
│ • Exclusive DNS priority (`ipv4.dns-priority = -1500`) │
│ • Drops traffic if tunnel fails; prevents fallback to physical gateway │
└──────────────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Defense Tier 2: │
│ Lockdown Mode (Netfilter Firewall Plane) │
│ • Active 24/7 (Even when tunnel is DISCONNECTED or RECONNECTING) │
│ • Permanent `firewalld` direct mangle OUTPUT rules (IPv4 & IPv6) │
│ • Allows: Loopback, configured tunnel interfaces, conditional DNS (53) │
│ • Allows: Peer Handshake Endpoints (Host:Port) │
│ • Allows: Private LAN (RFC 1918: 10.0.0.0/8, 192.168.0.0/16, DHCP, mDNS) │
│ • Blocks: Other outbound traffic via terminal DROP │
└─────────────────────────────────────────────────────────────────────────────┘
1. NetworkManager-Native Kill Switch
How It Works
The kill switch operates entirely through NetworkManager connection properties without introducing custom firewall scripts:
nmcli connection modify <uuid> \
wireguard.ip4-auto-default-route yes \
wireguard.ip6-auto-default-route yes \
ipv4.dns-priority -1500 \
ipv6.dns-priority -1500
Routing Invariants
- Dedicated Table Routing: NetworkManager places the tunnel default route into an isolated routing table guarded by an
fwmarkand asuppress_prefixlength 0rule. - Active-Connection Scope: Routing protection depends on the active connection’s routes and rules. Lockdown provides protection across disconnection and switching.
- DNS Priority: A negative DNS priority (
-1500) gives the tunnel’s DNS resolvers exclusive precedence over LAN/DHCP resolvers, eliminating DNS leaks to your local ISP.
The setting is global and applies to saved profiles for their next activation.
Automatic default-route handling is pinned independently of the DNS toggle;
full-tunnel routing requires a peer allowing 0.0.0.0/0 or ::/0. IPv6 policy
is adjusted for profiles without IPv6. See saved versus effective policy.
2. Always-On Lockdown Firewall
Why Lockdown is Needed
The Kill Switch only protects traffic while a tunnel connection is actively established. When disconnected, traffic flows normally over the physical interface.
Lockdown closes that gap by installing permanent firewalld direct rules in
mangle OUTPUT via a single batched pkexec invocation. This runs before
filter-table established-connection accepts on both firewalld backends.
Ruleset Hierarchy (OUTPUT Chain)
| Priority | Match Criteria | Target | Purpose |
|---|---|---|---|
| 0 | -o lo | ACCEPT | Allow local loopback communication |
| 1 | -p udp/tcp --dport 53 | ACCEPT / DROP | Allow bootstrap DNS when disconnected; block non-tunnel DNS when connected |
| 1 | -d <LAN_SUBNETS> | ACCEPT | Keep local LAN devices reachable (Router, Printer, NAS) |
| 0 | -o <TUNNEL_IFACE> | ACCEPT | Allow first traffic on configured WireGuard interfaces |
| 1 | -p udp -d <PEER_HOST> --dport <PEER_PORT> | ACCEPT | Allow encrypted WireGuard handshake packets out |
| 10 | All remaining packets | DROP | Block other outbound traffic before filter-table accepts |
Activation installs the target interface and connected DNS policy before bringing
the tunnel up. DNS drops precede LAN allowances. An ACCEPT here finishes this
table; it does not bypass other firewall policy.
Surgical Teardown Guarantee
Every single rule installed by Lockdown carries a marker:
-m comment --comment neutron-lockdown
When disabling Lockdown:
- Unprivileged reads enumerate permanent and runtime tagged rules in both mangle and legacy filter OUTPUT.
- A single privileged batch removes those rules individually, without reloading firewalld.
- Foreign runtime/permanent rules, custom chains, and rich rules are left untouched.
- Disable requires successful firewall authorization and execution; failures are reported.
Rebuild Recovery
Rebuilds install tagged priority -1 DROP guards for both address families before
replacing rules in each of the permanent and runtime configurations. Guards are removed only after the replacement is
complete. An interruption therefore remains fail-closed after reload or reboot,
but can block all outbound traffic. Retry enabling lockdown or explicitly disable
it to remove the guards and recover.
Sandbox tests exercise interrupted rebuilds, migration, teardown, and established IPv4/IPv6 egress on both firewalld backends.
Split Tunneling (IP & Domain Routing)
Neutron provides Global Split Tunneling, allowing users to specify exact subnets, IPs, and domain names that should route through or bypass the WireGuard tunnel.
Routing Modes
1. Include Mode (Route Only Listed Destinations via Tunnel)
- Behavior: Default internet browsing bypasses the tunnel over your physical interface, while only specified subnets and domain IPs are routed securely through the WireGuard tunnel.
- NetworkManager Mechanism:
nmcli connection modify <uuid> \ ipv4.never-default yes \ ipv6.never-default yes \ ipv4.routes "10.0.0.0/8, 192.168.10.0/24" \ ipv6.routes "2001:db8::/32" - Use Case: Remote network access where corporate/homelab subnets must route through the WireGuard tunnel while streaming, gaming, and personal browsing remain on high-speed unthrottled physical internet.
2. Exclude Mode (Bypass Tunnel for Listed Destinations)
-
Behavior: All general traffic routes through the encrypted WireGuard tunnel, while specified subnets or domains bypass the tunnel directly to your local physical gateway.
-
NetworkManager Mechanism:
never-default = yeswith the complement of the listed destinations installed as tunnel routes.Adding an excluded range to
ipv4.routeswould route it into the tunnel — the opposite of excluding it. There is no “bypass route” to install, because every route on a WireGuard connection points at the WireGuard device. So Neutron inverts the selection instead: it computes every CIDR except the listed ones and routes those through the tunnel, leaving the excluded ranges to the physical default route.Excluding
10.0.0.0/8therefore produces:nmcli connection modify <uuid> \ ipv4.never-default yes \ ipv6.never-default yes \ ipv4.routes "0.0.0.0/5, 8.0.0.0/7, 11.0.0.0/8, 12.0.0.0/6, ..." \ ipv6.routes "::/0"The complement is computed by
nm::split_tunnel::complement_routes, which recursively splits the address space into the smallest set of aligned CIDRs that covers everything but the exclusions. Excluding nothing yields a full tunnel (0.0.0.0/0); excluding0.0.0.0/0yields no routes at all. -
Use Case: Privacy browsing with exclusions for local services, banking portals, or gaming servers that block remote tunnel endpoints.
3. Disabled Mode (Standard Full-Tunnel)
- Restores
never-default = noand clears all custom static routes.
Route Normalization & Dynamic DNS
1. CIDR Normalization
Input routes are parsed and normalized into standard CIDR format:
- Single IPv4
192.168.1.50$\rightarrow$192.168.1.50/32 - Single IPv6
::1$\rightarrow$::1/128 - Subnets
10.0.0.0/8$\rightarrow$10.0.0.0/8 - Routes are partitioned into
ipv4.routesandipv6.routesautomatically.
2. Client-Side Domain Resolution
For domain entries (e.g., internal.corp, service.local):
- The domain is resolved via
std::net::ToSocketAddrsto all associated IPv4 (/32) and IPv6 (/128) literals. - Resolved IPs are merged with configured CIDRs before applying route arguments to NetworkManager.
- Resolution is performed in the background during connection or rule modification.
CLI & GUI Configuration
CLI Commands
# Check status
neutron split-tunnel status
# Set routing mode
neutron split-tunnel set-mode include
neutron split-tunnel set-mode exclude
neutron split-tunnel set-mode disabled
# Manage CIDRs & Domains
neutron split-tunnel add-cidr 10.0.0.0/8
neutron split-tunnel remove-cidr 10.0.0.0/8
neutron split-tunnel add-domain internal.corp
neutron split-tunnel remove-domain internal.corp
# Clear all rules
neutron split-tunnel clear
GUI Dialog
Available in the Settings section of the GUI main window:
- Clean mode selector dropdown (
Disabled,Include,Exclude). - Interactive list editors for CIDRs and Domain names with inline syntax validation.
- Real-time subtitle updates reflecting active rules.
NAT-PMP Dynamic Port Forwarding Engine
Neutron includes a native, pure Rust NAT-PMP (RFC 6886) client designed for WireGuard endpoints and providers that support dynamic port forwarding (such as Proton, Mullvad, and PIA).
Technical Protocol Overview
NAT-PMP (Port Mapping Protocol) allows clients behind a NAT gateway to request dynamic UDP and TCP port mappings.
┌──────────────┐ ┌──────────────┐
│ Neutron │ │Tunnel Gateway│
│ (Client) │ │ (NAT Router) │
└──────┬───────┘ └──────┬───────┘
│ │
│ 1. UDP Request (Opcode 1: Map UDP) │
│──────────────────────────────────────────>│
│ Internal: 0 (Request any WAN port) │
│ External: 0 │
│ Lifetime: 60 seconds │
│ │
│ 2. UDP Response (Opcode 129: Success) │
│<──────────────────────────────────────────│
│ Assigned Port: 51423 │
│ Lifetime: 60 seconds │
│ │
│ 3. Auto-Renew Loop (Every 45 seconds) │
│──────────────────────────────────────────>│
│ Repeats mapping request before expiry │
│ │
Gateway Derivation Logic
WireGuard profile interfaces obtain private IPv4 addresses (e.g. 10.2.0.2/32 or 100.96.0.4/32).
Neutron derives the default NAT-PMP gateway IP automatically:
- Extracts the primary tunnel IPv4 address via
nmcli -g ipv4.addresses connection show <uuid>. - Replaces the host octet with
.1(e.g.10.2.0.2$\rightarrow$10.2.0.1). - Dispatches the NAT-PMP packet to UDP port
5351at that gateway address.
Auto-Renewal Lifecycle
Port forwarding is off by default — a lease is renewed on a timer against the provider’s gateway, so it is never requested unless asked for. Turn it on with o in the TUI (or via the Command Palette), or set it in ~/.config/neutron/config.toml:
[port_forwarding]
enabled = true
- Lease Grant: Upon receiving a success packet, the granted port number is stored in memory and displayed in the UI banner.
- Periodic Renewal Timer: A background timer runs at
RENEW_INTERVAL(every 45 seconds) to refresh the lease with the gateway. - Profile Switch / Disconnect Cleanup: When switching profiles or disconnecting, the active port is cleared immediately to prevent displaying stale mappings.
- Clipboard Integration: A 1-click button in the GUI copies the forwarded port to the system clipboard for easy pasting into BitTorrent or game servers.
qBittorrent Dynamic Port Sync
Neutron can automatically push the dynamic NAT-PMP port to a running qBittorrent instance (native package, Flatpak, Docker/Podman container, or headless server) via its official Web API (/api/v2).
Setup Prerequisite (Required in qBittorrent)
Before enabling synchronization, ensure qBittorrent’s Web User Interface is enabled:
- In qBittorrent, open Tools $\rightarrow$ Options $\rightarrow$ Web UI (or Preferences $\rightarrow$ Web UI).
- Check “Web User Interface (Remote control)” (default port:
8080). - (Recommended) Under Authentication, check “Bypass authentication for clients on localhost”.
- If localhost authentication bypass is not enabled, configure your WebUI username and password in Neutron via
neutron qbit config --username <user> --password <pass>.
- If localhost authentication bypass is not enabled, configure your WebUI username and password in Neutron via
Compatibility (Flatpak, Native, Containers)
- Flatpak (
org.qbittorrent.qBittorrent): Fully compatible. Because Flatpak packages share the host network stack (--share=network), the WebUI is reached athttp://127.0.0.1:8080, and updated listening ports apply directly to the host socket. - Native Package: Fully compatible.
- Docker / Podman / Remote WebUI: Fully compatible by configuring the target URL (e.g.
neutron qbit config --url http://192.168.1.50:8080).
CLI Management
# Check status and test WebUI connectivity
neutron qbit status
# Test WebUI credentials and fetch current listening port
neutron qbit test
# Immediately forward the active leased port to qBittorrent
neutron qbit sync
# Enable / disable automated background port synchronization
neutron qbit enable
neutron qbit disable
# Configure WebUI parameters & optional interface binding
neutron qbit config --url http://127.0.0.1:8080 --bind true
User Configuration & Theming (config.toml)
Neutron uses a human-readable, self-documenting TOML configuration file located at ~/.config/neutron/config.toml.
Imported profile comments are stored separately in profile-info.json beside the
settings file, keyed by NetworkManager profile UUID. They still appear in profile
details, but no longer clutter config.toml. Existing inline profile_custom_info
entries migrate on the next successful settings save. The notes file uses atomic
writes and owner-only permissions; deleting a profile removes its notes.
Configuration File Schema
# ==============================================================================
# Neutron Configuration (~/.config/neutron/config.toml)
# ==============================================================================
[general]
# Directory monitored for WireGuard .conf files.
# Dropping, copying, or git-cloning profiles here automatically imports them to NetworkManager.
profiles_dir = "~/.config/neutron/profiles"
# Automatically import new/updated .conf files from profiles_dir on launch
auto_sync_profiles = true
# Opt in to connecting a random eligible profile at login
autoconnect_at_login = false
# Default interface when launching `neutron` with no arguments: "tui" or "gui"
default_ui = "tui"
# ==============================================================================
# Security & Routing Policies
# ==============================================================================
[security]
# NetworkManager policy routing (drops traffic if tunnel fails; exclusive DNS priority)
kill_switch = false
# Always-on Netfilter firewall via firewalld (blocks non-tunnel traffic even when disconnected)
lockdown = false
# ==============================================================================
# Global Split Tunneling
# ==============================================================================
[split_tunnel]
# Routing mode: "disabled", "include" (route only listed), or "exclude" (bypass listed)
mode = "include"
# Custom IP subnets (CIDRs) or single host IPs
cidrs = [
"10.0.0.0/8",
"192.168.10.0/24",
"172.16.0.0/12",
]
# Domain names resolved dynamically at connection time
domains = [
"internal.corp",
"homelab.local",
]
# ==============================================================================
# Startup Random Profile Selection Pool
# ==============================================================================
[startup_pool]
# List of profile names or UUIDs excluded from random selection (opt-out model)
excluded_profiles = [
"backup-slow-server",
"emergency-profile",
]
# ==============================================================================
# Terminal User Interface (TUI) & Theming
# ==============================================================================
[tui]
# Telemetry polling rate in milliseconds (handshake, transfer counters)
refresh_interval_ms = 1000
# Built-in theme preset: "nord" (default), "osaka-jade", "catppuccin", "gruvbox", "monochrome"
theme = "nord"
# Optional custom color overrides (accepts hex #rrggbb or standard color names)
[tui.colors]
active_border = "#88c0d0"
status_connected = "#a3be8c"
status_disconnected = "#bf616a"
transfer_rx = "#81a1c1"
transfer_tx = "#ebcb8b"
# ==============================================================================
# Port Forwarding (NAT-PMP)
# ==============================================================================
[port_forwarding]
# Lease an incoming port from the tunnel gateway and keep renewing it.
# Also togglable live from the TUI with `f`. Off by default: the lease is
# renewed on a timer against the provider, so it is only requested on request.
enabled = false
# ==============================================================================
# qBittorrent Dynamic Port Forwarding Sync
# ==============================================================================
[qbittorrent]
# Automatically push NAT-PMP leased ports to qBittorrent WebUI on connect/renew
enabled = false
# WebUI HTTP/HTTPS endpoint URL
url = "http://127.0.0.1:8080"
# Optional authentication (leave empty if localhost auth bypass is enabled in qBittorrent)
# username = "admin"
# password = "your-webui-password"
# Bind qBittorrent network interface to the active WireGuard interface
bind_interface = false
Theming Engine
Neutron features a built-in terminal theme engine with 5 carefully calibrated color palettes that can be toggled live via Ctrl+T:
| Theme | Description | Accent Colors | Best For |
|---|---|---|---|
nord (Default) | Arctic, north-bluish clean palette | Frost Cyan, Polar Night Gray | Minimalist dark setups |
osaka-jade | Osaka Jade / Bamboo palette | Jade Cyan, Bamboo Green, Gold | Dark forest green aesthetic |
catppuccin | Soothing pastel palette (Mocha) | Mauve, Sapphire, Peach | Modern terminal setups |
gruvbox | Retro groove warm earthy palette | Warm Amber, Forest Green | Tiling window managers & vim users |
monochrome | High-compatibility black & white | High-contrast ASCII/ANSI | Minimal TTYs, serial consoles, 16-color terms |
Managed Profile Drop Directory (profiles/)
Neutron manages a dedicated profile drop directory at ~/.config/neutron/profiles/ (with strict 0700 user-only permissions).
Workflow:
- Drop / Copy Profiles: Users can simply copy
.conffiles into the directory:cp ~/Downloads/wireguard_configs/*.conf ~/.config/neutron/profiles/ - Auto-Sync on Launch: Whenever the TUI or CLI runs, Neutron scans the folder, compares content checksums against NetworkManager, and batch-imports new profiles in milliseconds.
- Manual Sync Command: You can trigger an instant sync via CLI or within the TUI:
neutron sync - Git/Dotfiles Automation: The entire
~/.config/neutron/folder can be tracked in a private Git repository for instant syncing across multiple machines.
Packaging & Universal Distribution
Neutron is designed for easy distribution across all major Linux packaging ecosystems.
Packaging Channels Overview
| Format | Target Platform | Dependencies | Standalone? | Build Command |
|---|---|---|---|---|
| Homebrew | Linux / Linuxbrew | Zero (Pure Rust TUI/CLI) | Yes | brew tap pandabytez/tap && brew trust pandabytez/tap && brew install neutron |
| Static Musl | Headless Servers, SSH | Zero (Static musl binary) | Yes | cargo build --target x86_64-unknown-linux-musl |
| Arch AUR | Arch Linux, Manjaro | System dependencies | Native | makepkg -si |
1. Homebrew Tap Formula (Formula/neutron.rb)
Sample formula for custom tap (brew tap pandabytez/tap):
class Neutron < Formula
desc "Fast WireGuard profile manager via NetworkManager"
homepage "https://github.com/PandaBytez/neutron"
url "https://github.com/PandaBytez/neutron/archive/refs/tags/v0.1.0.tar.gz"
sha256 "<checksum>"
license "GPL-3.0-or-later"
depends_on "rust" => :build
depends_on :linux
def install
system "cargo", "install", *std_cargo_args
bin.install_symlink "neutron" => "neutron-vpn"
end
test do
assert_match "Neutron", shell_output("#{bin}/neutron --help")
end
end
Tap Trust Verification (Modern Homebrew)
Under Homebrew’s tap trust model, non-official taps can be explicitly marked as trusted:
# Trust the tap
brew trust pandabytez/tap
# Or trust specifically the neutron formula
brew trust --formula pandabytez/tap/neutron
2. Static Musl Target (Headless Servers / Homelabs)
Compile a 100% statically-linked executable with no dynamic shared library dependencies:
# Add musl target
rustup target add x86_64-unknown-linux-musl
# Build static binary
cargo build --release --target x86_64-unknown-linux-musl
The resulting binary (target/x86_64-unknown-linux-musl/release/neutron) runs on Alpine Linux, Debian, RHEL, Ubuntu, and any minimal Linux environment.
3. Arch Linux AUR Package (PKGBUILD)
Sample PKGBUILD for Arch Linux:
pkgname=neutron-bin
pkgver=0.1.0
pkgrel=1
pkgdesc="Fast WireGuard manager via NetworkManager"
arch=('x86_64' 'aarch64')
url="https://github.com/PandaBytez/neutron"
license=('GPL-3.0-or-later')
depends=('networkmanager')
source_x86_64=("https://github.com/PandaBytez/neutron/releases/download/v${pkgver}/neutron-linux-amd64.tar.gz")
sha256sums_x86_64=('SKIP')
package() {
install -Dm755 neutron "${pkgdir}/usr/bin/neutron"
ln -s neutron "${pkgdir}/usr/bin/neutron-vpn"
}
Implementation Notes
This page collects lifecycle and persistence details. See the existing architecture, NetworkManager integration, and security chapters for subsystem and policy design.
Startup Selection
Auto-connect at login defaults to off. Enable it with a in the TUI to persist
the preference and install the desktop autostart entry. Explicit saved settings
are preserved when upgrading.
Neutron issues explicit connection requests and disables NetworkManager’s native autoconnect on managed WireGuard profiles. The startup selector leaves a single active, eligible profile untouched. If replacement is needed, it validates the eligible pool before disconnecting existing tunnels; an empty pool returns an error without teardown. Candidate selection avoids immediately repeating the last random profile when alternatives exist.
Configuration Persistence
Application configuration is written atomically and uses owner-only 0o600
permissions on Unix. Production writers update individual settings under a
stable sidecar file lock, preserving newer settings written by other Neutron
processes. External editors must cooperate with locking to serialize edits.
Imported profile notes live in a separate profile-info.json alongside the
settings file. Reading older inline notes is backward-compatible; the next save
writes the notes file before removing the inline section. Once present, the notes
file is authoritative, including an empty map after deletion. Malformed or
unreadable notes produce an error rather than being silently overwritten.
WireGuard private keys remain in NetworkManager storage. Optional qBittorrent WebUI credentials are application settings; they are passed to curl through stdin rather than command-line arguments.
Activation and Import
Activation requires successful configuration loading and routing/DNS policy preparation. Invalid split-tunnel targets, unresolved domains, or rejected NetworkManager modifications stop activation with an error. Imported profiles receive the same checked preparation.
Automatic handshake-failure teardown applies to fresh interfaces whose profiles have an endpoint and nonzero persistent keepalive, when verification is enabled. On-demand profiles remain active while idle so their first packet can initiate the handshake.
Imports use nmcli connection import type wireguard file <path>. The profile
UUID comes from that command’s validated C-locale confirmation, rather than
guessing from concurrent profile-list changes. The profile
inbox consumes source files after successful import; source removal is
best-effort, and matching filenames are currently skipped by profile name rather
than content comparison. Interface comments are retained as application metadata.
Saved Intent and Effective Policy
The long-lived indicator refreshes domain routes every 30 seconds and reapplies them to active NetworkManager devices. Automatic firewall reconciliation and domain refresh share coordination with explicit policy changes; failed firewall reconciliation is retried. DNS rotation still has a polling interval, not a per-query routing guarantee.
TUI profile refresh and startup sync run in background workers. Transient profile read failures retry with a delay, and public-IP requests coalesce without losing the last request during worker shutdown. Favorite menu IDs remain tied to UUIDs across layout refreshes; clicks on removed items are ignored.
Port leases renew at half the granted lifetime (capped at 45 seconds). Failed qBittorrent synchronization retries after 45 seconds, while changed configuration can trigger an immediate attempt.
Routing/DNS changes to saved NetworkManager profiles require reconnect. Action toasts explain this; the policy panel shows saved settings. DNS details do not claim a verified live priority.
Policy errors distinguish potentially partial application from completed
application followed by failed saving. The current TUI session marks affected
policies UNKNOWN until a successful retry, even after a config refresh. CLI
lockdown status explicitly reports saved intent rather than verified firewall
state. Emergency disable attempts firewall removal even when saving is unavailable.
For permanent rebuild guards and recovery, see Lockdown rebuild recovery.
Binary and Verification
NAT-PMP sockets accept replies only from the requested gateway address and port. Replies must match the protocol version, length, opcode and internal port (with the provider’s zero-port allocation convention supported). Failure to bind the requested tunnel address aborts the request instead of falling back to another source.
The executable is named neutron. The musl target produces a statically linked
binary; system tools and services such as NetworkManager are still required.
Build instructions are in Packaging & Distribution.
Commands and test-tier boundaries are in Testing & Quality Checks.
Testing & Quality Checks
Run these commands from the repository root with the Rust toolchain installed.
Host Checks
# Check formatting and run strict Clippy across all features
cargo lint
# Run default-feature unit and integration tests
cargo test
# Include the optional qBittorrent integration tests
cargo test --all-features
Host tests use pure logic, mocks, and isolated local responders. Tests that modify real NetworkManager profiles or firewall rules are ignored on the host.
Disposable System Sandbox
Requires Podman or Docker. The Cargo tasks start NetworkManager and firewalld in a disposable container with its own network namespace. Network and firewall changes stay inside that sandbox.
# Host tests across all features, then containerized system tests
cargo test-all
# Containerized system tests only
cargo test-system
# Select a system tier or rebuild the sandbox image
cargo test-system -- --nm
cargo test-system -- --firewall
cargo test-system -- --rebuild
# Firewall leak regression checks
cargo test-leaks
# Interactive sandbox for investigation
cargo xtask container-shell
Never run cargo test -- --ignored directly on the host or set
NEUTRON_TEST_SANDBOX=1 there. The sandbox sets that marker for tests guarded by
require_sandbox().
Coverage and Limits
- NetworkManager tests check profile import, policy properties, and real WireGuard activation, including idle on-demand tunnels.
- Firewall tests check rule acceptance, legacy-rule migration, surgical teardown,
and established IPv4/IPv6 packet egress. The
leak_*checks are also included in the firewall tier. - The ignored library test
interrupted_rebuild_stays_closed_after_reload_and_recoversexercises partial permanent rebuilds and recovery. It runs in the full system suite;--firewallselects onlytests/system_firewall.rs. - The default sandbox uses firewalld’s iptables backend. Its entrypoint also
accepts
NEUTRON_TEST_FIREWALL_BACKEND=nftablesinside the container for cross-backend checks. - Container reload checks do not establish persistence across an actual reboot.
Build the Documentation
cargo docs
cargo xtask docs --serve
mdBook writes the site to public/. See Implementation Notes
for the behavior these tests protect.