A live, browser-based network monitoring dashboard. Pick an interface, watch your network's traffic in real time, see who's talking to whom, and get a passive bettercap-style view of every device on the LAN.
Dashboard tab — live overview while traffic flows:
- Throughput timeline — bytes/sec or packets/sec over the rolling window
- Top talkers / destinations — source and destination IPs by volume, with reverse-DNS hostnames
- Protocol breakdown — TCP / UDP / ICMP / ARP donut
- Top services — destination ports labelled (HTTPS, DNS, SSH, mDNS, …)
Hosts tab — every device the sniffer has seen:
- IP, MAC, MAC-vendor (OUI lookup), reverse-DNS hostname, mDNS name
- Sent / received bytes, first / last seen
- One-click scan lan button runs an active ARP + mDNS sweep
- Click any row to drill in to that host
Activity tab — passive insights from plaintext protocols:
- DNS query log + top-domains rollup
- TLS SNI extracted from ClientHellos (no decryption)
- Plaintext HTTP request log (Host / User-Agent / path)
Stats are kept in an in-memory rolling window — nothing is written to disk.
- macOS or Linux with BPF/raw-socket access
- Python 3.11+
- Capture privileges: usually
sudo, or membership in the BPF group - Optional: Linux + a USB Wi-Fi adapter with monitor-mode support (e.g.
Alfa with mt76 driver) for the
--monitorflag
python3 -m venv .venv
.venv/bin/pip install -e .
sudo .venv/bin/python -m networkchartThen open http://127.0.0.1:8088/ in your browser.
Other deployment modes — Docker on Linux, Docker-in-VM from macOS /
Windows, remote SSH capture host, active bettercap MITM (lab only) — see
docs/deployment.md.
If you have BPF group access (access_bpf on macOS) you may not need sudo.
-i, --interface Interface to capture on (default: first wireless, then wired)
-f, --filter BPF filter expression, e.g. 'tcp port 443'
-p, --port HTTP port (default: 8088)
--host Host to bind to (default: 127.0.0.1)
-w, --window Rolling-window size in seconds (default: 300)
--monitor Enable Wi-Fi monitor mode (Linux/mt76; not stock macOS)
--no-promisc Disable promiscuous mode (default is on)
--no-autostart Don't start capture immediately
--list-interfaces Print interfaces and exit
# capture on wlan0 in monitor mode (Linux + mt76)
sudo .venv/bin/python -m networkchart -i wlan0 --monitor --host 0.0.0.0
# scoped capture with a BPF filter and a longer window
sudo .venv/bin/python -m networkchart -i en0 -f "tcp port 443" -w 600┌──────────────┐ packets ┌─────────────┐ ┌─────────────┐
│ AsyncSniffer│ ──────────────▶│ StatsStore │ │ HostStore │
│ (thread) │ ▲ │ (rolling) │ │ (lifetime) │
└──────────────┘ │ └──────┬──────┘ └──────┬──────┘
│ │ │
└────────────────│ InsightsStore │
│ (DNS/HTTP/SNI) │
│ │
▼ snapshot() ▼
┌──────────────┐ JSON via WS ┌──────────────────────┐
│ Browser SPA │ ◀───────────────│ FastAPI │
│ (Chart.js) │ │ /ws/stats /api/... │
└──────────────┘ └──────────────────────┘
A Scapy AsyncSniffer runs on a background thread and feeds every packet
into three stores in parallel:
- StatsStore — 1-second buckets in a rolling window for the live charts
- HostStore — long-lived per-device record (MAC, vendor, hostnames, per-peer/per-protocol breakdown), built passively from ARP, mDNS, DHCP, and regular IP traffic
- InsightsStore — fixed-size deque of recent DNS / HTTP / TLS-SNI events
Once a second the FastAPI WebSocket sends a fresh dashboard snapshot to
every connected browser. The Hosts and Activity tabs poll their own REST
endpoints (/api/hosts, /api/host/{ip}, /api/insights).
The server only binds to 127.0.0.1 by default — nothing on your LAN can
reach it. Use --host 0.0.0.0 to expose it (e.g. running on a Linux
capture host).
networkchart/
├── __init__.py
├── __main__.py # CLI entry point
├── stats.py # rolling-window aggregation (PacketRecord, StatsStore)
├── hosts.py # per-device catalog (HostStore + HostObservation)
├── insights.py # DNS / HTTP / TLS SNI parser (InsightsStore)
├── packet.py # Scapy packet -> PacketRecord
├── observe.py # Scapy packet -> HostObservation(s)
├── lansweep.py # active ARP + mDNS sweep
├── capture.py # AsyncSniffer thread wrapper
├── dns_cache.py # async reverse-DNS with TTL + negative caching
├── services.py # port -> service-name labels
├── interfaces.py # interface enumeration with type detection (mac+linux)
├── oui.py # MAC -> vendor lookup
├── web.py # FastAPI app (REST + WebSocket)
└── static/ # frontend SPA (vanilla JS + Chart.js)
├── index.html
├── app.css
├── app.js
└── chart.umd.js # vendored Chart.js
scripts/
└── deploy_kali.sh # rsync + venv install on a remote capture host
tests/ # pytest test suite (122 tests)
.venv/bin/python -m pytest122 tests covering: rolling-window stats, packet feature extraction, host observation extraction, OUI lookup, host store with per-peer/proto/service breakdown, InsightsStore (DNS, HTTP, TLS SNI parsing), DNS cache, service labels, interface picker, capture worker, lansweep, and the full FastAPI + WebSocket layer.