Notice: This is an AI-engineered project developed using the agentic engineering flows defined in mattpocock/skills.
Secure P2P USB-over-IP. Attach a remote physical USB device as a native local USB device over an encrypted Iroh connection, without port forwarding, a VPN, custom hardware, or project-owned drivers.
| Operating System | Host Role (Share) | Client Role (Attach) | Notes |
|---|---|---|---|
| Linux | ✅ Supported | ✅ Supported | Uses libusb on the Host and the kernel vhci-hcd controller on the Client. |
| macOS | ✅ Supported | ❌ Not supported | Host capture works as root; macOS has no reusable virtual USB Client driver. |
| Windows | ✅ Supported | ✅ Supported | Uses usbipd-win on the Host and WHLK-certified usbip-win2 on the Client. |
Note
iroh-usbip detects these prerequisites. When one is missing, the command explains the exact change, asks for consent, and uses the operating system's administrator prompt to install or enable it. Downloads are version-pinned and verified before execution. Run iroh-usbip doctor to inspect both roles without changing the system.
Install the latest pre-compiled binary via our installer script:
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/seandlg/iroh-usbip/releases/latest/download/iroh-usbip-installer.sh | sh(For advanced options like Nix, Windows support, or building from source, see Advanced Installation below).
iroh-usbip facilitates secure peer-to-peer sharing of USB devices directly over the internet without VPNs or port forwarding.
sequenceDiagram
participant Host as Host (Physical USB Device)
participant Iroh as Iroh P2P Bridge (Encrypted)
participant Client as Client (Virtual USB Mount)
Host->>Iroh: 1. iroh-usbip share (generates single-use ticket)
Client->>Iroh: 2. iroh-usbip attach <ticket>
Iroh->>Host: P2P Connection established
Client->>Client: Attaches through the native virtual USB controller
Note over Host,Client: Standard USB/IP packets stay inside the encrypted bridge
- Check the machine and list USB devices on the Host:
iroh-usbip doctor iroh-usbip list
- Share exactly one device. Bus ID is preferred because VID/PID pairs need not be unique:
iroh-usbip share --bus-id <BUS_ID>
- Attach it on the Client with the generated ticket:
iroh-usbip attach <TICKET>
On Linux and macOS, share/attach offer to relaunch through sudo when native USB access requires it. On Windows, missing drivers are offered through winget or a verified installer followed by the normal UAC dialog. Use --yes only for automation where accepting these changes is intentional.
Note: Pressing Ctrl+C in either terminal cleanly terminates the P2P session, detaches the virtual device from the Client, and restores the original drivers on the Host.
No Zadig or per-device WinUSB replacement is used:
- Host:
usbipd-winowns native device capture.iroh-usbipcan install it withwinget, bind the selected device, and undo a newly-created binding at teardown. - Client:
usbip-win2provides the signed virtual controller.iroh-usbippins the installer version and SHA-256, validates its Authenticode signature, and requests UAC before running it. - Security:
usbipd-winnormally creates a LAN-accessible firewall rule. Before binding,iroh-usbiptemporarily disables that rule so devices are reachable only through the local Iroh Bridge, then restores the prior firewall state during teardown. Any other pre-shared or policy-allowed device causes sharing to fail closed because the service cannot filter its USB/IP device list per connection.
The iroh-usbip binary itself can be installed with the release installer:
```powershell
irm https://github.com/seandlg/iroh-usbip/releases/latest/download/iroh-usbip-installer.ps1 | iex
```
If you run Nix, you can install or run iroh-usbip directly:
- Run without installing:
nix run github:seandlg/iroh-usbip -- <args>
- Install to your Nix profile:
nix profile install github:seandlg/iroh-usbip
To compile from source, you only need the standard Rust toolchain (2024 edition) and a C compiler (like gcc, clang, or MSVC build tools). The underlying libusb-1.0 library is vendored and compiled statically automatically.
Install the binary directly from our GitHub repository:
cargo install --git https://github.com/seandlg/iroh-usbipThis section is for developers contributing to iroh-usbip.
To keep this project highly maintainable and avoid documentation drift, we separate system concerns:
- Domain Model & Vocabulary: See CONTEXT.md for terminology (Host, Client, Physical Device, Virtual Device, Bridge).
- Architectural Decision Records (ADRs): See docs/adr/ for design histories, including driver detachment and user-space limitations.
- Product Requirements: See docs/prd.md for scoping, goals, and non-goals.
- Agent and Triage Guidelines: See AGENTS.md and docs/agents/ for workspace labels and CLI issue tracker patterns.
To ensure a reproducible environment, this project uses Nix (specifically the Lix implementation with the Crane packaging library) to manage hermetic dependencies, build environments, and cached builds.
- Nix: Install Nix and enable Flakes.
- direnv (Optional): Automatically load the Nix environment when you enter the directory.
If you are not using Nix, you must manually install:
- Rust toolchain (2024 edition)
libusb-1.0dev librarypkg-configjust(task runner)git-cliff(changelog generator)gh(GitHub CLI)python3
Enter the reproducible shell containing all toolchains, libraries (libusb1, pkg-config), and helper CLIs:
nix develop(Alternatively, configure direnv with use flake to load the environment automatically)
- Idiomatic Cargo Flow: Once inside
nix develop, run standard Cargo commands directly (e.g.cargo build,cargo test). Spawning nested Nix subshells (likenix develop --command cargo) is avoided for local runs to keep feedback loops fast and IDE integrations (like Rust-Analyzer) working flawlessly. - Task Runner (
just): We reserve thejustfilepurely for complex, multi-step orchestrations or privilege transitions (e.g. running kernel integration tests and release pipelines).
To test your local code modifications, you will compile the binary in user-space and run it as root.
Because the root user does not inherit the Nix shell's environment paths, running standard sudo target/debug/iroh-usbip will fail due to missing dependencies.
Use the sudo -E env PATH="$PATH" prefix to preserve the development environment:
- Share local build:
sudo -E env PATH="$PATH" ./target/debug/iroh-usbip share --vid <VID> --pid <PID>
- Attach local build:
sudo -E env PATH="$PATH" ./target/debug/iroh-usbip attach <TICKET>
We separate testing into two distinct environments and privilege scopes:
These tests run without any physical USB hardware or host-level kernel permissions. They use in-memory stream mocks and mock devices.
- Run clippy and format checks:
cargo fmt --all --check
cargo clippy --all-targets -- --deny warnings
- Run all hermetic unit and mock integration tests:
cargo test - Nix hermetic check (runs clippy, formatting, and unit tests inside a sandboxed build derivation):
nix flake check
The Linux native test verifies a real USB gadget, kernel VHCI attachment, Iroh connection, and teardown. Because it loads kernel modules (vhci-hcd, dummy-hcd, libcomposite) and configures configfs/sysfs, it must run natively with root privileges.
- Run the E2E integration test:
Note: The runner automatically compiles the binary as the normal user first, then runs
just test-e2e
scripts/e2e.shusingsudo -E env PATH="$PATH"to preserve the Nix-provided dependencies. - Run the E2E test in Mock Mode (does not require root or Linux VHCI):
just test-e2e --mock
Our GitHub Actions pipeline (defined in .github/workflows/ci.yml) runs on every commit/PR:
- Nix Setup: Installs Nix/Lix and configures Magic Nix Cache for incremental store caching.
- Hermetic Checks: Executes
nix flake checkto verify formatting, clippy, and unit tests inside the sandbox. - E2E Testing: Runs the unsandboxed full mock lifecycle using
nix develop --command scripts/e2e.sh --mock. - Native compilation: Compiles and runs all portable tests on current macOS and Windows runners so platform adapters cannot silently rot.
We use a double-gated, mistake-proof release workflow built around cargo-dist and git-cliff. All release actions must be run inside nix develop.
-
Prepare Release (from a clean
mainbranch insidenix develop): Determine the next version according to Semantic Versioning (SemVer) rules:- Patch (
0.1.x): For bug fixes, refactorings, chores, and internal improvements. - Minor (
0.x.0): For new features (e.g. support for a new command). - Major (
x.0.0): For breaking changes.
Run the task runner recipe to prepare the release:
just prepare-release <version>
Gate 1 (Poka-Yoke): This will fail if the latest commit on
mainhas not passed GitHub Actions CI. If green, it creates arelease/v<version>branch, bumps the version inCargo.toml, updatesCHANGELOG.mdviagit-cliff, and commits the changes. - Patch (
-
Submit PR & Merge: Push the
release/v<version>branch to GitHub, open a PR, and merge it tomainonce PR checks (including E2E checks) succeed. -
Tag and Publish (inside
nix develop): Pull the merged commit locally onmainand run:just tag-release
Gate 2 (Poka-Yoke): This will fail if the post-merge CI on
mainhasn't completed successfully yet. If green, it creates the annotated git tagv<version>and pushes it, which triggerscargo-distin CI to compile binaries, package installers, and publish the GitHub Release.
