
Live USB device tree in your terminal. Rust TUI, no root, no libusb. Full activity metrics on Linux;
Live USB device tree in your terminal. Rust TUI, no root, no libusb. Full activity metrics on Linux;
Cross-platform TUI for inspecting the USB device tree (Linux, macOS, Windows). Enumerates via nusb — pure Rust, no root, no libusb. One static binary, zero runtime deps: ~1.5 MB download, ~5–7 MB on disk.
Website: gnomeria.github.io/usbtree
| Feature | Linux | macOS | Windows |
|---|---|---|---|
| Device tree — hubs, classes, speeds, tree rails | ✅ | ✅ | ✅ |
Friendly names (overrides.ids + usb.ids) |
✅ | ✅ | ✅ |
| Hot-plug watch + timestamped event log | ✅ | ✅ | ✅ |
| Detail panel — sysfs path, vid:pid, serial, children | ✅ | ✅ | ✅ |
Device power — advertised bMaxPower |
✅ | ✅ | — |
| Live activity sparklines — URBs/s (unprivileged) | ✅ | — | — |
| Real bandwidth — bytes/s via usbmon (root) | ✅ | — | — |
| Prebuilt binaries | amd64 · arm64 | arm64 | amd64 |
Live per-device activity is Linux-only — why →.
[!NOTE] The shell installer and prebuilt links need a published GitHub release. None yet? Install from source.
Homebrew (Linux · macOS Apple Silicon)
brew install usbtree
Linux / macOS (shell installer)
curl -fsSL https://raw.githubusercontent.com/gnomeria/usbtree/main/scripts/install.sh | sh
Windows
irm https://raw.githubusercontent.com/gnomeria/usbtree/main/scripts/install.ps1 | iex
From source
cargo install --git https://github.com/gnomeria/usbtree
Installers verify the archive's sha256 against checksums.txt and install to /usr/local/bin or ~/.local/bin (Linux/macOS) / %LOCALAPPDATA%\\usbtree\bin + user PATH (Windows). Env vars (prefix $env: in PowerShell):
| Variable | Effect |
|---|---|
USBTREE_VERSION |
pin a version, e.g. 0.0.1 (default: latest) |
USBTREE_INSTALL_DIR |
install directory override |
USBTREE_SUDO_SYMLINK |
1 also symlinks into /usr/local/bin via sudo, so sudo usbtree finds it for usbmon bytes/s |
Or grab a usbtree__-.{tar.gz,zip} archive from the latest release; each sha256 is in checksums.txt.
[!NOTE] macOS and Windows binaries are not code-signed or notarized.
- macOS: the install script and Homebrew formula clear the quarantine flag; manual downloads need
xattr -d com.apple.quarantine ./usbtree(or right-click → Open once).- Windows: SmartScreen may warn — More info → Run anyway, or
Unblock-File usbtree.exe. Verify the sha256 or build from source if in doubt.
▂ low/full, ▄ high 480M, █ SuperSpeed+ 5G/10G); rescans every second. Collapse hubs with Enter/Space/h/l → ▸ + +N child badgeoverrides.ids → descriptor strings → downloaded usb.ids (--updatelist) → embedded snapshot → vendor/class heuristicsbMaxPower, serial, connected childrene on a mass-storage device unmounts + cuts port power via udisks2, with a confirm dialogp) — flat address-sorted PCI list with detail pane (prog-if, subsystem, link speed/width, NUMA, IOMMU group, power state)/), yank (y vid:pid, Y full details), --dump prints the tree once (no TUI)usbtree # TUI
usbtree --dump # print the tree once and exit
usbtree --updatelist # download the latest usb.ids into the config dir
usbtree --upgrade # update to the latest release (re-runs the installer)
usbtree --version # print the version
usbtree --help # list all flags (also -h)
usbtree --demo # fake tree with scripted hot-plug + traffic (no hardware)
usbtree --light # use light theme (for light background terminals)
--upgrade re-runs the official installer for your platform, so it honours the
same env vars — e.g. USBTREE_VERSION=0.1.0 usbtree --upgrade pins a version,
USBTREE_INSTALL_DIR picks the target dir. Homebrew installs: use brew upgrade usbtree instead.
| Key | Action |
|---|---|
j/k, arrows |
move selection |
Enter/Space |
collapse/expand hub |
h/←, l/→ |
fold / unfold |
g/Home, G/End |
top / bottom |
/ |
filter tree |
Tab |
focus tree / events |
y / Y |
yank id / details |
e |
safe-eject storage |
p |
toggle USB / PCI |
r |
force rescan |
t |
toggle theme |
q / Esc |
quit |
Config lives in ~/.config/usbtree/ (%APPDATA%\\usbtree\ on Windows):
overrides.ids — personal names, one vvvv:pppp Friendly Name per line (# comments OK). Wins over descriptor strings and usb.ids:
07fd:000b MOTU M2 Audio Interface
usb.ids — written by usbtree --updatelist (from the systemd/hwdata mirror). Takes priority over the snapshot compiled into the binary.
The header shows the active source:
◌ urb activity — unprivileged default: URB-count deltas from sysfs urbnum, shown as URBs/s◉ usbmon bytes/s — real per-device bandwidth when /sys/kernel/debug/usb/usbmon is readable (root + usbmon module loaded)sudo alone is not enough — the usbmon module must be loaded or usbtree silently falls back to URB activity:
sudo modprobe usbmon
sudo "$(command -v usbtree)"
The absolute path matters: sudo's secure_path excludes ~/.local/bin, so plain sudo usbtree fails unless installed to /usr/local/bin. Load usbmon at boot with echo usbmon | sudo tee /etc/modules-load.d/usbmon.conf; needs CONFIG_USB_MON (standard on mainstream kernels).
macOS/Windows have no unprivileged per-device traffic counter (sudo doesn't help), so live activity isn't implemented there — the header reads ◌ activity n/a on this platform.
Rescans every second; hot-plug detection is a snapshot diff between scans. Paths use sysfs-style naming (1-1.4 = bus 1, port 1, port 4) on every platform, built from each device's port chain; root hubs are synthesized from the bus list.
Releases are automated with release-please: conventional commits on main roll into a release PR; merging it tags a version and builds the binaries.
Common commands live in the Taskfile — task -l lists them (task demo, task test, task lint, task ci, task shots, task hooks). The demo GIF/PNG in docs/screenshots/ are rendered headlessly by driving usbtree --demo with VHS tapes from tapes/, re-committed by the Screenshots workflow when src/ or the tapes change.
Contributions welcome — see CONTRIBUTING.md; security reports follow SECURITY.md.
MIT.
No open issues yet, or sync has not completed.