tinytap

module
v0.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 15, 2026 License: MIT

README

tinytap

Test codecov Go Reference OpenSSF Scorecard OpenSSF Best Practices

A tiny eBPF-based HTTP traffic capture tool for local development.

An ordinary app calls an API over HTTPS; tinytap's TUI (output = "tui" in the config file, see Configuration) shows the exact bytes that app sent: the request line, every header including Authorization: Bearer, and the decoded JSON body, in plaintext, with no proxy and no CA certificate installed. In the request table j/k scroll and Enter opens the detail panel (b toggles the hex body view). See scripts/demo/ for how the gif is recorded.

Quick start

Install it with:

curl -fsSL https://raw.githubusercontent.com/shinagawa-web/tinytap/main/scripts/install.sh | sh

Grant it the capabilities it needs, then run it, no full root required:

sudo setcap cap_dac_read_search,cap_perfmon,cap_bpf,cap_sys_admin,cap_syslog=eip $(command -v tinytap)
tinytap

That's the full set: plaintext HTTP and HTTPS (via the libssl uprobes, cap_sys_admin) both work out of the box. See Running Without Full Root on the docs site if you want the smaller plaintext-only set instead.

With no config file, that opens the TUI shown at the top of this README: j/k to scroll, Enter for the detail panel, q or Ctrl-C to quit, as long as your terminal is at least 120x24. In a smaller or non-interactive terminal it prints guidance and exits instead of silently streaming; see Configuration to switch to the JSONL stdout mode.

Didn't work? Run tinytap doctor first for a read-only preflight report (kernel version, capabilities, libssl execute bit, etc.); see Troubleshooting.

Linux amd64/arm64 only. On macOS/Windows, see Where tinytap Runs. Want a specific version, or to verify a release download? See the docs site below.

Where tinytap Runs

tinytap requires a Linux kernel. It cannot run natively on macOS or Windows, because eBPF is a Linux kernel technology. But that's less restrictive than it sounds, because Linux kernels are everywhere:

Where the user works How tinytap runs there
Linux (desktop, laptop, workstation, or server) Native. Just run the binary.
Mac (Intel or Apple Silicon) Inside a Linux VM: Docker Desktop's VM, OrbStack, Lima, UTM, Multipass, etc.
Windows Inside WSL2 (which is a real Linux kernel).

Which Linux distributions are supported? Ubuntu 22.04+, Fedora 43, Debian 12, AlmaLinux 9, and Alpine 3.23 all pass. Two exceptions worth knowing:

  • Ubuntu 20.04: the GA kernel (5.4.0) is too old and fails at startup. The HWE kernel (5.15+) works.
  • RHEL-family distros (AlmaLinux 9, Rocky Linux 9): they report kernel versions like "5.14" but backport the required BPF features, so they work despite the low version number.

The full tested matrix (distro, kernel, plaintext/TLS/sendfile status, capability requirements) is on the Platform Support page.

Containers need no special handling either, since eBPF sees every process on the host kernel, containerized or not, so tinytap doesn't need to run inside a container or as a sidecar. See Where tinytap Runs on the docs site for the full container story.

What it does today

tinytap attaches eBPF probes to a process's socket syscalls (accept4/read/write/close/recvfrom/sendto/recvmsg/sendmsg), parses the payload bytes as HTTP/1.1, pairs each request with its response, and renders the exchange live, either in the terminal TUI above or as JSONL (one JSON object per exchange) on stdout:

{"reqTsNs":1754887677005000000,"latencyNs":300000,"pid":27122,"comm":"python3","method":"GET","path":"/","status":200,"resBytes":1304,"abandoned":false,...}

