[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-92553":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":17,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":22,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":34,"readmeContent":35,"aiSummary":36,"trendingCount":16,"starSnapshotCount":16,"syncStatus":15,"lastSyncTime":37,"discoverSource":38},92553,"usbtree","gnomeria\u002Fusbtree","gnomeria","Live USB device tree in your terminal. Rust TUI, no root, no libusb. Full activity metrics on Linux; device tree on macOS\u002FWindows.","https:\u002F\u002Fgnomeria.github.io\u002Fusbtree\u002F",null,"Rust",129,5,1,2,0,49,47.23,"MIT License",false,"main",true,[24,25,26,27,28,29,30,31,32,33],"cli","linux","macos","nusb","ratatui","rust","terminal","tui","usb","windows","2026-07-22 04:02:06","# usbtree\n\nCross-platform TUI for inspecting the USB device tree (Linux, macOS, Windows). Enumerates devices via [nusb](https:\u002F\u002Fcrates.io\u002Fcrates\u002Fnusb) — pure Rust, no root, no libusb.\n\n![usbtree](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Frust-ratatui-blue)\n[![license: MIT](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-green.svg)](LICENSE)\n[![ci](https:\u002F\u002Fgithub.com\u002Fgnomeria\u002Fusbtree\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg)](https:\u002F\u002Fgithub.com\u002Fgnomeria\u002Fusbtree\u002Factions\u002Fworkflows\u002Fci.yml)\n[![release](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fv\u002Frelease\u002Fgnomeria\u002Fusbtree)](https:\u002F\u002Fgithub.com\u002Fgnomeria\u002Fusbtree\u002Freleases\u002Flatest)\n\n**Website:** [gnomeria.github.io\u002Fusbtree](https:\u002F\u002Fgnomeria.github.io\u002Fusbtree)\n\n![usbtree demo](docs\u002Fscreenshots\u002Fdemo.gif)\n\n## Platform support\n\n| Feature                                              |     Linux     | macOS | Windows |\n| ---------------------------------------------------- | :-----------: | :---: | :-----: |\n| Device tree — hubs, classes, speeds, tree rails      |      ✅       |  ✅   |   ✅    |\n| Friendly names (`overrides.ids` + usb.ids)           |      ✅       |  ✅   |   ✅    |\n| Hot-plug watch + timestamped event log               |      ✅       |  ✅   |   ✅    |\n| Detail panel — sysfs path, vid:pid, serial, children |      ✅       |  ✅   |   ✅    |\n| Device power — advertised `bMaxPower`                |      ✅       |  ✅   |    —    |\n| Live activity sparklines — URBs\u002Fs (unprivileged)     |      ✅       |   —   |    —    |\n| Real bandwidth — bytes\u002Fs via usbmon (root)           |      ✅       |   —   |    —    |\n| Prebuilt binaries                                    | amd64 · arm64 | arm64 |  amd64  |\n\nLive per-device activity is Linux-only: macOS and Windows expose no unprivileged per-device traffic counter (`sudo` doesn't help), so the header reads `◌ activity n\u002Fa on this platform`. [Details →](#activity-metrics-linux)\n\n## Install\n\n> [!NOTE]\n> The shell installer and prebuilt binary links require a published GitHub release. If no release is available yet, install from source.\n\n### Shell script (Linux, macOS)\n\n```sh\ncurl -fsSL https:\u002F\u002Fraw.githubusercontent.com\u002Fgnomeria\u002Fusbtree\u002Fmain\u002Fscripts\u002Finstall.sh | sh\n```\n\nDownloads the latest release for your platform, verifies its sha256 against `checksums.txt`, and installs to `\u002Fusr\u002Flocal\u002Fbin` (if writable) or `~\u002F.local\u002Fbin`.\n\n### PowerShell script (Windows)\n\n```powershell\nirm https:\u002F\u002Fraw.githubusercontent.com\u002Fgnomeria\u002Fusbtree\u002Fmain\u002Fscripts\u002Finstall.ps1 | iex\n```\n\nDownloads the latest release, verifies its sha256 against `checksums.txt`, installs to `%LOCALAPPDATA%\\usbtree\\bin`, and adds that directory to your user `PATH`. The binaries are unsigned — see the note below if SmartScreen warns.\n\n### Installer environment variables\n\nBoth installers read the same variables (prefix with `$env:` in PowerShell):\n\n| Variable               | Effect                                                     |\n| ---------------------- | ---------------------------------------------------------- |\n| `USBTREE_VERSION`      | install a specific version, e.g. `0.0.1` (default: latest) |\n| `USBTREE_INSTALL_DIR`  | install directory override                                 |\n| `USBTREE_SUDO_SYMLINK` | Linux\u002FmacOS only — `1` → symlink into `\u002Fusr\u002Flocal\u002Fbin` (via `sudo`) so `sudo usbtree` works for usbmon bytes\u002Fs |\n\n### Prebuilt binaries\n\nGrab an archive from the [latest release](https:\u002F\u002Fgithub.com\u002Fgnomeria\u002Fusbtree\u002Freleases\u002Flatest):\n\n| Platform            | Asset                                   |\n| ------------------- | --------------------------------------- |\n| Linux x86_64        | `usbtree_\u003Cversion>_linux-amd64.tar.gz`  |\n| Linux arm64         | `usbtree_\u003Cversion>_linux-arm64.tar.gz`  |\n| macOS Apple Silicon | `usbtree_\u003Cversion>_darwin-arm64.tar.gz` |\n| Windows x86_64      | `usbtree_\u003Cversion>_windows-amd64.zip`   |\n\nEvery archive's sha256 is listed in the release's `checksums.txt`.\n\n> [!NOTE]\n> The macOS and Windows binaries are **not code-signed or notarized**.\n>\n> - **macOS**: Gatekeeper quarantines downloaded binaries. The install script clears the flag for you; if you download manually, run `xattr -d com.apple.quarantine .\u002Fusbtree` (or right-click → Open once).\n> - **Windows**: SmartScreen may show \"Windows protected your PC\" — click _More info_ → _Run anyway_, or unblock the file: `Unblock-File usbtree.exe` in PowerShell.\n>\n> If in doubt, verify the archive's sha256 against `checksums.txt`, or build from source.\n\n### From source\n\n```sh\ncargo install --git https:\u002F\u002Fgithub.com\u002Fgnomeria\u002Fusbtree   # or clone + cargo build --release\n```\n\n## Features\n\n- Live USB tree with a color-coded class gutter, per-class icons, and tree rails; rescans every second\n- Names resolved through a fallback chain: personal overrides → device descriptor strings → downloaded [usb.ids](http:\u002F\u002Fwww.linux-usb.org\u002Fusb-ids.html) (`--updatelist`) → embedded usb.ids snapshot → vendor + class heuristics\n- Collapse\u002Fexpand hubs with `Enter`\u002F`Space` (or `h`\u002F`l`) — collapsed nodes show `▸` and a `+N` child badge\n- Composite\u002FMisc (0xef) devices are classified by their interface classes, so e.g. a MOTU M2 audio interface shows as Audio, not Misc\n- Hot-plug watch: plugged devices flash green, unplugged devices linger as red crossed-out ghosts for 30 s, and every event lands in a timestamped log panel\n- Live per-device activity (Linux): inline sparklines in the tree plus a bandwidth graph in the detail panel — URBs\u002Fs unprivileged, real bytes\u002Fs when usbmon is readable (see below)\n- Speed badges with tier glyphs: `▂` low\u002Ffull, `▄` high (480M), `█` SuperSpeed+ (5G\u002F10G)\n- Detail panel: sysfs path, vid:pid, vendor, class, speed, max power (advertised `bMaxPower`), serial, connected children\n- `usbtree --dump` prints the tree once to stdout (no TUI)\n\n## Usage\n\n```sh\nusbtree                 # TUI\nusbtree --dump          # print the tree once and exit\nusbtree --updatelist    # download the latest usb.ids into the config dir\nusbtree --demo          # fake device tree with scripted hot-plug + traffic\n```\n\n`--demo` needs no USB access at all — it's what the screenshots are recorded from, and a quick way to preview the UI (combine with `--dump` for a one-shot fake tree).\n\n| Key                   | Action              |\n| --------------------- | ------------------- |\n| `j`\u002F`k`, arrows       | move selection      |\n| `Enter`\u002F`Space`       | collapse\u002Fexpand hub |\n| `h`\u002F`←`, `l`\u002F`→`      | fold \u002F unfold       |\n| `g`\u002F`Home`, `G`\u002F`End` | top \u002F bottom        |\n| `r`                   | force rescan        |\n| `q` \u002F `Esc`           | quit                |\n\n## Configuration\n\nConfig lives in `~\u002F.config\u002Fusbtree\u002F` (`%APPDATA%\\usbtree\\` on Windows):\n\n- **`overrides.ids`** — personal naming heuristics, one `vvvv:pppp Friendly Name` per line (`#` comments allowed). Wins over descriptor strings and usb.ids:\n\n  ```\n  07fd:000b MOTU M2 Audio Interface\n  ```\n\n- **`usb.ids`** — written by `usbtree --updatelist` (fetched from the [systemd\u002Fhwdata](https:\u002F\u002Fgithub.com\u002Fsystemd\u002Fhwdata) mirror). When present it takes priority over the usb.ids snapshot compiled into the binary.\n\n## Activity metrics (Linux)\n\nThe header shows which source is active:\n\n- **`◌ urb activity`** — unprivileged default: URB-count deltas from sysfs `urbnum`, shown as relative activity (URBs\u002Fs)\n- **`◉ usbmon bytes\u002Fs`** — real per-device bandwidth when `\u002Fsys\u002Fkernel\u002Fdebug\u002Fusb\u002Fusbmon` is readable, i.e. running as root with the `usbmon` module loaded and debugfs mounted\n\n### Enabling `usbmon` bytes\u002Fs\n\n`sudo` alone is **not enough** — root gives access, but the `usbmon` kernel module must be loaded or `\u002Fsys\u002Fkernel\u002Fdebug\u002Fusb\u002Fusbmon\u002F0u` won't exist and usbtree silently falls back to URB activity. Load it, then run:\n\n```sh\nsudo modprobe usbmon\nsudo \"$(command -v usbtree)\"\n```\n\nWhy `sudo \"$(command -v usbtree)\"` and not plain `sudo usbtree`? If you installed to `~\u002F.local\u002Fbin`, `sudo` won't find `usbtree` — it resolves commands against the restricted `secure_path` in `\u002Fetc\u002Fsudoers`, which excludes your home bin, so `sudo usbtree` gives `command not found`. Passing the absolute path (`command -v` prints it) sidesteps the lookup. Installing to `\u002Fusr\u002Flocal\u002Fbin` (the installer's default when writable) avoids this entirely, since that dir *is* in `secure_path`.\n\nCheck it's loaded with `lsmod | grep usbmon`. To load it automatically on every boot:\n\n```sh\necho usbmon | sudo tee \u002Fetc\u002Fmodules-load.d\u002Fusbmon.conf\n```\n\n`usbmon` is a standard in-tree module on virtually every distro; if `modprobe` fails, your kernel needs `CONFIG_USB_MON` (built-in on mainstream kernels). debugfs is mounted at `\u002Fsys\u002Fkernel\u002Fdebug` by default on systemd systems.\n\nOn macOS and Windows the tree, details, and hot-plug log all work; **live activity sparklines are not available**. The header reads `◌ activity n\u002Fa on this platform`. There is no unprivileged per-device traffic counter on those systems — `urbnum`\u002F`usbmon` are Linux-only, and `sudo` does not help. macOS IOKit only exposes an HID-specific report counter (keyboards\u002Fmice), which is too partial to be worth it, so per-device activity is **not implemented** there yet.\n\n## How it works\n\nThe tree rescans every second; hot-plug detection is a snapshot diff between scans. Device 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.\n\nReleases are automated with [release-please](https:\u002F\u002Fgithub.com\u002Fgoogleapis\u002Frelease-please): conventional commits on `main` roll up into a release PR, and merging it tags a version and builds the binaries above.\n\n## Development\n\nCommon commands live in the [Taskfile](https:\u002F\u002Ftaskfile.dev) — run `task -l` (or bare `task`) to list them: `task demo` runs the TUI on fake data, `task test` \u002F `task lint` \u002F `task ci` mirror what CI checks, `task shots` re-renders the screenshots, and `task hooks` enables the pre-commit secrets scan once per clone.\n\nContributions are welcome; see [CONTRIBUTING.md](CONTRIBUTING.md). Security reports should follow [SECURITY.md](SECURITY.md).\n\n## Screenshots pipeline\n\nThe demo GIF and PNG in `docs\u002Fscreenshots\u002F` are rendered headlessly — no real hardware — by driving `usbtree --demo` with [VHS](https:\u002F\u002Fgithub.com\u002Fcharmbracelet\u002Fvhs) tapes from `tapes\u002F`. The [Screenshots workflow](.github\u002Fworkflows\u002Fscreenshots.yml) re-renders and commits them whenever `src\u002F` or the tapes change on `main`; locally, run `scripts\u002Fshots.sh` (needs `vhs` installed).\n\n## License\n\nusbtree is licensed under the [MIT License](LICENSE).\n","usbtree 是一个跨平台终端 USB 设备树可视化工具，以 Rust 编写，无需 root 权限或 libusb 依赖。它基于纯 Rust 的 nusb 库枚举设备，支持 Linux、macOS 和 Windows，在终端中实时展示 USB 设备拓扑结构、厂商\u002F产品 ID、序列号、连接速度、供电信息及热插拔事件日志；Linux 平台额外提供免 root 的每设备活动指标（URB\u002Fs、带宽）。适用于系统调试、嵌入式开发、USB 设备排查及运维人员快速查看物理连接状态。","2026-07-09 02:30:21","CREATED_QUERY"]