Initial commit: NetworkCity — network traffic as a neon city
A native macOS network monitor that renders live traffic as a top-down neon city: your Mac is downtown, remote orgs are glowing districts, and traffic is cars of light driving the roads. Architecture: - NetworkCityCore: swappable data layer behind a ConnectionSource protocol (nettop-backed today), plus pure/tested logic — traffic diffing, org classification (reverse-DNS + CIDR), traffic classes, hub-and-spoke expansion policy, label anti-overlap, and latency→distance layout. - NetworkCityApp: SwiftUI + SpriteKit city — auto-expanding districts, protocol-coloured cars, click-to-inspect panel, and latency-as-distance (districts glide to their measured RTT). - nettop-probe: CLI proof of the data layer. 44 tests passing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
# NetworkCity
|
||||
|
||||
A macOS network monitor that renders live traffic as a top-down **city map** —
|
||||
remote hosts become buildings, and your traffic becomes cars driving to them
|
||||
(SimCity-style). This repo currently contains **Phase 1: the proven data layer**.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
ConnectionSource (protocol) ← the seam: "where bytes come from"
|
||||
│
|
||||
├─ NettopSource ← Phase 1: shells out to /usr/bin/nettop (no privileges)
|
||||
└─ (libpcap source) ← Phase 2: real packets (needs privilege / helper tool)
|
||||
│
|
||||
▼
|
||||
ConnectionSnapshot ← list of Connection, with cumulative byte counts
|
||||
│
|
||||
▼
|
||||
SpriteKit "city" (Phase 3) ← buildings + cars; never knows the data source
|
||||
```
|
||||
|
||||
The renderer depends only on `NetworkCityCore`, so the data backend can be
|
||||
swapped without touching the visuals.
|
||||
|
||||
## Layout
|
||||
|
||||
- `Sources/NetworkCityCore/` — models, the `ConnectionSource` protocol, the
|
||||
`nettop` parser (`NettopParser`, pure/testable) and poller (`NettopSource`).
|
||||
- `Sources/nettop-probe/` — Phase-1 CLI proof of concept. Prints live KB/s.
|
||||
- `Tests/` — parser tests pinned to real captured `nettop` output.
|
||||
|
||||
## Try it
|
||||
|
||||
```sh
|
||||
swift test # parser tests against real fixtures
|
||||
swift run nettop-probe # live traffic, busiest external flows as KB/s
|
||||
```
|
||||
|
||||
## Key design facts (learned from real output)
|
||||
|
||||
- **nettop format**: process rows are `Name.PID,bytes_in,bytes_out`; the
|
||||
connection sub-rows beneath inherit that process. IPv4 ports use `:`, IPv6
|
||||
uses `.` and may carry a `%zone` suffix.
|
||||
- **Rates by diffing**: byte counts are lifetime-cumulative, so we keep the last
|
||||
snapshot and subtract. Robust to nettop quirks; works across fresh invocations.
|
||||
- **Process names are unreliable** — nettop truncates/mangles them (it reported
|
||||
`claude` as `2.1.177`). The **pid is trustworthy**; enrich the real name via
|
||||
`proc_pidpath`/`ps` keyed on pid. (Phase-2 polish.)
|
||||
- **`isExternal`** filters listeners, loopback, and link-local so only real
|
||||
off-box destinations become buildings.
|
||||
|
||||
## Roadmap
|
||||
|
||||
1. ✅ Data layer + live probe (nettop, no privileges)
|
||||
2. ✅ SpriteKit city — SwiftUI window, glowing buildings, cars of light
|
||||
(`swift run NetworkCityApp`): cyan cars inbound = downloads, amber outbound
|
||||
= uploads; deterministic building placement; drag to pan, scroll to zoom.
|
||||
3. ✅ Enrichment — real process names (`proc_pidpath`), reverse-DNS + a curated
|
||||
CIDR table so hosts group into named **districts** (Apple, Google, AWS,
|
||||
Telegram, Local Network…). PTR is primary; the CIDR table catches the
|
||||
no-PTR giants (all of `17.0.0.0/8` is Apple). Run with `NC_DEBUG=1` to log
|
||||
each host→district resolution to stderr.
|
||||
4. ✅ Hub-and-spoke — busy districts auto-expand into their endpoints
|
||||
(`This Mac → OneDrive → its many servers`); cars route the full two-segment
|
||||
path. Expansion is a pure, tested `ExpansionPolicy` (≥2 endpoints, top-K by
|
||||
bytes, TTL tail to avoid flicker). A single client downloading from a CDN
|
||||
pins to one IP, so fan-out is driven by apps that genuinely spread
|
||||
connections (sync clients, browsers).
|
||||
5. ✅ Traffic differentiation — cars coloured by protocol class (DNS gold,
|
||||
HTTPS cyan, HTTP orange, QUIC violet, Other slate) since direction is already
|
||||
read from motion. Behaviour reinforces it: DNS = tiny fast sparks, QUIC =
|
||||
streaks, heavy flows = big slow "freight" comets. Classifier is a pure tested
|
||||
`TrafficClass`; HUD shows a legend.
|
||||
6. ✅ Label anti-overlap (`LayoutSolver`).
|
||||
7. ✅ Click-to-inspect — click a district hub to open a live panel: total
|
||||
down/up, every endpoint with its reverse-DNS name + per-endpoint rate +
|
||||
protocol-colour dot, and the owning process(es). Click empty space or ✕ to
|
||||
close. A pulsing ring marks the selection. Click vs drag is disambiguated by
|
||||
movement distance.
|
||||
8. ✅ Latency as distance — distance from downtown = measured round-trip time
|
||||
(unprivileged TCP-connect probe, cached), not geography. GeoIP was rejected
|
||||
because anycast makes it assert a physical fiction; latency is measured so it
|
||||
can't lie. Districts glide to their true distance as probes complete; the
|
||||
inspector shows per-endpoint ms. Pure `LatencyLayout` (log curve) is tested;
|
||||
`LatencyProber` uses Network.framework. Our own pid is filtered out so we
|
||||
don't render our own probes.
|
||||
9. `MenuBarExtra` shell; optional libpcap source via privileged helper.
|
||||
|
||||
### Env flags (opt-in diagnostics)
|
||||
- `NC_DEBUG=1` — log host→district resolution, per-tick top districts, and fan-outs to stderr.
|
||||
- `NC_DEMO=1` — offline demo: feeds a synthetic 8-endpoint "OneDrive" through the
|
||||
real scene so the hub-and-spoke fan-out runs without nettop.
|
||||
Reference in New Issue
Block a user