RustNet: per-process network monitoring in the terminal
Per-process network monitoring for your terminal with deep packet inspection. Cross-platform, sandboxed.
At a glance
- What is it?
- RustNet maps every TCP, UDP and QUIC connection to the process that owns it, adds deep packet inspection, and runs sandboxed by default. It sits between netstat and Wireshark, and the README is candid about what it drops under load.
- Who is it for?
- Adopt RustNet if you need to answer which process opened a connection on a machine you already have shell access to, and you want the answer without X11 forwarding or a root tcpdump pipe. Skip it if you need full packet payloads or guaranteed capture under sustained overload, because the README states the queue is bounded at 10,000 packets and bursts can drop.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The gap RustNet targets between netstat and Wireshark
netstat and ss tell you a socket exists and what state it is in. They do not tell you which binary opened it, and they show a snapshot rather than a live view. Wireshark and tcpdump see packets but not sockets, so they cannot name the owning process at all. RustNet's README states this split directly: Wireshark cannot provide process attribution because it only sees packets, not sockets.
The tool is aimed at engineers who are already on the box. The README calls out SSH-friendliness as a design goal: the TUI runs over an SSH session, so you can look at a remote server without forwarding X11 or piping a capture back to your laptop. That is the audience. Not a network operations centre running continuous capture, but someone debugging why a host is talking to an address they do not recognise, or which of twelve services on a machine is holding a connection open.
Attribution is per platform, and the README is explicit about the mechanism on each: eBPF on Linux, PKTAP on macOS, ETW with an automatic IP Helper fallback on Windows, and native APIs on FreeBSD. The reported fields include PID, executable path, user and group names, a match confidence value, and a capped parent-process chain. That confidence field is worth noticing. It implies attribution is not always exact, which is honest for a tool that has to correlate kernel socket data with process tables.
How capture, parsing and connection state fit together
The architecture is a pipeline with a deliberate ordering rule. Packet parsing runs in parallel across workers, but connection updates and annotated export writes keep capture order. Parallel workers group up to 16 already queued batches into one ordered update and do not wait to fill the group, so a quiet link does not stall on a batch boundary. When only one processor is active, it parses and updates each packet immediately and skips the staging allocation for DPI.
The bound is where the design gets interesting. The queue holds at most 10,000 packets, plus up to 1,600 in-flight packets per worker, up to 6,400 across at most four workers, and that memory is separate from the rest of the application. When the queue is full, the writer uses a 5 ms send timeout before dropping the batch. The README says plainly that bursts or sustained overload can still drop packets in the queue or in the capture backend. There is no claim of lossless capture, and you should not read one into it.
Idle handling is the other half. Supported capture backends on Linux, macOS, FreeBSD and Windows wake the reader when traffic arrives. Where native readiness is unavailable, the code falls back to sleep polling against a shared 10 ms idle wait budget, which also preserves shutdown and partial-batch flushing. The README is careful to note this is not a per-packet delay. That distinction matters if you are trying to reason about latency overhead.
The workspace splits the code into four library crates plus the binary: rustnet-core, rustnet-capture, rustnet-host and rustnet-sandbox, all at version 0.5.0, while the binary tracks the 1.x user-facing line at 1.6.0. rustnet-host holds the eBPF programs and bundled vmlinux headers compiled by its own build.rs, and rustnet-core carries the baked-in oui.gz and services assets.
Installing RustNet and reading your first connection list
The repository ships a Dockerfile and a Dockerfile.static, and the README links a ghcr.io image. The Dockerfile is multi-stage: a rust:1.98-slim builder pinned by digest installs libpcap-dev, libelf-dev, zlib1g-dev, clang, llvm, make and pkg-config, then builds in release mode, and the runtime stage is debian:trixie-slim, also pinned by digest. On Linux, eBPF is enabled by default in that build. The Kubernetes feature is additive and off unless you pass it through the build argument the Dockerfile defines:
ARG CARGO_FEATURES=""
RUN if [ -n "$CARGO_FEATURES" ]; then \
cargo build --release --features "$CARGO_FEATURES"; \
else \
cargo build --release; \
fiThe Dockerfile comment states that the CI Kubernetes image variant passes CARGO_FEATURES=kubernetes, while the default image leaves it empty. The README points at INSTALL.md for platform packages, and the debian/ and rpm/ directories exist at the top level, so distribution packaging is part of the repository rather than an afterthought. The crate is published as rustnet-monitor on crates.io. Once installed, the first thing to check is which build you actually have, because the README warns that main documents unreleased features:
rustnet --version
rustnet --helpThe README states that for v1.6.0 you should read the v1.6.0 documentation rather than the main branch README, and that rustnet --version reports your installed version while rustnet --help lists its supported options. That is not boilerplate. The features list marks WireGuard/OpenVPN detection, the Host tab, Health badges and sorting, the idle countdown, improved Linux attribution of pre-existing sockets, the Activity browser, compact-layout controls and the pid: filter as unreleased since v1.6.0. If you install 1.6.0 and go looking for the Host tab, you will not find it.
For a first real use, start the TUI and filter. The filter syntax is vim and fzf style, with port:, src:, dst:, sni:, process:, state: and proto: prefixes, plus regex in the form /(?i)pattern/. Pressing t toggles historic connections so closed sockets stay visible for forensics. If you want a file rather than a screen, --pcapng-export writes a Wireshark-ready capture with process, PID, direction, DPI/SNI and GeoIP embedded as per-packet comments, and the classic --pcap-export writes a capture with a JSONL sidecar for offline correlation.
Sandboxing, and what a failed privilege drop actually does
RustNet applies an OS-level sandbox on each platform: Landlock on Linux 5.13 and newer, Seatbelt on macOS, and on Windows a token privilege drop combined with a job-object child-process block. Privileges are dropped after initialization.
The detail that separates this from a marketing bullet is the failure path. The README states that a failed requested UID/GID drop stops startup before packet-processing workers run. So if you ask for a privilege drop and it does not take, you get no monitor rather than a monitor running with more privilege than you asked for. That is a defensible choice for a tool that needs capture rights, and it is the kind of behaviour you should confirm against SECURITY.md before you put it in a pipeline, because a hard failure on startup changes how you write your invocation.
Landlock has a version floor. On a kernel older than 5.13 the Linux sandbox cannot engage, and the README does not describe what happens in that case. If you are on an older distribution kernel, that is the first thing to verify, not the last.
Where RustNet is the wrong tool
The README is unusually direct about the capture bound, and it should be taken at face value. The queue caps at 10,000 packets with a 5 ms send timeout before a batch is dropped, and the README states that bursts or sustained overload can still drop packets in the queue or in the capture backend. If your job is to produce a forensically complete capture of a busy 10 GbE link, RustNet is not that tool. Use a dedicated capture stack and accept that you will lose process attribution.
Payload inspection is the second boundary. Deep packet inspection here identifies protocols, including HTTP, TLS with SNI, DNS, SSH, FTP, QUIC, MQTT, BitTorrent, WireGuard, OpenVPN, STUN, NTP, mDNS, LLMNR, DHCP, SNMP, SSDP and NetBIOS, without external dissectors. Identifying a protocol is not the same as giving you the bytes. If you need to read a TLS record or reconstruct a stream, the PCAPNG export is the handoff point, not the TUI.
The third boundary is the documentation itself. The README carries an explicit warning that main describes development code and may include unreleased features, and then lists a substantial set of those features. Anyone evaluating from the repository front page will see capabilities that a released binary does not have. That is a real cost for an evaluator, and a reason to pin your reading to the v1.6.0 README and to CHANGELOG.md.
How RustNet differs from Sniffnet and Bandwhich
The related searches around this project point at Sniffnet and Bandwhich, and the difference is structural rather than cosmetic. Sniffnet is a packet-level traffic monitor: it presents captured traffic and its statistics, and the searches pairing it with Wireshark and OpenWrt place it in the packet-capture lineage. RustNet starts from the socket side and attaches process ownership to each connection, which is the inverse starting point. A Sniffnet user looking for per-process attribution has to correlate separately; RustNet's README frames that correlation as the product.
Bandwhich also does per-process bandwidth attribution, and the comparison is closer. The distinction the README draws is depth of protocol work: RustNet adds DPI across the protocol list above, round-trip time measurement for TCP, QUIC handshakes, DNS responses and ICMP echo, plus TCP retransmission, out-of-order and fast-retransmit detection, and annotated PCAPNG export with GeoIP comments. Whether that extra depth is worth it depends on whether you need protocol identification or only a ranked list of talkers. For a quick answer to which process is saturating a link, the lighter tool is sufficient and RustNet is more machinery than the question requires.
On the netstat side, the README's own framing is that netstat and ss cannot show live state. That is the honest summary of the alternative: if a snapshot is enough, you already have the tool installed.
Maintenance, licence and the cost of upgrading
The repository is not archived, and the last push was on 2026-09-21. Releases have come at a steady cadence through 2026: v1.4.0 on 2026-06-16, v1.5.0 on 2026-07-21, and v1.6.0 on 2026-08-20. The binary version is decoupled from the library crates, which sit at 0.5.0, and Cargo.toml notes that the binary keeps its own version because it tracks the 1.x release line independently. That means a workspace version bump is a one-line edit in the shared metadata section, but it also means you cannot infer the binary version from the crate versions.
The rust-version is 1.88.0, with a comment that let-chains require it. Edition is 2024. If you build from source rather than installing a package, that is your toolchain floor, and the Dockerfile pins rust:1.98-slim as the builder, so container builds are well above it.
Licence is Apache-2.0 across the workspace, declared once in the shared package metadata. Apache-2.0 is permissive and includes an explicit patent grant, which matters for a tool that links against kernel interfaces. It also means there is no copyleft obligation on your own code. This is a description of the licence text, not legal advice; if you are redistributing a modified binary, read LICENSE and the NOTICE situation for the bundled assets, since rustnet-core bakes in an OUI database and a services file, and the GeoIP path uses a local MaxMind GeoLite2 database that carries its own terms.
The upgrade cost is documentation drift. Because main describes unreleased behaviour, an upgrade from v1.6.0 to the next release is where the features listed as pending actually land. Read CHANGELOG.md rather than the README diff when you move, and re-check rustnet --help, because the README states that --help is the authority on which options your installed build supports.
Editorial conclusion
Adopt RustNet if you need to answer which process opened a connection on a machine you already have shell access to, and you want the answer without X11 forwarding or a root tcpdump pipe. Skip it if you need full packet payloads or guaranteed capture under sustained overload, because the README states the queue is bounded at 10,000 packets and bursts can drop. Before rolling it out, run rustnet --version against the v1.6.0 README, since several documented features are unreleased on main, and check whether your kernel is 5.13 or newer so Landlock actually engages.
Frequently asked questions
How is Rust used in networking?
RustNet is one example: it is written in Rust and uses the language for a capture pipeline where packet parsing runs in parallel while connection updates keep capture order. The workspace uses Rust 2024 edition and requires rust-version 1.88.0, noted in Cargo.toml as needed for let-chains.
How do I install RustNet on Linux?
The README points to INSTALL.md and the repository carries debian/ and rpm/ directories for distribution packaging, plus a Dockerfile and Dockerfile.static with a ghcr.io image linked from the badge row. The crate is published on crates.io as rustnet-monitor.
Does RustNet work on Windows and macOS?
Yes. The README lists Linux, macOS, Windows and FreeBSD as supported, with per-process attribution via ETW and an automatic IP Helper fallback on Windows, PKTAP on macOS, and native APIs on FreeBSD. Sandboxing is platform-specific: Landlock on Linux 5.13+, Seatbelt on macOS, and a token privilege drop with a job-object child-process block on Windows.
Which features are missing from the RustNet v1.6.0 release?
The README marks WireGuard/OpenVPN detection, the Host tab, Health badges and sorting, the idle countdown, improved Linux attribution of pre-existing sockets, the Activity browser, compact-layout controls and the pid: filter as unreleased since v1.6.0. It directs readers to CHANGELOG.md for the full list of pending changes.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/domcyrus-rustnet)