A pure-Zig implementation of libp2p, the modular peer-to-peer networking stack. zig-libp2p targets the lean-consensus / Ethereum-consensus subset of libp2p — QUIC transport, gossipsub, and request/response — and is verified to interoperate with go-libp2p and rust-libp2p on the wire.
It was built for the zeam lean-Ethereum
client, but the Host API is client-agnostic and usable by any Zig project that
needs a libp2p node.
Status: pre-1.0. The public API is still evolving toward a 1.0 freeze (#172). Pin a release tag in your
build.zig.zonand review the changelog before upgrading.
- QUIC-first transport (RFC 9000/9001) with libp2p TLS 1.3, backed by the
companion
zquicstack — no C dependencies, builds in aFROM scratchcontainer. - Gossipsub v1.1 (StrictNoSign) with mesh maintenance, peer scoring, PX, IDONTWANT, direct peers, and FANOUT.
- Request/response with length-prefixed SSZ + snappy framing.
- Cross-implementation interop continuously exercised in CI against go-libp2p and rust-libp2p (handshake, ping, gossipsub, req/resp).
- Single dependency, single import —
@import("zig_libp2p")exposes the whole surface; oneHostobject drives transport, muxing, and protocols.
Add the dependency to your build.zig.zon (pin a released tag):
zig fetch --save "https://github.com/blockblaz/zig-libp2p/archive/refs/tags/v0.2.13.tar.gz"Then wire it into build.zig:
const zig_libp2p = b.dependency("zig_libp2p", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("zig_libp2p", zig_libp2p.module("zig_libp2p"));The transitively-pinned zquic (v1.7.49+)
supports the Shadow network simulator — deterministic,
bit-exact multi-peer replays — via the -Dshadow=true build flag. Because
zig-libp2p sits entirely on top of zquic for QUIC transport (no direct syscalls
of its own beyond what zquic re-exports), running a libp2p node under Shadow
just means building zquic with that flag transitively.
Embedders forward the flag through their own build.zig:
const shadow = b.option(bool, "shadow", "Build for the Shadow simulator") orelse false;
const zig_libp2p_dep = b.dependency("zig_libp2p", .{
.target = target,
.optimize = optimize,
// Forwarded into the zquic dep — zquic owns the syscall layer. When zquic
// is built with -Dshadow=true it links libc and routes time / random / UDP
// through libc so Shadow's LD_PRELOAD shim can intercept.
.shadow = shadow,
});Build with:
zig build -Dtarget=x86_64-linux-gnu -Dshadow=true -Doptimize=ReleaseSafe
file zig-out/bin/your-node # → dynamically linked, /lib64/ld-linux-x86-64.so.2The dynamically-linked-against-glibc binary is what Shadow's shim injects into.
The default no-libc Linux build is unaffected. See
zquic/docs/shadow.md
for the full list of behavior knobs that flip (libc-routed clock_gettime /
getrandom, sendmmsg/recvmmsg disabled in favor of per-message I/O), a
minimal shadow.yaml, and known limitations.
The public surface lives in src/root.zig; the canonical
end-to-end wiring (host creation, QUIC listen/dial, gossipsub, req/resp, event
loop) is examples/host_quic_node.zig. Build the
examples and run it with:
zig build examples
./zig-out/bin/example-host-quic-nodeCoverage of the libp2p specifications, organized by spec area. zig-libp2p targets the subset required by lean / Ethereum consensus, so some libp2p features are intentionally out of scope.
Legend: ✅ implemented · 🚧 partial / experimental · ⬜ planned · ⛔ out of scope
| Spec | Status | Notes |
|---|---|---|
| TCP | ✅ | transport.tcp |
| QUIC v1 (RFC 9000/9001) | ✅ | primary transport, via zquic; interop go ✅ · rust ✅ |
WebSocket — /ws (RFC 6455) |
✅ | transport.ws*; unit-tested |
Secure WebSocket — /wss |
⬜ | #94 |
| WebTransport | ⬜ | #94 |
| WebRTC | ⬜ | #94 |
| Spec | Status | Notes |
|---|---|---|
| TLS 1.3 (libp2p TLS, RFC 0001) | ✅ | over QUIC and TCP (/tls/1.0.0); interop go ✅ · rust ✅ |
Noise (/noise, XX) |
✅ | RSA, ECDSA-P256, secp256k1, ed25519 identities |
| Spec | Status | Notes |
|---|---|---|
| QUIC native streams | ✅ | default for the QUIC transport |
| yamux | ✅ | transport.yamux |
| mplex | ✅ | transport.mplex |
| Spec | Status | Notes |
|---|---|---|
| multistream-select 1.0 | ✅ | transport.stream_multistream |
| Spec | Status | Notes |
|---|---|---|
| Peer IDs / keypairs | ✅ | identity, keypair |
| Signed peer records (RFC 0002) | ✅ | identify.verifySignedPeerRecord |
| Spec | Status | Notes |
|---|---|---|
| gossipsub v1.1 (StrictNoSign) | ✅ | mesh, peer scoring (#199), PX, IDONTWANT, direct peers, FANOUT; interop go ✅ · rust ✅ |
| floodsub | ⛔ | superseded by gossipsub |
| Spec | Status | Notes |
|---|---|---|
| Bootstrap (static dial) | ✅ | connect_peers via connection_manager |
| Kademlia DHT | ✅ | kad_dht, host; lifecycle (#203), record validators (#198) |
| mDNS (LAN discovery) | ✅ | discovery.mdns, host; peer_discovered events (#207) |
| Rendezvous | ✅ | rendezvous client/server, cookie paging, peer_discovered (#209) |
| Spec | Status | Notes |
|---|---|---|
| AutoNAT v1 | ✅ | autonat; active probing, dial-back verification, sliding-window vote aggregation (#206). v2 wire codecs present; v2 transport wiring pending |
| Circuit Relay v2 | 🚧 | relay, QUIC runtime; /p2p-circuit dial + reservation refresh (#204) |
| DCUtR (hole punching) | ✅ | dcutr; auto-trigger on relayed connections with retry (#205) |
| Spec | Status | Notes |
|---|---|---|
ping (/ipfs/ping/1.0.0) |
✅ | interop go ✅ · rust ✅ |
identify (/ipfs/id/1.0.0) |
✅ | identify |
identify-push (/ipfs/id/push/1.0.0) |
✅ | identify, host, QUIC runtime auto-opens push streams |
| Request/response (length-prefixed, SSZ-snappy) | ✅ | req_resp; interop go ✅ · rust ✅ |
| Spec | Status | Notes |
|---|---|---|
| Private networks (PSK / pnet) | ⬜ | #171 |
| Resource manager (scope-based limits) | ⬜ | #169 |
The live cross-impl interop matrix is in
harness/quic/README.md; the full module map is
src/root.zig and the layout rationale is
docs/REPO_LAYOUT.md.
Cross-implementation conformance is part of CI. To run the QUIC matrix locally:
zig build -Doptimize=ReleaseFast
(cd harness/quic/impls/go-libp2p && go build -o interop-quic-node-go .)
(cd harness/quic/impls/rust-libp2p && cargo build --release --locked)
harness/quic/run_matrix.sh zig,go-libp2p handshake,ping,gossipsub,reqresp
harness/quic/run_matrix.sh zig,rust-libp2p handshake,ping,gossipsub,reqrespOr zig build interop-matrix for a quick zig↔go handshake+ping smoke. Harness details and the full status table
live in harness/README.md.
All examples are under examples/ and install to zig-out/bin/
via zig build:
| Example | What it shows |
|---|---|
example-host-quic-node |
Full Host + QUIC lifecycle (production wiring) |
interop-quic-node |
Env-driven endpoint used by the cross-impl matrix |
gen-libp2p-cert |
Mint a libp2p TLS certificate + derive its peer id |
example-gossipsub-mesh |
Gossipsub publish/subscribe over a mesh |
example-quic-ping-loopback |
QUIC ping round-trip on loopback |
example-autonat-membuf, example-kad-dht-membuf |
In-memory AutoNAT / Kademlia smoke |
See examples/README.md for the complete list.
Organized by libp2p layer (primitives → core → protocols → transport),
not by transport. Each folder below has its own README describing its modules;
the public API is the facade in src/root.zig.
zig-libp2p/
├── src/ → library source — see src/README.md
│ ├── primitives/ wire-agnostic building blocks (identity, varint, multistream, protobuf)
│ ├── core/ node runtime: Host, Swarm, connection manager, events
│ ├── protocols/ one folder per libp2p protocol (gossipsub, kad-dht, autonat, relay, …)
│ ├── transport/ QUIC stack, TCP, WebSocket, yamux/mplex
│ └── security/ libp2p TLS 1.3 + Noise
├── examples/ runnable examples — see examples/README.md
├── harness/ cross-impl interop harnesses (quic/, tcp/) — see harness/README.md
├── bench/ micro-benchmarks
├── build/ build helpers (deps, examples, fuzz, soak, interop)
├── docs/ design docs (architecture, security, per-protocol)
├── tests/ integration tests — see tests/interop/README.md
├── fixtures/ shared test fixtures
└── vendor/ vendored TLS/RSA, outside src/ — see vendor/README.md
| Folder | README |
|---|---|
src/ overview |
src/README.md |
src/primitives/ |
src/primitives/README.md |
src/core/ |
src/core/README.md |
src/protocols/ |
src/protocols/README.md |
src/transport/ |
src/transport/README.md |
src/security/ |
src/security/README.md |
examples/ |
examples/README.md |
harness/ |
harness/README.md |
vendor/ |
vendor/README.md |
Full rationale and the migration phases are in
docs/REPO_LAYOUT.md; the layer diagram is in
docs/ARCHITECTURE.md. Legacy src/*.zig import paths
remain as compatibility shims through the 1.0 freeze.
- Repository layout —
docs/REPO_LAYOUT.md· Architecture —docs/ARCHITECTURE.md - Security model & wire limits —
docs/SECURITY.md - AutoNAT —
docs/AUTONAT.md· Kademlia DHT —docs/KAD_DHT.md· mDNS —docs/MDNS.md - Async swarm design —
docs/async-swarm.md - zeam integration notes —
docs/zeam-parity.md
Per-protocol gaps are tracked in the spec-coverage tables above. The broader milestones toward a stable release:
- 1.0-RC API freeze + semver — #172
- Third-party security audit + disclosure policy — #170
- Async swarm (
std.Ioco-scheduled, moving off the threaded runtime) — #57
Spec-compliance umbrella: #80.
zig fmt --check . # formatting
zig build test # unit tests + example smoke-runs
zig build soak-test # opt-in 60s QUIC gossipsub soak (#235)
zig build fuzz # wire-decoder fuzzing via std.testing.fuzzCI workflows live in .github/workflows/.
Releases are cut manually: tag the chosen commit and publish a GitHub
release (gh release create vX.Y.Z --target main --notes …), bumping
.version in build.zig.zon and the install snippet above in
the same change.
Contributions are welcome. Please open an issue to discuss substantial changes
first, keep zig fmt clean, and make sure zig build test passes. New protocol
behaviour should come with unit tests and, where it crosses the wire, an entry
in the interop matrix.
Wire-size limits and the threat model are documented in
docs/SECURITY.md. A coordinated-disclosure policy is
tracked in #170; until it
lands, please report vulnerabilities privately to the maintainer rather than via
public issues.
Released under the MIT License.
Built on the libp2p specifications and verified against go-libp2p and rust-libp2p.