Skip to content

Repository files navigation

iroh-usbip

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.

OS Support Matrix

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.

iroh-usbip demo

Installation

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).


Usage

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
Loading

Steps

  1. Check the machine and list USB devices on the Host:
    iroh-usbip doctor
    iroh-usbip list
  2. Share exactly one device. Bus ID is preferred because VID/PID pairs need not be unique:
    iroh-usbip share --bus-id <BUS_ID>
  3. 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.


Advanced Installation

Windows prerequisites and security

No Zadig or per-device WinUSB replacement is used:

  • Host: usbipd-win owns native device capture. iroh-usbip can install it with winget, bind the selected device, and undo a newly-created binding at teardown.
  • Client: usbip-win2 provides the signed virtual controller. iroh-usbip pins the installer version and SHA-256, validates its Authenticode signature, and requests UAC before running it.
  • Security: usbipd-win normally creates a LAN-accessible firewall rule. Before binding, iroh-usbip temporarily 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
```

Nix (Flake-enabled)

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

From Source (Cargo Fallback)

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-usbip

Development

This section is for developers contributing to iroh-usbip.

Architecture & Reference

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.

Development Prerequisites

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.0 dev library
  • pkg-config
  • just (task runner)
  • git-cliff (changelog generator)
  • gh (GitHub CLI)
  • python3

Entering the Development Environment

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)

Nix & Cargo Interplay Guidelines

  1. Idiomatic Cargo Flow: Once inside nix develop, run standard Cargo commands directly (e.g. cargo build, cargo test). Spawning nested Nix subshells (like nix develop --command cargo) is avoided for local runs to keep feedback loops fast and IDE integrations (like Rust-Analyzer) working flawlessly.
  2. Task Runner (just): We reserve the justfile purely for complex, multi-step orchestrations or privilege transitions (e.g. running kernel integration tests and release pipelines).

Running / Testing Local Builds with Root Privileges

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>

Testing Scopes

We separate testing into two distinct environments and privilege scopes:

1. Hermetic Checks (Mock Unit & Integration Tests)

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

2. Native E2E Integration Tests

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:
    just test-e2e
    Note: The runner automatically compiles the binary as the normal user first, then runs scripts/e2e.sh using sudo -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

Continuous Integration (CI)

Our GitHub Actions pipeline (defined in .github/workflows/ci.yml) runs on every commit/PR:

  1. Nix Setup: Installs Nix/Lix and configures Magic Nix Cache for incremental store caching.
  2. Hermetic Checks: Executes nix flake check to verify formatting, clippy, and unit tests inside the sandbox.
  3. E2E Testing: Runs the unsandboxed full mock lifecycle using nix develop --command scripts/e2e.sh --mock.
  4. Native compilation: Compiles and runs all portable tests on current macOS and Windows runners so platform adapters cannot silently rot.

Release Process (Automation & SemVer)

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.

How to release:

  1. Prepare Release (from a clean main branch inside nix 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 main has not passed GitHub Actions CI. If green, it creates a release/v<version> branch, bumps the version in Cargo.toml, updates CHANGELOG.md via git-cliff, and commits the changes.

  2. Submit PR & Merge: Push the release/v<version> branch to GitHub, open a PR, and merge it to main once PR checks (including E2E checks) succeed.

  3. Tag and Publish (inside nix develop): Pull the merged commit locally on main and run:

    just tag-release

    Gate 2 (Poka-Yoke): This will fail if the post-merge CI on main hasn't completed successfully yet. If green, it creates the annotated git tag v<version> and pushes it, which triggers cargo-dist in CI to compile binaries, package installers, and publish the GitHub Release.

About

Secure P2P USB-over-IP. Tunnel physical USB devices to remote clients over encrypted Iroh P2P streams with zero network configuration.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages