htop for your network: a real-time IP traffic monitor for the terminal, ported to Zig from iptop
  • Zig 94.8%
  • Nix 5.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Jeffrey C. Ollie 098667f07b
All checks were successful
test / test (push) Successful in 12m1s
Give the Tangled mirror's clone command in the README
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WMedP6sZjZGg8FvX8TZ6oX
2026-10-20 20:01:57 -05:00
.forgejo/workflows Port iptop to Zig 0.17 2026-10-20 19:13:35 -05:00
LICENSES Port iptop to Zig 0.17 2026-10-20 19:13:35 -05:00
src Port iptop to Zig 0.17 2026-10-20 19:13:35 -05:00
tools Read the world map's polygons with zig-geojson 2026-10-20 19:42:49 -05:00
.gitignore Port iptop to Zig 0.17 2026-10-20 19:13:35 -05:00
build.zig Read the world map's polygons with zig-geojson 2026-10-20 19:42:49 -05:00
build.zig.zon Read the world map's polygons with zig-geojson 2026-10-20 19:42:49 -05:00
build.zig.zon.nix Read the world map's polygons with zig-geojson 2026-10-20 19:42:49 -05:00
CONTRIBUTING.md Port iptop to Zig 0.17 2026-10-20 19:13:35 -05:00
flake.lock Port iptop to Zig 0.17 2026-10-20 19:13:35 -05:00
flake.nix Port iptop to Zig 0.17 2026-10-20 19:13:35 -05:00
package.nix Port iptop to Zig 0.17 2026-10-20 19:13:35 -05:00
README.md Give the Tangled mirror's clone command in the README 2026-10-20 20:01:57 -05:00
REUSE.toml Port iptop to Zig 0.17 2026-10-20 19:13:35 -05:00

ziptop

htop for your network: a real-time IP traffic monitor for the terminal, written in Zig 0.17. It is a port of franlol's iptop, which is written in TypeScript for Bun, and it draws the same screen from the same sources.

  • Throughput charts in braille — download rising and upload hanging below it, two samples to a cell and four dots of height per row, with the live rate, the peak and average of the window, and the total since boot.
  • Traffic by process — every process with an open socket, by rate or by total, with an inspector that charts one process and lists its connections.
  • Traffic by remote host and by service — who the traffic is with, resolved by name and placed by country, and which services carry it (https against QUIC, ssh, DNS and the rest), each with a center-pivot gauge of download against upload.
  • A world map — the countries the traffic goes to, tinted by how much, with a dot for each host on a braille map of the world. ziptop --map makes it the whole screen.
  • Every socket — each TCP and UDP connection and listener, busiest first.
  • Latency — round trips to the gateway, to the Internet (1.1.1.1) and to the DNS server, with their jitter charted and the packet loss counted.
  • No root and no packet capture — it reads what the operating system's own tools already report, every half second.

Fourteen color themes come with it, switched with t and remembered.

Running it

$ nix run git+https://git.jcollie.dev/jeff/ziptop.git

or build it (see Building) and run ziptop. ziptop --map starts in the fullscreen world map, ziptop --help lists the options and ziptop --version prints the version.

Keys

Key Does
j k ↓ ↑ Move the selection through the processes
⏎ Inspect the selected process
esc Leave the inspector, or quit
s Sort the processes by rate or by total, and show that in the bars
m Show or hide the world map
t Open the theme picker, where j and k preview, ⏎ keeps and esc goes back
[ ] Sample less or more often: every 2 s, 1 s or 0.5 s
mouse wheel Scroll the list under the pointer
q, ctrl-c Quit

The map is left out when the terminal is fewer than 32 rows tall.

The settings file

The chosen theme is kept in $XDG_CONFIG_HOME/ziptop/config, or ~/.config/ziptop/config when XDG_CONFIG_HOME is unset. It is plain key = value lines with # comments, so it can be edited by hand:

theme = Gruvbox Dark

The themes are Tokyo Night (the default), Catppuccin Mocha, Gruvbox Dark, Nord, Dracula, Rosé Pine, Solarized Dark, One Dark, Everforest, Kanagawa, Monokai, Ayu Dark, Catppuccin Latte and Rosé Pine Dawn.

