Making Tor Traffic Visible Without Weakening Its Privacy Model
TL;DR: Network visualization becomes much more useful when it explains where traffic is going without quietly creating a second privacy problem. Torbit captures outbound IPv4 flows, enriches them through local-first GeoIP, optionally correlates local Tor circuits, and renders the result as a live terminal map. The difficult part was not drawing arcs. It was making every potentially identifying lookup explicit, bounded, and testable.
A terminal is a good place for network truth
Most network monitors optimize for storage. They collect everything, send it somewhere, and provide a dashboard after the fact. That is useful for fleet observability, but excessive when the question is immediate and local: what is this machine talking to right now?
Torbit takes the opposite position. It is a transient decision surface. Outbound flows appear on a Braille world map alongside their protocol, destination, interface, country, packet count, and byte volume. The operator can focus a connection, isolate Tor traffic, or switch the layout without leaving the shell.
The terminal constraint is productive. There is no browser runtime, no collector account, and no remote telemetry service required to understand a laptop or server. Demo mode produces representative traffic without packet-capture privileges, while live mode uses Scapy and therefore requires raw-socket capabilities.
The runtime is an event pipeline
The first implementation could have been one loop: sniff a packet, resolve its address, then redraw the screen. That design stops behaving well as soon as a lookup is slow or traffic becomes busy.
Torbit separates the work into a small pipeline:
outbound packet
-> packet classification
-> flow event
-> GeoIP enrichment
-> optional hop or Tor enrichment
-> UI state reduction
-> Rich render
Capture, enrichment, tracing, Tor control, and rendering communicate through bounded queues. When a consumer falls behind, the oldest pending observation is discarded in favor of current state. This is intentional. Torbit is not an audit log; it is a live view. Showing a recent flow is more valuable than faithfully replaying a backlog that is already stale.
The data model preserves that distinction. A packet observation carries interface, protocol, ports, size, and a stable flow key. Enrichment produces an update for the same flow instead of pretending that a resolved hostname or traceroute hop is another packet. Counters therefore describe captured traffic, not the amount of internal processing performed on it.
Geolocation has a privacy cost
An IP address does not contain coordinates. A map requires a GeoIP provider, and the easiest provider is usually a remote HTTP endpoint. Calling that endpoint for every destination would disclose the exact set of addresses being observed to a third party.
Torbit therefore treats local MaxMind-compatible data as the preferred path. Remote GeoIP is disabled by default and must be enabled explicitly. Reverse DNS and active traceroute are independent opt-ins as well: one reveals queries to a resolver, while the other emits network probes that did not exist in the captured application flow.
The home point follows the same rule. Live mode accepts explicit latitude and longitude when no local database can derive them. It does not invent an arbitrary origin just to ensure that the map looks complete.
These defaults make the interface slightly less magical and materially easier to reason about. Missing information is visible as missing information instead of being filled through an undeclared network side effect.
Tor integration cannot be just another lookup
Packet capture alone sees encrypted connections to Tor relays. It cannot reliably explain which local stream is attached to which circuit. Torbit can optionally read that relationship from a local Tor ControlPort.
That integration has a narrow trust boundary:
- The endpoint must be a Unix socket or loopback TCP peer.
- Remote Tor daemons should be reached through a local SSH tunnel.
- SAFECOOKIE authentication verifies both sides of the challenge.
- Authentication cookies must be regular, non-symlink files of exactly 32 bytes.
- Remote GeoIP is rejected when Tor control or Tor-only mode is active.
The last rule matters. Correlating a live Tor destination through an external GeoIP service would undermine the reason for observing Tor carefully in the first place.
The UI maps the guard and exit context that helps an operator understand a circuit. Middle relays stay hidden from the default route display because exposing every internal hop adds visual noise without improving the common diagnostic question. Tor-only mode can then remove unrelated clearnet flows from both the connection table and map markers.
Rendering is a state problem, not a drawing problem
The map itself uses a cached land mask and a virtual 2x4 Braille-dot layer. The interesting behavior lives around it: routes reveal their segments in sequence, active paths persist long enough to be read, old paths decay, and markers remain stable while new events arrive.
Connection selection also has to survive updates. A naive implementation keys focus by row index, so every incoming packet moves the selection. Torbit keys it by flow identity. Lazy list updates reduce unnecessary table churn while preserving the operator's current target.
Those details are the difference between a terminal animation and an instrument that can be used while traffic is changing.
Tests are part of the privacy model
The test suite covers more than map projection. It asserts that remote GeoIP, reverse DNS, traceroute, automatic sudo, and remote ControlPort access stay disabled until explicitly requested. It verifies SAFECOOKIE negotiation, cookie-file constraints, untrusted terminal labels, outbound packet classification, flow deduplication, Tor filtering, and route progression.
At the time of writing, the suite contains 77 passing tests plus 28 parametrized subcases. That number is less important than what the tests protect: command-line defaults are security behavior. A future refactor should not be able to turn a local-only operation into an external lookup by accident.
The remaining boundary
Torbit still runs packet capture and the interface in one process, and its worker threads do not yet share a fully owned cancellation lifecycle. The next structural improvement is a small privileged capture helper feeding an unprivileged UI process, with explicit stop, join, and queue-drop metrics.
That limitation is documented because observability software should not claim a cleaner privilege boundary than it actually has.
The useful result today is narrower and concrete: a fast, local terminal view of outbound traffic, with Tor context when requested and privacy-sensitive enrichment kept under operator control.
