timban c08fc277a9 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>
2026-06-13 08:43:32 -07:00

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

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.
S
Description
Network monitor for MacOS
Readme 90 KiB
Languages
Swift 100%