output = "auto" (the default) picks the TUI when stdout/stdin are an interactive terminal of at least 120x24; otherwise it prints guidance and exits rather than silently streaming; the JSONL stream is opt-in via output = "stdout". output = "tui" forces the TUI (and exits the same way if the terminal can't host it). --version prints the build's version, commit, and date, and exits without needing root.

Configuration

Session settings (output) live in a TOML config file, not CLI flags. tinytap config init writes one, fully populated with defaults, so tinytap config init && tinytap just works:

tinytap config init          # writes ./tinytap.toml
tinytap config init path/to/config.toml   # or a specific path
tinytap config init --force  # overwrite an existing file

Search order when --config <path> isn't given: ./tinytap.toml, then $XDG_CONFIG_HOME/tinytap/config.toml (falling back to ~/.config/tinytap/config.toml); finding neither is not an error, the defaults below apply.

output = "auto"   # auto | stdout | tui

The only CLI surface is one-shot actions, not session settings: --config <path> (point at an alternate config file), --version (build metadata, exits before any eBPF load), config init (above), and doctor (see Troubleshooting).

Current limitations

HTTP/1.1 only (no HTTP/2/gRPC yet), single-host only, and TLS capture needs the process to expose OpenSSL symbols, either a dynamically linked libssl.so or an unstripped static build. Stacks that don't use OpenSSL at all, like Go's crypto/tls, remain invisible either way. Full list, including per-server body-visibility details, on the docs site.

Status & Roadmap

Released so far: v0.1.0 (HTTP request/response visible) through v0.6.0 (production readiness). v0.7.0 (real-hardware bring-up) is in progress, see #198. Full roadmap in #19.

Documentation

Full documentation is available at shinagawa-web.github.io/tinytap:

Toolchain

Component Choice Why
eBPF lib github.com/cilium/ebpf Pure Go, modern, standard for new projects
Build bpf2go (part of cilium/ebpf) Generates Go bindings from C code
Compiler clang 17+ Standard for eBPF, supports BTF. clang-14 compiles cleanly but the emitted bpf_probe_read_user call fails the kernel verifier (R2 unbounded memory access), so CI pins 17 (#207); 15/16 untested
Go 1.24+
Kernel Linux 5.8+ Required for BPF_MAP_TYPE_RINGBUF, tinytap's event transport. See Platform Support for tested distros
Architecture amd64 + arm64 Need arm64 for Apple Silicon Lima VM
Release builds GoReleaser v2 Cross-compiles linux/amd64 + linux/arm64 on tag push (.goreleaser.yml). The release workflow regenerates the bpf2go artifacts from source first (#260), then it's a plain go build; GoReleaser itself needs no clang/libbpf step
Dev environment

Mac (Apple Silicon) + Lima with Ubuntu 24.04. Build and run inside the Lima VM. Edit code on Mac via VS Code's remote SSH or the auto-mounted filesystem. This is private to the maintainer's workflow; it does not constrain users. See Where tinytap Runs for how tinytap runs on a user's machine.

Setup commands are:

# Mac side
brew install lima
limactl start --name=tinytap template://ubuntu
limactl shell tinytap

# Inside the VM
sudo apt update
sudo apt install -y llvm linux-headers-$(uname -r) build-essential git pkg-config

# clang 17 from the official LLVM repo — Ubuntu's own `clang` package isn't
# guaranteed to be 17, and clang-14 is known to fail the eBPF verifier (#207)
wget -O /tmp/llvm.sh https://apt.llvm.org/llvm.sh
echo "9474ecd78b52aba6e923976b1e9773f5613027cc7e237b9956986cb536e02a36  /tmp/llvm.sh" | sha256sum -c -
chmod +x /tmp/llvm.sh
sudo /tmp/llvm.sh 17

# libbpf 1.6.2 headers — Ubuntu's libbpf-dev predates the BPF_UPROBE macro
# bpf/tinytap_uprobe.bpf.c uses
git clone --depth 1 --branch v1.6.2 https://github.com/libbpf/libbpf.git /tmp/libbpf
sudo make -C /tmp/libbpf/src install_headers PREFIX=/usr

# Go (apt version is old)
GO_VERSION=1.24.0
ARCH=$(dpkg --print-architecture)  # arm64 on Apple Silicon
wget https://go.dev/dl/go${GO_VERSION}.linux-${ARCH}.tar.gz
sudo tar -C /usr/local -xzf go${GO_VERSION}.linux-${ARCH}.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc

Building from source

Build and run inside the Lima VM (see Toolchain above for setup):

# Regenerate Go bindings from C — required before every build/test, not just
# after editing bpf/*.c: the generated files aren't committed (needs clang-17
# + libbpf 1.6.2, see Toolchain above)
cd ~/tinytap && make generate

# Build (go build ./... alone won't write a ./tinytap binary — it builds
# every package in the module to check they compile, without picking one
# to write out; -o plus a single package path produces the binary)
go build -o tinytap ./cmd/tinytap

# Run (requires root — eBPF needs CAP_BPF/CAP_PERFMON or root)
sudo ./tinytap

Root isn't actually required. See Running Without Full Root for the minimal setcap invocation.

Or via make:

make run       # orchestrated smoke test: starts a demo HTTP server, fires a request, shows the capture
make run-raw   # build + run with output = "stdout" against whatever's already running

Run make install once per checkout (or worktree) to install the pre-push hook that runs lint, tests, and coverage checks before every push.

License

MIT, see LICENSE. Exception: bpf/vmlinux.h is generated from the Linux kernel's BTF info and is distributed under the kernel's GPL-2.0 license instead.

References

Directories

Path Synopsis
cmd
tinytap command
internal
loader/bpf
Package bpf holds the bpf2go-generated bindings for the kernel-side program in bpf/tinytap.bpf.c.
Package bpf holds the bpf2go-generated bindings for the kernel-side program in bpf/tinytap.bpf.c.
loader/bpf/fixture
Package fixture holds the bpf2go-generated bindings for the integration-test fixture program in fixture.bpf.c.
Package fixture holds the bpf2go-generated bindings for the integration-test fixture program in fixture.bpf.c.
tls

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL