OS Proxy Injection: Linux, macOS, and Windows Configurators
Files: kinetic-pac/src/os/linux.rs, os/macos.rs, os/windows.rs, os/mod.rs
Crate: kinetic-pac | Stage: 11
Reading Time: 35 minutes
1. What Is This?
These files contain the actual OS-integration code — the shell commands and registry edits that make the browser route .kin traffic through the Kinetic proxy without any user configuration.
Each file implements the ProxyConfigurator trait for its target platform:
linux.rs: Two implementations —KdeConfigurator(usingkwriteconfig5andkreadconfig5) andGnomeConfigurator(usinggsettings). Plusdetect_linux_configurator()which readsXDG_CURRENT_DESKTOPto choose between them.macos.rs:MacosConfigurator(usingnetworksetup) that sets the PAC URL on every detected network service.windows.rs:WindowsConfigurator(using PowerShell) that writes directly to the Windows registry’sInternet Settingskey.
All four methods of the trait (install, uninstall, save_previous_state, restore_state) are implemented for each platform.
2. Why Kinetic Needs This
A PAC file works only if the OS knows about it. Writing a perfect PAC file and serving it at http://127.0.0.1:16001/proxy.pac accomplishes nothing if the browser or OS doesn’t know to fetch it from there.
Each platform has a completely different mechanism for storing this configuration:
- GNOME stores it in a DConf database accessed via
gsettingsCLI. - KDE Plasma stores it in
~/.config/kioslavercwritten viakwriteconfig5CLI. - macOS stores per-service proxy settings queried and set via the
networksetupbinary. - Windows stores it in the user’s registry at
HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings.
There is no cross-platform library that abstracts these four mechanisms, so Kinetic shells out to the native tools directly.
3. How It Works — Linux (GNOME)
GnomeConfigurator::install(pac_url)
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/linux.rs` — Lines 174–191
}
Two gsettings set commands in sequence:
Command 1: Sets the proxy mode to auto. This tells GNOME’s network manager to use a PAC file instead of manual proxy settings or no proxy.
gsettings set org.gnome.system.proxy mode 'auto'
Command 2: Sets the autoconfiguration URL to the Kinetic PAC endpoint.
gsettings set org.gnome.system.proxy autoconfig-url 'http://127.0.0.1:16001/proxy.pac'
Once these two settings are applied, GNOME-based browsers (and most GTK apps) will automatically fetch the PAC file and route .kin traffic through the Kinetic proxy.
GnomeConfigurator::save_previous_state()
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/linux.rs` — Lines 202–235
}
Runs gsettings get for both mode and autoconfig-url. The output is captured from stdout using .output() (not .status()) so the return value can be stored.
The result is trimmed of whitespace and filtered for empty strings. The autoconfig-url is also filtered for '' (single-quoted empty string, which is what gsettings returns when no URL is set). This prevents storing a garbage value in the SavedState that would later be “restored” as a proxy URL.
GnomeConfigurator::uninstall()
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/linux.rs` — Lines 193–199
}
Sets mode back to 'none'. Does not touch the autoconfig-url key — it’s not needed since the mode overrides it.
GnomeConfigurator::restore_state()
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/linux.rs` — Lines 238–254
}
Writes back mode first, then optionally the autoconfig-url. If there was no previous autoconfig-url, it explicitly sets it to '' to clear any value Kinetic left.
4. How It Works — Linux (KDE Plasma)
KdeConfigurator::install(pac_url)
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/linux.rs` — Lines 10–47
}
Three commands:
Command 1: Sets ProxyType to 2 in kioslaverc. In KDE’s proxy system, type 0 = no proxy, 1 = manual proxy, 2 = PAC script URL. This switches KDE into PAC mode.
kwriteconfig5 --file kioslaverc --group "Proxy Settings" --key ProxyType 2
Command 2: Writes the PAC URL to the Proxy Config Script key.
kwriteconfig5 --file kioslaverc --group "Proxy Settings" --key "Proxy Config Script" <url>
Command 3: Sends a D-Bus signal to KIO/Scheduler to reload its slave configuration. Without this, KDE applications that are already running won’t pick up the change until they restart.
dbus-send --type=signal /KIO/Scheduler org.kde.KIO.Scheduler.reparseSlaveConfiguration string:''
The D-Bus call failure is deliberately ignored (let _ = ...) because KDE applications might not be running (headless or server environments).
KdeConfigurator::save_previous_state()
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/linux.rs` — Lines 76–124
}
Uses kreadconfig5 to read back both ProxyType and Proxy Config Script. This is the read counterpart to kwriteconfig5. If ProxyType can’t be read, it defaults to "0" (no proxy) so restore always has a valid value.
KdeConfigurator::restore_state()
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/linux.rs` — Lines 126–167
}
Writes ProxyType back from the saved value, then optionally restores Proxy Config Script. Always sends the D-Bus reload signal at the end so running KDE applications pick up the change immediately.
detect_linux_configurator()
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/linux.rs` — Lines 259–278
}
Reads the XDG_CURRENT_DESKTOP environment variable and lowercases it. Matches against known desktop environment names:
- Contains
kdeorplasma→KdeConfigurator - Contains
gnome,unity, orbudgie→GnomeConfigurator - Anything else →
FallbackConfigurator
The fallback covers headless servers, Sway/Hyprland (Wayland-only), Cinnamon, XFCE, and any other environment without an automatic proxy API.
5. How It Works — macOS
The networksetup approach — multiple services
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/macos.rs` — Lines 11–27
}
macOS is more complex than Linux because it has multiple distinct “network services” — Wi-Fi, Ethernet, USB Ethernet, VPN adapters. Each service has its own independent proxy configuration. Setting the PAC URL on only the Wi-Fi service would break it when the user switches to Ethernet.
MacosConfigurator::install() runs networksetup -listallnetworkservices first to get the full list of services. It then loops over every service that doesn’t start with * (disabled services are prefixed with * in the output) and calls:
networksetup -setautoproxyurl "<service name>" "http://127.0.0.1:16001/proxy.pac"
Each call is fire-and-forget with let _ = ... because some services may not support proxy settings (VPN tunnels often reject this command).
MacosConfigurator::save_previous_state()
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/macos.rs` — Lines 47–83
}
Loops over all services again. For each service, runs:
networksetup -getautoproxyurl "<service>"
The output contains both the URL and whether it’s enabled:
URL: http://some-existing-pac.com
Enabled: Yes
The code checks url_str.contains("Enabled: Yes") — only saves the URL if the PAC was actually enabled before Kinetic touched it. This prevents recording a PAC URL that was already configured but disabled.
Saves everything into a HashMap<String, String> keyed by service name.
MacosConfigurator::restore_state()
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/macos.rs` — Lines 85–98
}
Calls self.uninstall() first (disables PAC on all services), then iterates the macos_services hashmap and re-enables PAC on each service that previously had it, using both -setautoproxyurl (to set the URL) and -setautoproxystate ... on (to re-enable it).
6. How It Works — Windows
Registry-based PAC injection
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/windows.rs` — Lines 11–30
}
Windows stores proxy settings in the user’s HKCU registry hive, not in a config file. The key path is:
HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings
Step 1: Uses PowerShell Set-ItemProperty to write AutoConfigURL:
Set-ItemProperty -Path 'HKCU:\...' -Name AutoConfigURL -Value 'http://127.0.0.1:16001/proxy.pac'
Step 2: Sets ProxyEnable to 0. This disables any manual HTTP proxy that might have been configured. When AutoConfigURL is set, Windows uses the PAC file regardless of ProxyEnable, but disabling manual proxy prevents conflicts.
WindowsConfigurator::uninstall()
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/windows.rs` — Lines 32–42
}
Uses Remove-ItemProperty with -ErrorAction SilentlyContinue to delete the AutoConfigURL key entirely. The SilentlyContinue flag prevents PowerShell from failing if the key doesn’t exist.
WindowsConfigurator::save_previous_state()
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/windows.rs` — Lines 44–68
}
Runs Get-ItemProperty to read the current AutoConfigURL value. Uses String::from_utf8_lossy() (not from_utf8()) because PowerShell output can sometimes contain BOM characters or locale-specific encoding on older Windows versions. The lossy variant replaces invalid UTF-8 sequences with replacement characters rather than returning an error.
7. Key Pieces
KdeConfigurator
Implements the full 4-method ProxyConfigurator contract using kwriteconfig5/kreadconfig5. Uses D-Bus to notify running KDE apps of the change.
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/linux.rs` — Lines 7–168
}
GnomeConfigurator
Uses gsettings with the org.gnome.system.proxy schema. Two keys: mode (the proxy type) and autoconfig-url (the PAC URL).
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/linux.rs` — Lines 171–255
}
detect_linux_configurator()
Reads XDG_CURRENT_DESKTOP and dispatches to the right configurator. Returns a Box<dyn ProxyConfigurator> so the caller has a uniform interface.
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/linux.rs` — Lines 259–278
}
MacosConfigurator
Loops over all macOS network services and applies proxy settings to each one individually. Saves per-service state so each service is restored to exactly its original configuration.
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/macos.rs` — Lines 10–99
}
WindowsConfigurator
Uses PowerShell to write/read/delete the AutoConfigURL registry key. Uses from_utf8_lossy for robust output parsing.
#![allow(unused)]
fn main() {
-> See: `kinetic-pac/src/os/windows.rs` — Lines 10–84
}
8. Cross-Crate Connections
main.rs(SavedState,ProxyConfigurator,PacError,FallbackConfigurator): All OS configurators are children ofmain.rstypes. They referenceSavedStateas their state snapshot type andPacErroras their error type.error.rs(PacError): The error type used in allResult<(), PacError>returns from the OS commands. Contains variants likePacError::Command(String)for subprocess failures.
9. Quick Reference
| Platform | Tool Used | Proxy Setting Location |
|---|---|---|
| GNOME/Unity | gsettings | DConf schema org.gnome.system.proxy |
| KDE Plasma | kwriteconfig5 / D-Bus | ~/.config/kioslaverc |
| macOS | networksetup | Per-service system preference |
| Windows | PowerShell | HKCU:\...\Internet Settings\AutoConfigURL |
| Other/Headless | FallbackConfigurator | No-op, manual user config required |
macOS difference: Must loop over ALL network services. Single-service injection leaves other interfaces without the PAC rule.
KDE difference: Requires a D-Bus signal after writing the config file to notify running applications.
Windows difference: PAC URL is a registry value, not a file. PowerShell is the most portable way to write it without a Windows-specific Rust crate.
10. Open Questions
- XFCE/Cinnamon/Sway: These environments are not detected and fall through to
FallbackConfigurator. Users on Sway (Wayland compositor) or XFCE need to manually configure their browser’s proxy. - macOS VPN adapters: The
networksetup -setautoproxyurlcall on VPN services typically fails silently. There is no way to verify success without re-reading the value. - Windows UAC: Writing to
HKCU(current user) does not require elevation. But if Kinetic is run as a service underSYSTEM, theHKCUhive targeted is the SYSTEM hive, not the logged-in user’s. This would silently fail to configure the correct user’s proxy. - Chrome on Linux: Chrome/Chromium on Linux reads proxy settings from GNOME or KDE depending on flags. But if launched with
--no-system-proxy, it ignores all of this. The PAC injection has no effect on such invocations.
11. Rust Concepts
Command::new(...).output()vs.status():.status()waits for the subprocess and returns only the exit code..output()captures both stdout/stderr and the exit code. Used insave_previous_state()where the result of the command needs to be read back.String::from_utf8_lossy(&bytes): Converts bytes to string, replacing invalid UTF-8 sequences with\u{FFFD}instead of returning an error. Critical for Windows PowerShell output which may contain BOMs or non-UTF8 on older systems.#[cfg(target_os = "macos")]on the struct definition: The structMacosConfiguratoritself is only compiled on macOS. Thedetect_configurator()function uses a second#[cfg]inside the"macos"match arm to conditionally return it. This is belt-and-suspenders: the function can only reach that code path when compiled for macOS.let _ = Command::new(...).status(): The leadinglet _ =explicitly discards theResult. Used for D-Bus signals and non-critical cleanup commands where failure is acceptable and should not propagate.String::from_utf8(o.stdout).ok().and_then(...): A chain of fallible conversions..ok()convertsResulttoOption..and_then()applies a function to the inner value if it exists, short-circuiting toNoneon any failure. Cleaner than nestedmatchfor sequential conversions.