The Problem Developers repeatedly hit “address already in use” errors while iterating on local services. Finding the offending PID, confirming the owning project, and killing or restarting the process requires manual netstat/lsof look‑ups and copy‑pasting kill commands. The friction is especially painful on machines that run many frameworks (Node, Django, Rust, etc.) simultaneously.
What This Does PortPal delivers a native desktop dashboard (Tauri 2) that continuously enumerates listening ports, maps each to its owning process, and enriches the data with framework detection and project discovery. The UI lives in src/App.tsx and src/PortMap.tsx; it shows a sortable table, per‑port sparkline traffic, and a D3‑based topology graph (src/PortMap.tsx). Clicking the ✕ icon triggers a kill request; a ↻ icon (when the start command is known) relaunches the service.
The Rust side in src‑tauri/src/ performs the heavy lifting:
src-tauri/src/scanner.rs– polls the OS network stack (e.g.,netstat‑style APIs) and builds a list of{port, pid, exe, connections}structs.src-tauri/src/connections.rs– tracks per‑port traffic counters used for the sparkline UI.src-tauri/src/logger.rs– appends chronological events (starts, stops, spikes) tosrc-tauri/errors.jsonfor the “Historical Event Logging” view.src-tauri/src/tray.rs– creates the system‑tray icon and forwards click events to the main window.
src-tauri/src/lib.rs re‑exports the command functions and wires them into Tauri’s invoke_handler. src-tauri/src/main.rs builds the Tauri application, registers the commands, and launches the webview that loads the React bundle (index.html).
How It Is Wired
- Startup – Execution begins in
src-tauri/src/main.rs. Thetauri::Builder::default()call loadstauri.conf.json, creates the window, and registers the Rust command handler defined insrc-tauri/src/lib.rs. - Command Registration –
lib.rscontains statements likeinvoke_handler(tauri::generate_handler![scan_ports, kill_process, restart_process]). Each function lives in a dedicated module (scanner.rs,connections.rs, etc.). - Frontend Call – React components invoke these commands via
window.__TAURI__.invoke('scan_ports'). The request travels through the Tauri bridge into the matching Rust function (scanner::scan_ports). - Port Scan –
scanner::scan_portscalls OS APIs (e.g.,netstaton Windows,/proc/net/tcpon Linux) and assembles a vector ofPortInfo. It enriches each entry by readingpackage.json,Cargo.toml, orgo.modfiles in the process’s working directory to infer the framework. - Response – The Rust function serialises the vector to JSON and returns it to the webview. React updates state, causing
PortMap.tsxto redraw the table and D3 graph. - Kill/Restart – UI actions invoke
kill_process(pid)orrestart_process(pid, cmd). The Rust side usesstd::process::Command::new("kill")(or platform‑specific APIs) and, for restart, re‑executes the stored start command. Results are logged bylogger.rsand reflected instantly in the UI. - Tray Interaction –
tray.rsregisters a system‑tray menu. Selecting “Open Dashboard” sends atauri::Window::show()call, while background scans continue to updateerrors.json.
The most connected module is lib.rs, which acts as the command hub; any change to the public API must be mirrored in the React invoke calls. No cyclic dependencies are present; the data flow is unidirectional from Rust → UI.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/Portpal
cd Portpal
# Install Node dependencies (npm inferred from package.json)
npm ci
# Build the Rust side (cargo inferred from Cargo.toml)
cargo build # optional – Tauri will invoke cargo automatically
# Run the development version
npm run tauri dev # standard Tauri script defined in package.json
No additional configuration files are required; the application reads the OS network stack at runtime. To package a release, the README references npm run tauri build.
Real‑World Use
A developer working on a Node API (localhost:3000) and a Rust microservice (localhost:8080) starts both with npm start and cargo run. While coding, the developer notices port 3000 is busy. Opening PortPal instantly shows two entries, the Node process flagged with the react framework detection, and a red tray icon. Clicking the ✕ next to the Node entry kills the stray process; the UI updates in < 200 ms. The developer then clicks ↻ to relaunch the Node server in a new terminal—all without leaving the dashboard.
Code Health & Issues
- Medium – Untested code – No
*.test.*files detected; core scanning and kill logic lack automated unit tests. - Low – Missing CI for Rust – GitHub Actions workflow (
.github/workflows/build.yml) builds the app but does not run cargo tests (none exist) or lint checks. - Low – Documentation gaps – Only two Markdown docs (
README.md,LICENSE); no API reference for the Rust commands or React component hierarchy.
The Bottom Line
PortPal provides a functional, Tauri‑based desktop UI that replaces manual port hunting with a single‑click workflow. The architecture is cleanly split between a lightweight Rust backend and a React front‑end, making it approachable for developers familiar with either stack. The primary drawback is the lack of automated tests and limited CI validation, so teams should add unit tests around scanner.rs and connections.rs before relying on it in production environments. Suitable for solo developers or small teams needing quick visibility into local network usage.