# usbtree: the USB tree in one static binary, and a metrics column that lies politely

> usbtree is a terminal interface for the USB device tree written in Rust, shipping as a single static binary of about 1.5MB with five direct dependencies, no root and no libusb, and it is refreshingly explicit that real bandwidth numbers exist only on Linux with root and a kernel module loaded. The other half of the project is the unglamorous part: a five-step name resolution chain, an eject that cuts port power, unsigned binaries whose installer clears the quarantine flag, and a demo mode so you can try it with no hardware at all.

**gnomeria/usbtree** — Live USB device tree in your terminal. Rust TUI, no root, no libusb. Full activity metrics on Linux; device tree on macOS/Windows.

- Repository: https://github.com/gnomeria/usbtree
- Website: https://gnomeria.github.io/usbtree/
- Stars: 684 · Forks: 14
- Language: Rust
- License: MIT
- Published: 2026-09-17 · Updated: 2026-09-17 · Language: en
- Canonical page: https://hysenlabs.com/projects/gnomeria-usbtree

## Five dependencies, one binary, and a snapshot with a date in its version

The size claim is the whole pitch: one static binary, roughly one and a half megabytes downloaded and five to seven megabytes on disk, with no runtime dependencies at all. The manifest backs it up. There are five direct dependencies and no more: a pure-Rust USB enumeration library, two crates for reading PCI configuration, a terminal user interface framework, and a crate that ships the USB identifier database. The enumeration choice is the portability decision. The traditional way to enumerate USB from userspace is a C library, which means a system library, per-distribution builds, and a udev rule file on Linux; a pure-Rust path means the same binary works on three operating systems with nothing to install first, and it is why the readme can claim no root and no libusb in the same breath. Two smaller details in the manifest are worth noticing. The terminal framework and the Rust edition both point at current versions, so this is not a project held back by its toolchain. And the identifier database crate carries a date in its version number, which means you can read the vintage of the name snapshot compiled into your binary without opening the file. That matters more than it sounds, because device names change and a snapshot from last year will confidently mislabel a device released this spring.

## The support table separates the tree from the telemetry

Most cross-platform tools present a single list of features and let you discover the gaps when something is missing. This one publishes a matrix, and the shape of the matrix is the design. The device tree itself, the friendly names, the hot-plug watch with a timestamped event log, and the detail panel with the system path, the identifier pair, the serial and the child count all work on Linux, macOS and Windows. Device power, read from what the device advertises as its maximum draw, works on Linux and macOS but not Windows, which is a kernel-interface difference rather than a missing feature nobody wrote. Then the two rows that matter for anyone evaluating this: live activity sparklines, computed from request counts without privileges, are Linux only, and real bandwidth in bytes per second, which needs root, is Linux only as well. The readme links the matrix to a section explaining why, which is the honest way to present a feature that is the main draw on exactly one platform. The prebuilt binaries row has its own gaps: Linux for both architectures, Windows for one, and macOS for Apple Silicon only, so an Intel Mac builds from source like everyone else.

## Five fallbacks decide what a device is called

A device tree that shows two hundred rows of hexadecimal identifiers is technically correct and practically useless, so the name resolution is the feature. It is a five-step chain, and the order is the design. First, your own overrides file, which wins over everything and holds one line per device in the form of an identifier pair followed by a name. Second, the descriptor strings the device reports about itself, which are often absent and frequently useless. Third, the identifier database downloaded on demand from the same mirror the systemd hardware database project uses. Fourth, the snapshot of that database compiled into the binary, which is where most installs end up. Fifth, heuristics based on vendor and class identifiers, which is what produces a guess rather than a name. The chain never bottoms out in a blank cell, which is the right behaviour for a diagnostic tool: a wrong name is still more useful than nothing when you are trying to identify an unknown device. The downloaded database takes priority over the compiled snapshot, which is why there is a flag to fetch it and why the snapshot's version date matters. One more classification detail that fixes the most common complaint about tools of this kind: composite devices, which report a generic class, are reclassified by looking at their interfaces, so a particular audio interface appears as audio rather than as miscellaneous hardware.

## Safe eject cuts power to the port, not just the mount

Press the eject key on a mass-storage device and the tool unmounts it and then cuts power to the port through a desktop service, behind a confirmation dialog. That second half is the interesting half. Unmounting is what a conventional eject does, and unmounting leaves the device powered and attached, which is exactly the state that produces a busy-resource error the next time you try, or a drive that never reappears until you unplug it. Cutting port power is what the hardware supports and what the desktop's device service exposes, so the tool is doing the part that actually fixes the problem, and it does it without root on Linux. The confirmation dialog is the right call for a keyboard shortcut inside a live display: one stray keypress should not unmount a drive you were writing to. It is also the reason this feature is marked Linux-only in the support table, since the desktop service that performs it is the Linux one. For everyone else the same key does nothing, which is the pattern the project follows everywhere else too: a feature that cannot work hides itself rather than half-working.

## Two bandwidth numbers, and the wrong one still animates

The activity display is the feature people want, and the honest version of it needs root on Linux and nothing else on any platform. The header always names the source it is using, which is the correct design for a number that changes meaning depending on privileges. The unprivileged default counts request-block deltas read from the kernel's sysfs interface and shows them as a rate; that measures how busy a device is, not how fast it moves data. The real figure reads the kernel's USB monitor, which reports actual bytes, and it needs root plus the monitor module loaded. Here is the trap the readme spells out: running under sudo is not sufficient. If the module is not loaded, the tool silently falls back to request counts, so the sparkline keeps moving, the header keeps updating, and the number you are looking at is a different quantity from the one you asked for. Nothing crashes and nothing warns you. The fix is two commands, and the second one has a subtlety worth internalising. ```sh
brew install usbtree
``` The absolute path is not incidental: the elevated shell resets its own search path for security, so a binary installed in a per-user directory is invisible to it, and the command has to ask the shell where the binary actually is before elevating. That single requirement is why the installer has an option to also link the binary into a system directory.

## Unsigned builds, and the installer exists partly to silence the warning

The macOS and Windows binaries are neither code-signed nor notarised, and the readme devotes a callout to what that means on each platform. On macOS the quarantine attribute attaches to anything downloaded, so the operating system refuses to run it until the attribute is cleared; the shell installer and the Homebrew formula do that for you, and a manual download needs you to remove the attribute yourself or open the file once through the finder. On Windows the equivalent is the SmartScreen interstitial, which you can dismiss or unblock with a single command. The readme's own advice in both cases is to verify the digest or build from source if you are unsure. That is where the checksum story lands. The installers verify the downloaded archive against a published list of digests before unpacking it, and every release archive is named by version, operating system and architecture so you can pick one deliberately. So there are two paths with different trust properties: use an installer and you get verification plus the quarantine workaround, or download by hand and you own both. For a tool whose entire pitch is that it needs no privileges, moving the friction from permissions to trust is the right trade.

## A demo mode with scripted traffic, so the tool can be tried with nothing plugged in

There is a flag that builds a fake device tree with scripted hot-plug events and scripted traffic, and it needs no hardware whatsoever. That is more useful than it sounds. It means the interface can be demonstrated to someone who has no USB devices, screenshotted for documentation, and exercised in continuous integration, where the tree layout, the sparkline rendering and the key handling can be asserted without a physical bus. The repository carries a directory of terminal recordings alongside it, which is the other half of that story: a scripted demo is exactly what a recorded demonstration needs, since the same script produces the same tree every time. The non-interactive path is a separate flag that prints the tree once and exits, which is what you want in a script or a health check, and the rest of the interaction is keyboard-only: a live filter, copying an identifier or a full detail block to the clipboard, and a key that toggles between the USB tree and a PCI view. That second view is a flat list sorted by bus address with the programming interface, the subsystem identifier, the negotiated link speed and width, the NUMA node, the IOMMU group and the power state, which is why two of the five dependencies are PCI crates. A hardware inventory tool that also shows you the topology it is plugged into is more useful than one that stops at the USB root hub.

## Three tags in a fortnight, a stale install note, and packaging that excludes its own tooling

The release history is short and fast: three tags inside about two weeks in July 2026, ending at version one point one, and the last commit on record is dated 20 August 2026. Version bumps and changelog entries are automated, with a release manifest, a release configuration and a changelog file in the tree, so the version history comes from commit messages rather than from a person deciding to cut a release. That explains both the speed and one thing that looks like an oversight. The readme still carries a note warning that the shell installer and the prebuilt links need a published release, and telling the reader that if there is none they should install from source. With three tags published, that note is stale, and the fact that it survived is a small illustration of how documentation ages in a project where the releases are cut by a bot. The packaging is more deliberate than the documentation. The manifest excludes the agent-facing directories and the agent instruction file from the published crate, which tells you the author thought about what ships and what is tooling for working on the project. A task runner file and a directory of git hooks sit at the top level for the same reason. For a user, the practical consequence of all this is the upgrade flag: it re-runs the official installer for your platform, so it honours the same environment variables, which is how you pin a version instead of taking the newest one.

## Conclusion

usbtree fits you if you have ever lost twenty minutes to a device that would not enumerate and you want the answer on screen rather than in a forum thread, because the tree, the detail panel and the hot-plug log answer most of those questions in one second rescan, and the tool asks for no privileges to do it. It does not fit if you need real throughput numbers on macOS or Windows, because that column in the support table is empty on purpose and no amount of configuration will fill it. Three things to check before you rely on it. Verify the checksum if you install manually, since the macOS and Windows builds are neither signed nor notarised and the installer exists partly to clear that warning for you. Keep the active metric source in view, because a run under sudo without the kernel module loaded will show request counts rather than bytes and will not tell you it has downgraded. And pin a version if you script against it, since three tags landed inside a fortnight and the upgrade flag will otherwise pull the newest release.

## FAQ

### Does usbtree need root or extra libraries?

Neither. It enumerates through a pure-Rust library with no libusb involved, and the default activity metric runs unprivileged. Root is needed only for real byte counts on Linux, which additionally requires the kernel USB monitor module to be loaded, and running under sudo without that module silently falls back to request counts.

### How do I install usbtree?

Through Homebrew on Linux and Apple Silicon macOS, a piped shell installer on Linux and macOS, a one-line PowerShell installer on Windows, or cargo install from the git repository. The installers verify the archive against a published checksum list and install into a per-user binary directory by default, with an environment variable to change that.

### Why does macOS or Windows warn me about the usbtree binary?

The macOS and Windows builds are not code-signed or notarised, so the quarantine attribute and the SmartScreen prompt both object. The installers clear that on macOS and the Homebrew formula does it too; a manual download needs you to remove the attribute or open the file once, or to unblock the executable.

### How do I give a USB device a friendly name in usbtree?

Add a line to the overrides file in the configuration directory, giving the identifier pair and the name you want. That file beats both the strings a device reports about itself and the downloaded identifier database, and comments are allowed.

### What does the usbtree demo mode do?

It shows a fake device tree with scripted hot-plug events and traffic, so you can try the interface with no hardware attached and record it deterministically. The tree can also be printed once with no interface at all, which is the form you want in a script.

### How do I keep usbtree on a fixed version?

Set the version environment variable before running the installer, or before the upgrade flag, since the upgrade re-runs the same installer and honours the same variables. There is also an environment variable for the install directory. If you installed through Homebrew, upgrade through Homebrew instead.

## Sources

- [gnomeria/usbtree on GitHub](https://github.com/gnomeria/usbtree)
- [License: MIT](https://github.com/gnomeria/usbtree/blob/main/LICENSE)
- [Project website](https://gnomeria.github.io/usbtree/)
- [README](https://github.com/gnomeria/usbtree/blob/main/README.md)
- [Releases](https://github.com/gnomeria/usbtree/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/gnomeria-usbtree
