timban 34cdcee00b Group Microsoft/OneDrive no-PTR ranges into one district
OneDrive's IPs (e.g. 13.107.x, 150.171.x) have no PTR and weren't in our
CIDR table, so each fragmented into its own IP-named district — a long
session showed dozens, all with the "OneDrive" process subtitle. Add the
documented Microsoft 365 service ranges so they collapse to one Microsoft
district. Deliberately excludes generic Azure tenant space, which would
mislabel third-party apps hosted on Azure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 10:46:53 -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%