libp2p-wasi-sockets
A WASI 0.2 sockets transport for rust-libp2p.
Implements libp2p_core::Transport over wasi:sockets/tcp, enabling rust-libp2p applications to run as Wasm Components on any WASI 0.2 host — Wasmtime, Spin, jco, Wasmer — without any modification to the rest of the libp2p stack.
Why does this exist?
Every major rust-libp2p sub-crate (libp2p-core, libp2p-swarm, libp2p-noise, libp2p-yamux, libp2p-gossipsub, …) compiles cleanly for wasm32-wasip2. The only missing piece was a transport: libp2p-tcp is gated out for all wasm targets, and no wasi:sockets-based alternative existed.
This crate closes that gap.
What you can do with it
- Run libp2p-based protocols as sandboxed Wasm Components in Wasmtime or Spin.
- Drop P2P functionality into plugin systems that already embed a WASI 0.2 runtime.
- Leverage the WASI network capability model — the host explicitly grants (or denies) network access per component, giving stronger isolation than containers.
What is deliberately out of scope
- UDP / QUIC —
wasi:sockets/udpdoesn't expose the socket controlsquinnneeds. - DNS multiaddrs —
/dns4,/dns6,/dnsaddrplanned for v0.2. - Browser — already covered by
libp2p-websocket-websys/libp2p-webtransport-websys. - NAT traversal — AutoNAT, circuit-relay, DCUtR are not supported by the
wasi-socketsAPI.
Status
v0.1.0 — all milestones complete; full Noise XX + Yamux interop verified against native rust-libp2p.
| Milestone | Description | Status |
|---|---|---|
| M0 | Scaffold, Cargo.toml, CI, multiaddr utils, unit tests | ✅ |
| M1 | WasiTcpStream AsyncRead/AsyncWrite bridge + echo integration test |
✅ |
| M2 | WasiTcpTransport listen_on + dial integration test |
✅ |
| M3 | Multi-listener, AddressExpired / ListenerClosed lifecycle events | ✅ |
| M4 | Noise XX + Yamux upgrade over WasiTcpTransport |
✅ |
| M5 | Interop test: WASM component ↔ native rust-libp2p (tokio) | ✅ |
| M6 | Docs polish, examples, 0.1.0 crates.io release | ✅ |
Quick start
# Cargo.toml — do NOT enable the `tcp` feature on the umbrella `libp2p` crate
[]
= "0.1"
= "0.47"
= "0.46"
= "0.47"
= "0.47"
= { = "0.2", = ["ed25519", "rand"] }
= "0.6"
use ;
use WasiTcpTransport;
use Duration;
async
Build and run:
# -S inherit-network grants the component access to the host network.
# Without it, all dials return Error::AccessDenied.
Supported multiaddrs
| Pattern | Status |
|---|---|
/ip4/<addr>/tcp/<port> |
✅ |
/ip6/<addr>/tcp/<port> |
✅ |
…/p2p/<peer-id> suffix |
✅ stripped, not dialled |
/ip4/0.0.0.0/tcp/0 (ephemeral port) |
✅ |
/ip6/::/tcp/0 (ephemeral port) |
✅ |
/dns4, /dns6, /dnsaddr |
❌ planned for v0.2 |
/quic-v1, /ws, /wss, /webrtc |
❌ out of scope |
Architecture
┌─────────────────────────────────────────┐
│ libp2p Swarm (user code) │
│ Behaviour: ping / gossipsub / kad / … │
└──────────────────┬──────────────────────┘
│
┌──────────────────┴──────────────────────┐
│ Yamux (muxer) · Noise (security) │
└──────────────────┬──────────────────────┘
│
┌──────────────────┴──────────────────────┐
│ libp2p-wasi-sockets (this crate) │
│ │
│ WasiTcpTransport impl Transport │
│ WasiTcpStream impl AsyncRead │
│ AsyncWrite │
└──────────────────┬──────────────────────┘
│
┌──────────────────┴──────────────────────┐
│ wstd 0.6 (wasi:sockets/tcp wrapper) │
└──────────────────┬──────────────────────┘
│
┌──────────────────┴──────────────────────┐
│ WASI 0.2 host (Wasmtime / Spin / …) │
└─────────────────────────────────────────┘
Async bridge
wstd's TcpStream::read / write are async fns; futures::io::AsyncRead / AsyncWrite are poll-based. The bridge works by boxing each in-flight wstd operation as a Pin<Box<dyn Future>> and re-polling it on subsequent poll_* calls. On wasm32-wasip2 there are no threads, so the Send bound is satisfied via unsafe impl — safe because WASI resource handles are plain integers.
Pitfalls
Do not pull in libp2p-tcp
The umbrella libp2p crate gates libp2p-tcp off for all wasm targets, but if you pull it in manually the build will fail. Depend on sub-crates directly and verify:
|
# must produce no output
Wasmtime network permissions
Wasmtime denies all network access by default:
# Grant full network access (development)
# Grant only specific addresses (production)
Requirements
- Rust 1.83+
- Target:
wasm32-wasip2 - WASI 0.2 host: Wasmtime ≥ 44, Spin ≥ 3.5, or any WASI 0.2.1-compatible runtime
License
Licensed under either of:
at your option. This matches the license of rust-libp2p.