dumbpipe: netcat for machines you cannot see, addressed by endpoint id
Unix pipes between devices
At a glance
- What is it?
- dumbpipe is a small Rust CLI that carries a byte stream between two machines over QUIC using 256 bit endpoint ids instead of IP addresses. It fits ad hoc tunnels and socket forwarding, and it is not a replacement for a managed network.
- Who is it for?
- dumbpipe suits an engineer who needs one ad hoc tunnel between two machines today, a shared terminal session, or a Unix socket exposed to a colleague, without arranging inbound firewall rules or a static address. It does not suit production access that needs identity, audit trails or a stable path, because the only credential in the design is the ticket itself.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A ticket replaces the IP address on both ends of the pipe
dumbpipe is a command line tool that pipes data between two machines over a QUIC connection. The model comes from netcat, and the README says so directly, but the addressing is different. Where netcat needs an IP address and a port, dumbpipe works with 256 bit endpoint ids, which makes it largely independent of where a machine sits on the network. A connection is made in two halves. One side runs a listener, which prints a ticket. The other side passes that ticket to a connect command, and the two endpoints find each other and negotiate a session. The README also notes that connections are encrypted using TLS. The tool is written in Rust by the n0 team and is described as an example project built on the iroh crate, so it doubles as a worked example of how iroh is meant to be used.
Hole punching first, a relay when the network refuses
The interesting part of the design sits below the CLI. Iroh handles hole punching and NAT traversal whenever it can, and falls back to a relay when hole punching does not succeed. That fallback is what makes the tool usable on ordinary office and home networks without opening ports or configuring a VPN, and it is also the part that deserves scrutiny: a relay in the path is a third party carrying your bytes, even if the session itself is encrypted with TLS and the relay only moves ciphertext it cannot read. When hole punching succeeds, the traffic is direct between the two peers. Which of the two paths a given connection takes is not something the CLI output in the documentation spells out, so anyone with a strict policy about where packets travel would want that confirmed before relying on it. The repository topics list hole punching as a headline feature, and the tool is packaged with a tls-ring feature in its iroh dependency, so the encryption stack is part of the build rather than an optional layer.
Installation is a single cargo or brew command
The tool ships on crates.io, so a Rust toolchain is all you need on Linux or macOS.
cargo install dumbpipeOn macOS the same binary is available through Homebrew.
brew install dumbpipeThe Cargo manifest pins a minimum Rust version of 1.91 and uses the 2021 edition, which means an older toolchain on a managed CI image will refuse to build it. That is worth knowing before a job depends on it, since the release profile in the manifest is tuned for a small static binary with panic set to abort, link time optimisation on and symbols stripped. The same manifest declares the licence as MIT OR Apache-2.0, and the repository ships LICENSE-APACHE and LICENSE-MIT side by side, which is the usual dual licence arrangement that lets a company pick whichever terms its legal side prefers. GitHub itself classifies the licence as a custom one that it cannot categorise.
Forwarding a dev server takes one listener and one connector
The most useful pattern in the documentation is exposing a development web server to a colleague in another office. The server runs locally on port 3000 as usual. A dumbpipe listener on the remote side accepts connections and forwards everything it receives to that local server.
dumbpipe listen-tcp --host localhost:3000That command prints the ticket you send to your colleague. On their machine a connector listens on a local port and forwards everything arriving there to the endpoint encoded in the ticket.
dumbpipe connect-tcp --addr 0.0.0.0:3001 <ticket>The colleague then browses port 3001 locally and the requests travel through the pipe to your dev server. The documentation notes that any number of connections can flow through a single dumb pipe, though each one becomes a separate local TCP connection on the far side. The same two subcommands work in reverse for other services, and the tty-share example shows a listener bound to a local port being tunnelled to a remote address instead.
Unix socket forwarding needs matching socket paths on both machines
Applications that talk over a Unix socket can be forwarded too, which is how the documentation shows sharing a Zellij session. The pattern is that the socket directory layout has to exist locally as well, because the connector expects a real path to bind to. The remote side listens on the session socket and yields a ticket.
dumbpipe listen-unix --socket-path /tmp/zellij-0/0.42.2/remote-task-1234The local side mirrors the version directory, creates the socket and connects it to the remote one.
dumbpipe connect-unix --socket-path /tmp/zj-remote/0.42.1/remote-task-1234 <ticket>The local client then attaches to that socket path through the ZELLIJ_SOCKET_DIR environment variable. Because the socket path encodes the Zellij version, the two machines do not need matching versions, only matching directory structures. The subcommands also mix: a Unix socket listener on one machine can be paired with a TCP connector on the other, which is the flexibility that makes the tool useful when one side runs something that only speaks a Unix socket and the other side only has a browser.
A custom ALPN opens the tool to other iroh services
Most of the interface treats the pipe as an anonymous byte stream, but there is an expert mode that sets a custom ALPN string, which is how a dumbpipe client talks to an existing iroh service such as the iroh-bytes protocol.
echo request1.bin | dumbpipe connect <ticket> --custom-alpn utf8:/iroh-bytes/2 > response1.binThe request file is written to standard input and the response is written to standard output, so the tool drops into an ordinary shell pipeline. The documentation notes that the protocol was previously called iroh-bytes, which is the kind of detail that matters when you are matching a string against a server. This is the feature that turns dumbpipe from a demo into a building block: with an ALPN you are not limited to piping into whatever the peer happens to run, you can address a specific protocol on the other side.
Where dumbpipe stops being enough
The design has one clear boundary, and it is identity. A ticket is the entire credential in the design, and the documentation shows no authentication option, no key pinning and no user model for the listen and connect commands. Anyone holding the ticket can open the pipe, so sharing it is sharing the service. There is also no addressability: the endpoint id is location transparent by design, which is exactly what makes it easy to use and exactly what makes it hard to place under an access policy. For a shared terminal session the README points out that the public tty-share server is unreliable and not end to end encrypted, and dumbpipe is the fix for that specific case, which tells you where the tool fits: filling a specific gap for a session you control, not replacing the access layer of an organisation. Releases come at v0.40.0 on 2026-10-16, v0.39.0 on 2026-06-15 and v0.38.0 on 2026-05-27, and the last push was on 2026-10-16, so the project is moving, and you should pin a version rather than track the default branch.
Editorial conclusion
dumbpipe suits an engineer who needs one ad hoc tunnel between two machines today, a shared terminal session, or a Unix socket exposed to a colleague, without arranging inbound firewall rules or a static address. It does not suit production access that needs identity, audit trails or a stable path, because the only credential in the design is the ticket itself. Before adopting it, verify that the peer can be reached by hole punching on your network, decide who may forward the ticket, and check that the relay fallback your connection will use is acceptable under your data handling rules.
Frequently asked questions
How do I install dumbpipe?
The README gives two options: cargo install dumbpipe for anyone with a Rust toolchain, or brew install dumbpipe on macOS. The Cargo manifest requires Rust 1.91 or newer.
What is a dumbpipe ticket?
The listener command prints a ticket, and the other machine passes it to the connect command. It encodes the 256 bit endpoint id of the listening side, which replaces the IP address and port pair that netcat would need.
Does dumbpipe work behind NAT?
The documentation states that iroh takes care of hole punching and NAT traversal whenever possible, and falls back to a relay when hole punching does not succeed. The connection is encrypted using TLS either way.
How do I share a terminal session with dumbpipe?
Run a TCP listener on the host with the session and a connector on the client, then point the client tool at the local port. The README uses tty-share this way because the public tty-share server is unreliable and is not end to end encrypted.
What licence is dumbpipe released under?
The Cargo manifest declares MIT OR Apache-2.0, and the repository contains LICENSE-APACHE and LICENSE-MIT. GitHub lists the repository licence as a custom one it cannot classify.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/n0-computer-dumbpipe)Was this useful?