Skip to content

Repository files navigation

Networkchart

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.

What it shows

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.

Requirements

  • 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 --monitor flag

Quickstart (Linux/macOS native)

python3 -m venv .venv
.venv/bin/pip install -e .
sudo .venv/bin/python -m networkchart

Then 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.

Useful flags

-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

How it works

┌──────────────┐    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).

Project layout

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)

Tests

.venv/bin/python -m pytest

122 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.

About

Live web dashboard for passive packet-capture and LAN host discovery. Scapy + FastAPI + Chart.js.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages