Real-time local network traffic monitoring — built as a desktop app with a live dashboard.
Local Watch captures packets from your machine's network interface, aggregates traffic metrics in real time, and streams them to a web dashboard wrapped in a native desktop shell. It is designed as a lightweight, privacy-first alternative to cloud-based network analytics: all capture and processing happens on your machine.
Most network monitoring tools are either heavyweight enterprise suites or CLI-only utilities. Local Watch explores a middle ground:
- Local-first — traffic never leaves your machine
- Real-time — sub-second metric snapshots over WebSocket
- Modern stack — TypeScript monorepo with a React dashboard and Electron desktop wrapper
- Extensible — clear separation between capture, aggregation, transport, and UI layers
This project demonstrates systems-level integration (packet capture via Wireshark's tshark), streaming data pipelines, and full-stack desktop application architecture.
┌─────────────────────────────────────────────────────────────────┐
│ Electron (desktop) │
│ loads http://localhost:3000 │
└───────────────────────────────┬─────────────────────────────────┘
│
┌───────────────────────────────▼─────────────────────────────────┐
│ Dashboard (Next.js + React) │
│ WebSocket client → live traffic UI │
│ :3000 │
└───────────────────────────────┬─────────────────────────────────┘
│ ws://localhost:4000
┌───────────────────────────────▼─────────────────────────────────┐
│ Collector (Node.js) │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌─────────┐ │
│ │ tshark │ → │ parser │ → │ metrics eng. │ → │ WS │ │
│ │ capture │ │ │ │ (1s ticks) │ │ server │ │
│ └──────────┘ └──────────┘ └──────────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
network interface
(e.g. en0)
- Capture —
tsharkstreams live packet fields (timestamp, IPs, protocol, length, ports) from a network interface as CSV lines. - Parse — Each line is normalized into a typed
PacketEvent. - Aggregate — A metrics engine rolls up per-second counters: packets/sec, bytes/sec, and top talkers by protocol, IP, and port.
- Broadcast — Every second, a snapshot is pushed to all connected WebSocket clients.
- Visualize — The dashboard (in progress) consumes snapshots and renders live charts and tables.
| App | Description |
|---|---|
| Collector | Packet capture, aggregation, and WebSocket event streaming |
| Dashboard | Electron desktop client (Next.js UI + live metrics) |
| Desktop shell | Electron window that hosts the dashboard |
| Docs index | Full documentation map |
| Layer | Technology |
|---|---|
| Monorepo | pnpm workspaces |
| Packet capture | Wireshark tshark |
| Backend / pipeline | Node.js, TypeScript, ws, Pino, Zod |
| Dashboard | Next.js 16, React 19, Tailwind CSS 4 |
| Desktop shell | Electron 42 |
| Dev tooling | tsx, concurrently, Turbo |
local-watch/
├── apps/
│ ├── collector/ # Packet capture, aggregation, WebSocket server
│ │ └── src/
│ │ ├── capture/ # tshark subprocess + line parser
│ │ ├── aggregation/# Metrics state + snapshot builder
│ │ ├── websocket/ # Broadcast server (:4000)
│ │ └── types/ # Shared event types
│ ├── dashboard/ # Next.js frontend (:3000)
│ └── desktop/ # Electron wrapper
├── packages/ # Shared packages (reserved for future use)
├── package.json # Root scripts
└── pnpm-workspace.yaml
The collector emits a JSON snapshot every second:
{
"timestamp": 1716585600123,
"packetsPerSecond": 142,
"bytesPerSecond": 98304,
"topProtocols": [{ "key": "TCP", "value": 51200 }],
"topIps": [{ "key": "192.168.1.42", "value": 40960 }],
"topPorts": [{ "key": 443, "value": 87 }]
}- Node.js 20+
- pnpm 10+ (
corepack enable && corepack prepare pnpm@latest --activate) - Wireshark (for
tshark) — Install Wireshark - macOS/Linux — packet capture requires elevated permissions (see below)
Verify tshark is available:
tshark --versionpnpm installThe collector defaults to interface en0 (typical macOS Wi-Fi). Edit apps/collector/src/capture/tshark.ts to match your interface:
# macOS — list interfaces
networksetup -listallhardwareports
# Linux
ip link showpnpm devThis starts all three apps concurrently:
| Service | URL / Port | Description |
|---|---|---|
| Collector | ws://localhost:4000 |
Packet capture + metrics broadcast |
| Dashboard | http://localhost:3000 |
Web UI |
| Desktop | Electron window | Native shell around the dashboard |
Or run services individually:
pnpm dev:collector # WebSocket server + tshark
pnpm dev:dashboard # Next.js dev server
pnpm dev:desktop # Electron windowOn macOS, grant Wireshark/tshark permission to capture packets:
- System Settings → Privacy & Security → Full Disk Access / Local Network (as applicable)
- Or run the collector with
sudoduring development:
sudo pnpm dev:collectorOn Linux, you may need to run with sudo or add your user to the wireshark group.
The collector is the core backend. Key modules:
capture/tshark.ts— spawnstsharkwith field extraction and line-buffered stdout parsingcapture/parser.ts— converts CSV lines intoPacketEventobjectsaggregation/metricsEngine.ts— rolling counters and top-N rankingswebsocket/server.ts— fans out snapshots to connected clients
Test the pipeline without the dashboard by watching collector logs — snapshots are printed every second.
Connect manually via WebSocket:
const ws = new WebSocket("ws://localhost:4000");
ws.onmessage = (e) => console.log(JSON.parse(e.data));The dashboard is a Next.js App Router project. The UI layer is scaffolded and ready to wire up to the WebSocket feed.
Planned UI components:
- Live packets/sec and bytes/sec gauges
- Top protocols, IPs, and ports tables
- Connection status indicator
The Electron app loads the dashboard dev server in a BrowserWindow. In production, this would point at a built static export or bundled Next.js output.
| Component | Status |
|---|---|
Packet capture (tshark) |
✅ Working |
| CSV parsing + typed events | ✅ Working |
| Per-second metrics aggregation | ✅ Working |
| WebSocket broadcast | ✅ Working |
| Dashboard UI | 🚧 Scaffolded (Next.js default) |
| WebSocket → React integration | 🚧 Planned |
| Electron production build | 🚧 Planned |
| Cross-platform interface detection | 🚧 Planned |
- Wire dashboard to WebSocket with live charts (Recharts or similar)
- Interface auto-detection and CLI flag (
--interface en0) - Historical buffer (ring buffer of last N snapshots)
- Filter by protocol, IP, or port
- Shared types package in
packages/ - Production Electron build with embedded dashboard
- Optional export to PCAP or CSV
Why tshark instead of a native binding?
Spawning tshark as a subprocess keeps the collector simple, leverages Wireshark's battle-tested dissectors, and avoids native addon complexity. The tradeoff is a process boundary and the Wireshark install requirement.
Why WebSocket instead of HTTP polling?
Metrics are pushed every second to all connected clients. WebSocket gives low-latency, bidirectional transport with minimal overhead compared to polling.
Why a monorepo?
The collector, dashboard, and desktop shell are independently deployable but share a development workflow. A shared packages/ workspace is reserved for types and utilities as the project grows.
Why Electron?
Desktop apps need reliable access to local services and a consistent window chrome. Electron wraps the web dashboard without rewriting the UI in a native toolkit.
| Problem | Likely cause | Fix |
|---|---|---|
tshark: command not found |
Wireshark not installed | Install Wireshark and ensure tshark is on your PATH |
| No packets captured | Wrong interface | Update the -i flag in tshark.ts |
| Permission denied | Insufficient capture privileges | Run with sudo or grant Wireshark permissions |
| Dashboard shows default Next.js page | UI not yet connected | Expected — connect WebSocket client in app/page.tsx |
| Electron blank window | Dashboard not running | Start pnpm dev:dashboard before or with pnpm dev:desktop |
ISC
Built by David Martin as a portfolio project exploring network observability, real-time data pipelines, and desktop application architecture.