14 KiB
Project
tcptop
A polished command-line utility that provides a real-time, top-like TUI for layer 4 (TCP/UDP) network statistics. It shows per-connection data — bytes, packets, latency, and state — in a sortable table with an aggregate summary header. It also supports CSV logging for offline analysis. Built in Rust using eBPF for kernel-level connection tracing, targeting both macOS and Linux.
Core Value: Live, accurate, per-connection network visibility from the terminal — the network equivalent of top.
Constraints
- Privileges: Requires root/elevated permissions for kernel tracing — must handle gracefully when not root
- Platform divergence: macOS and Linux have different kernel tracing APIs — architecture must abstract this
- Language: Rust (preferred for performance, safety, and eBPF ecosystem)
- Overhead: Must be low-impact on the system being observed — no heavy polling
Technology Stack
Recommended Stack
Core Technologies
| Technology | Version | Purpose | Why Recommended |
|---|---|---|---|
| Rust (nightly) | nightly-2026-xx | Language + eBPF compilation | Nightly required for eBPF target (bpfel-unknown-none, -Z build-std=core). Pin a specific nightly via rust-toolchain.toml for reproducibility. |
| Aya | 0.13.x | eBPF library (userspace loader + kernel program SDK) | Pure Rust eBPF, no libbpf/bcc dependency. BTF support for kernel portability. Async tokio integration via AsyncPerfEventArray and RingBufAsync. The standard choice for Rust eBPF -- no real competitor in the Rust ecosystem. |
| aya-ebpf | 0.1.x | eBPF kernel-side program macros | Companion to aya. Provides #[kprobe], #[tracepoint], #[cgroup_skb] macros for writing eBPF programs in Rust. |
| Ratatui | 0.30.x | Terminal UI framework | Undisputed standard for Rust TUIs. Immediate-mode rendering, sub-millisecond performance, huge widget library. Table, sparklines, and layout primitives are exactly what a top-like tool needs. |
| Crossterm | 0.28.x+ | Terminal backend for Ratatui | Default backend for ratatui. Cross-platform (Linux, macOS). Pure Rust, no ncurses dependency. |
| Tokio | 1.43+ (LTS) | Async runtime | Required by Aya's async features (AsyncPerfEventArray, RingBufAsync). Handles concurrent event processing from eBPF maps and TUI rendering loop. Use the LTS release for stability. |
| clap | 4.6.x | CLI argument parsing | De facto standard. Derive macros for zero-boilerplate argument definitions. Supports --port, --log, --pid flags naturally. |
Platform-Specific Backend (Data Collection)
| Technology | Platform | Purpose | Why Recommended |
|---|---|---|---|
| Aya (eBPF kprobes/tracepoints) | Linux | Kernel-level connection tracing | Lowest overhead. Attach to tcp_connect, tcp_close, tcp_sendmsg, udp_sendmsg etc. Data flows via RingBuf or PerfEventArray to userspace. |
| pcap (libpcap bindings) | macOS (primary) | Packet capture with PKTAP | macOS PKTAP embeds PID and process name directly in packet headers. The pcap crate (v2.3.x) wraps libpcap which is pre-installed on macOS. PKTAP is the closest macOS equivalent to eBPF for network monitoring. |
| sysinfo | Both | Process name/metadata enrichment | Cross-platform process information (PID to name mapping, etc.). Supplements both backends. |
| procfs | Linux only | /proc/net/* parsing (fallback) | Fallback for connection state when eBPF data is incomplete. Parse /proc/net/tcp, /proc/net/udp for socket-to-PID mapping. |
Supporting Libraries
| Library | Version | Purpose | When to Use |
|---|---|---|---|
| csv | 1.4.x | CSV log file writing | --log output.csv mode. BurntSushi's csv crate is the only sensible choice -- fast, serde-integrated, battle-tested (129M+ downloads). |
| serde + serde_json | 1.x | Serialization framework | Derive Serialize for connection records. Used by csv crate for struct-to-row mapping. |
| anyhow | 1.x | Error handling (application) | Ergonomic error handling in the binary crate. Use thiserror in library code if you extract one. |
| thiserror | 2.x | Error handling (library) | Typed errors for the platform abstraction layer and data collection backends. |
| nix | 0.29.x | Unix system calls | Low-level syscall access (socket options, process info, privilege checking). Cross-platform Unix support. |
| libc | 0.2.x | FFI type definitions | Required by aya and nix. Transitive dependency but worth pinning. |
| log + env_logger | 0.4.x / 0.11.x | Logging | Debug logging during development. Not for the TUI output -- for internal diagnostics. |
| signal-hook | 0.3.x | Signal handling | Graceful shutdown on SIGINT/SIGTERM. Important for root-privilege tools that manipulate kernel state. |
Build and Development Tools
| Tool | Purpose | Notes |
|---|---|---|
| bpf-linker | Links eBPF object files | cargo install bpf-linker. On macOS, install with --no-default-features and ensure LLVM 21+ is available. |
| cargo-generate | Project scaffolding | Use aya-template for initial project structure. Optional after initial setup. |
| rust-toolchain.toml | Pin nightly version | Critical for reproducible eBPF builds. Pin both nightly channel and bpfel-unknown-none target. |
| cargo xtask | Build orchestration | Aya projects use an xtask pattern: a workspace member that builds the eBPF program and embeds it in the userspace binary. Standard pattern -- follow it. |
| cross | Cross-compilation | For building Linux binaries from macOS (CI/CD). Not needed for local dev if targeting native platform. |
Project Structure (Aya Convention)
Installation
Prerequisites (Linux)
Prerequisites (macOS -- for cross-compiling eBPF or developing userspace)
Project dependencies (in workspace Cargo.toml for userspace crate)
eBPF crate dependencies (tcptop-ebpf/Cargo.toml)
aya-ebpf = "0.1"
aya-log-ebpf = "0.1"
Alternatives Considered
| Recommended | Alternative | When to Use Alternative |
|---|---|---|
| Aya | libbpf-rs (Rust bindings for libbpf) | If you need C-based eBPF programs or have existing .bpf.c code to integrate. Aya is better for pure-Rust projects. |
| Aya | RedBPF | Never -- RedBPF is largely unmaintained and has been superseded by Aya in the Rust ecosystem. |
| Ratatui | cursive | If you want a callback-based (retained mode) TUI instead of immediate mode. Ratatui is better for data-heavy dashboards that redraw frequently. |
| Crossterm | termion | If you want a simpler API and only target Unix. Crossterm is better because it already works on both platforms and is ratatui's default. |
| Tokio | async-std | Aya supports both, but tokio has far more ecosystem support and is the standard async runtime. No reason to choose async-std. |
| clap | argh (Google) | Only if you need minimal binary size and don't need rich help text. clap is better for user-facing CLI tools. |
| pcap (macOS) | DTrace via FFI | DTrace is powerful but the Rust FFI story is poor, scripting interface is awkward from Rust, and PKTAP via libpcap is simpler and provides process attribution natively. |
| pcap (macOS) | Network Extension Framework | Requires app sandbox, provisioning profiles, and Apple Developer account. Massive overkill for a CLI tool. |
What NOT to Use
| Avoid | Why | Use Instead |
|---|---|---|
| RedBPF | Largely abandoned, last meaningful update 2022. Aya has won the Rust eBPF space. | Aya |
| tui-rs (original) | Deprecated. Ratatui is the maintained fork that the community migrated to in 2023. | Ratatui |
| termion | Doesn't support Windows (not relevant here), but more importantly crossterm is ratatui's default and better maintained. | Crossterm |
| ncurses / pancurses | C dependency, complex linking, less Rust-idiomatic. | Ratatui + Crossterm (pure Rust) |
| raw /proc parsing for all data | Fragile, slow (polling), and misses short-lived connections. | eBPF (event-driven, kernel-level) with /proc as fallback only |
| npcap / WinPcap | Windows only. Out of scope. | pcap (libpcap) for macOS |
| pnet (Rust networking) | Lower-level than needed, doesn't provide PKTAP integration, would require reimplementing what libpcap gives you. | pcap crate |
Stack Patterns by Variant
- Skip the pcap dependency entirely
- Skip the platform abstraction trait (simplifies architecture)
- Use Aya directly in the main collector module
- This significantly reduces complexity
- Must implement the
NetworkCollectortrait with two backends - macOS backend uses pcap with PKTAP for packet capture + process attribution
- Connection state tracking (TCP states, byte counts) must be done in userspace on macOS since PKTAP gives raw packets, not connection events
- Consider shipping macOS as "monitoring mode" (less kernel integration) vs Linux as "full mode"
- bpf-linker compiles eBPF bytecode on macOS (cross-compilation works)
- But you cannot load/test eBPF programs on macOS -- need a Linux VM or remote machine
- Use Lima or OrbStack for local Linux VM testing on Apple Silicon
Version Compatibility
| Package | Compatible With | Notes |
|---|---|---|
| aya 0.13.x | tokio 1.x | Use features = ["async_tokio"] on aya |
| aya-ebpf 0.1.x | Rust nightly | Must use nightly toolchain with bpfel-unknown-none target |
| ratatui 0.30.x | crossterm 0.28.x+ | Ratatui re-exports crossterm types; check ratatui's Cargo.toml for exact pin |
| bpf-linker | LLVM 21+ | On macOS, brew install llvm and set LLVM_SYS_210_PREFIX |
| pcap 2.3.x | libpcap (system) | Pre-installed on macOS. On Linux, apt install libpcap-dev. |
| clap 4.6.x | serde 1.x | Optional serde integration for config file parsing |
| Rust nightly | aya-ebpf, bpf-linker | Only the eBPF crate needs nightly. Userspace crate can use stable Rust via workspace toolchain override. |
Key Architecture Decisions Driven by Stack
Confidence Assessment
| Component | Confidence | Rationale |
|---|---|---|
| Aya for eBPF | HIGH | Undisputed leader in Rust eBPF. Active development (0.13.x released recently). No viable alternative. |
| Ratatui for TUI | HIGH | Community standard since tui-rs deprecation. 0.30.x is mature. |
| clap for CLI | HIGH | De facto standard, v4.6 actively maintained. |
| Tokio async runtime | HIGH | Standard async runtime, LTS releases, Aya integration proven. |
| csv crate | HIGH | BurntSushi's csv is the only real option. Mature and stable. |
| pcap + PKTAP for macOS | MEDIUM | PKTAP approach is proven (RustNet uses it), but the pcap crate's PKTAP-specific support is less documented. May need raw header parsing. Needs validation during implementation. |
| bpf-linker on macOS | MEDIUM | Cross-compilation works per docs, but LLVM version pinning can be fragile. CI may need Linux runners for eBPF compilation. |
| Overall macOS parity | MEDIUM-LOW | macOS backend will inherently provide less data than eBPF (no kernel-level TCP state machine hooks). Feature parity is not fully achievable -- must design the abstraction to handle this gracefully. |
Sources
- Aya GitHub repository -- versions, feature set, project structure
- Aya crates.io -- v0.13.x latest release
- Aya documentation -- async features, map types
- Aya book -- development environment setup, bpf-linker installation
- Ratatui official site -- v0.30.x features, no_std support
- Ratatui GitHub -- latest release info
- clap crates.io -- v4.6.0 latest
- Tokio -- LTS release schedule (1.43 until March 2026, 1.47 until Sept 2026)
- csv crate -- v1.4.0 latest
- pcap crate GitHub -- v2.3.x, libpcap bindings
- PKTAP vs eBPF article -- macOS PKTAP approach, process attribution
- RustNet GitHub -- cross-platform network monitoring reference implementation
- Bandix GitHub -- Rust eBPF network monitoring reference
- eBPF with Rust using Aya (blog) -- macOS cross-compilation workflow
- How to Write eBPF Programs in Rust with Aya -- build pipeline details
Conventions
Conventions not yet established. Will populate as patterns emerge during development.
Architecture
Architecture not yet mapped. Follow existing patterns found in the codebase.
GSD Workflow Enforcement
Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync.
Use these entry points:
/gsd:quickfor small fixes, doc updates, and ad-hoc tasks/gsd:debugfor investigation and bug fixing/gsd:execute-phasefor planned phase work
Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it.
Developer Profile
Profile not yet configured. Run
/gsd:profile-userto generate your developer profile. This section is managed bygenerate-claude-profile-- do not edit manually.