What it reads

It runs on Linux and macOS, and on each it reads the tools already there:

Linux macOS
Interface counters /proc/net/dev netstat -ibn
Traffic by process and connection ss -tinp nettop -x -J bytes_in,bytes_out -L 1
Sockets ss -tunap lsof -nP -i -FpcnPT
Gateway and local address ip route, ip addr route -n get default, ipconfig getifaddr
DNS server /etc/resolv.conf scutil --dns
Latency ping ping

On Linux that is iproute2 and iputils. Without root, a socket belonging to another user is listed without its process, and its traffic is not counted against any process. Linux keeps byte counters for TCP sockets only, so UDP traffic — QUIC most of all — shows in the throughput charts and in the services panel's connection count, but not in the rates of a process or a host.

Some network drivers refresh their counters only every second or two. A rate is therefore worked out over the time since an interface's counters last moved, and held for up to two and a half seconds while they do not, rather than reading as zero on every sample that falls between two refreshes.

What it asks of the Internet

Three things leave the machine:

  • Reverse DNS lookups of the remote addresses, through the resolvers in /etc/resolv.conf, so that hosts are shown by name.
  • Geolocation of every public remote address, from ip-api.com. Its free tier is plain HTTP only, so the addresses being looked up cross the network unencrypted. Addresses are sent a hundred at a time to its batch endpoint, no more than its limit of fifteen requests a minute allows, and private, loopback and link-local addresses are never sent.
  • The public address of the network, from api.ipify.org over HTTPS, once at startup.

Building

The development shell has the Zig compiler and everything else:

$ nix develop
$ zig build                 # zig-out/bin/ziptop
$ zig build run             # build and run it
$ zig build run -- --map
$ zig build test

nix build builds the package, running the tests on the way, and nix flake check does the same.

The world map

src/worldmap_data.zig is a land bitmap of 180 by 72 dots — longitude −180° to 180° and latitude 84° to −60° — with each land dot tagged with the country it falls in. It is generated rather than written, from country polygons in GeoJSON and a table of ISO 3166 codes:

$ curl -LO https://raw.githubusercontent.com/johan/world.geo.json/master/countries.geo.json
$ curl -Lo iso3166.json https://raw.githubusercontent.com/lukes/ISO-3166-Countries-with-Regional-Codes/master/all/all.json
$ zig build worldmap -- countries.geo.json iso3166.json > src/worldmap_data.zig

The tool reads the polygons with zig-geojson and asks it whether the center of each dot is inside one, holes and all. The tool is the only thing that uses zig-geojson; nothing of it is compiled into ziptop.

How it is put together

  • src/collectors/ parses each tool's output into counters, with no I/O of its own, so every parser is tested against captured output. traffic.zig is the engine that turns two samples of counters into rates, smooths them so that rows do not trade places on every sample, and lets processes and hosts that fall quiet decay rather than vanish.
  • src/sampler.zig runs the collectors on their own tasks and keeps the newest sample of each kind, each in an arena of its own, in a store the interface draws from.
  • src/lookups.zig answers the reverse lookups and the geolocation in the background, from caches: the first sample that mentions an address only asks, and a later one finds the answer.
  • src/ui.zig draws the screen with libvaxis and handles the keys and the mouse. src/braille.zig, src/format.zig, src/tint.zig and src/theme.zig are the charts, the numbers, the country colors and the palettes it draws with.

Repository

The repository lives on a Forgejo instance and is mirrored to Tangled:

$ git clone https://git.jcollie.dev/jeff/ziptop.git    # Forgejo
$ git clone https://tangled.org/jcollie.dev/ziptop     # Tangled

Both carry the same history on the same main branch.

Issues and contributions

Report issues and submit pull requests on the Tangled mirror, https://tangled.org/jcollie.dev/ziptop. CONTRIBUTING.md says what a change needs to pass.

References cited

License

MIT. The port keeps the copyright of the program it is ported from, which is © 2026 Francesc Muñoz and also MIT licensed; see LICENSES/MIT.txt and the header of each file.