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) to src-tauri/errors.json for 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

  1. Startup – Execution begins in src-tauri/src/main.rs. The tauri::Builder::default() call loads tauri.conf.json, creates the window, and registers the Rust command handler defined in src-tauri/src/lib.rs.
  2. Command Registrationlib.rs contains statements like invoke_handler(tauri::generate_handler![scan_ports, kill_process, restart_process]). Each function lives in a dedicated module (scanner.rs, connections.rs, etc.).
  3. 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).
  4. Port Scanscanner::scan_ports calls OS APIs (e.g., netstat on Windows, /proc/net/tcp on Linux) and assembles a vector of PortInfo. It enriches each entry by reading package.json, Cargo.toml, or go.mod files in the process’s working directory to infer the framework.
  5. Response – The Rust function serialises the vector to JSON and returns it to the webview. React updates state, causing PortMap.tsx to redraw the table and D3 graph.
  6. Kill/Restart – UI actions invoke kill_process(pid) or restart_process(pid, cmd). The Rust side uses std::process::Command::new("kill") (or platform‑specific APIs) and, for restart, re‑executes the stored start command. Results are logged by logger.rs and reflected instantly in the UI.
  7. Tray Interactiontray.rs registers a system‑tray menu. Selecting “Open Dashboard” sends a tauri::Window::show() call, while background scans continue to update errors.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.