A modern Swift implementation of the libp2p networking stack, designed for wire-protocol compatibility with Go and Rust implementations.
- Transport Layer: TCP (SwiftNIO), QUIC (RFC 9000/9001), WebRTC Direct (DTLS 1.2 + SCTP), Memory for testing
- Security Layer: TLS 1.3 (libp2p spec, via swift-tls), Noise Protocol (XX pattern), QUIC TLS 1.3, DTLS 1.2 (WebRTC), Plaintext for testing
- Multiplexing: Yamux stream multiplexer, Mplex, QUIC native multiplexing, SCTP data channels (WebRTC)
- Protocol Negotiation: multistream-select 1.0
- Identity: Ed25519/ECDSA P-256 keys, PeerID derivation
- Addressing: Full Multiaddr support
- Discovery: SWIM membership, mDNS local discovery, CYCLON random peer sampling
- NAT Traversal: Circuit Relay v2, AutoNAT, DCUtR hole punching
- Standard Protocols: Identify (+ Push), Ping, GossipSub, Kademlia DHT, Plumtree
| Component | Status | Notes |
|---|---|---|
| PeerID / KeyPair (Ed25519, ECDSA P-256) | ✅ Implemented | |
| Multiaddr | ✅ Implemented | |
| Varint / Multihash | ✅ Implemented | |
| Signed Envelope / Peer Records | ✅ Implemented | |
| EventEmitting pattern | ✅ Implemented | |
| ProtobufLite (shared wire type 2 utility) | ✅ Implemented |
| Component | Status | Notes |
|---|---|---|
| TCP (SwiftNIO) | ✅ Implemented | |
| QUIC (swift-quic) | ✅ Implemented | 0-RTT, connection migration pending |
| Memory (testing) | ✅ Implemented | |
| Circuit Relay v2 Transport | ✅ Implemented | |
| WebRTC Direct (swift-webrtc) | ✅ Implemented | DTLS 1.2 + SCTP, 25 tests |
| WebSocket | ❌ Not implemented | Browser interop |
| Component | Status | Notes |
|---|---|---|
| Noise XX (X25519 + ChaChaPoly + SHA256) | ✅ Implemented | Small-order key validation included |
| TLS 1.3 (swift-tls, libp2p spec) | ✅ Implemented | Go/Rust compatible |
| QUIC TLS 1.3 (libp2p certificate) | ✅ Implemented | |
| Plaintext (testing) | ✅ Implemented | |
| Early Muxer Negotiation (TLS ALPN) | ✅ Implemented | EarlyMuxerNegotiating protocol, ALPN muxer hints |
| Component | Status | Notes |
|---|---|---|
| Yamux | ✅ Implemented | Flow control, keep-alive, 42+ tests |
| Mplex | ✅ Implemented | Frame-level tests; connection/stream tests limited |
| QUIC native multiplexing | ✅ Implemented |
| Component | Status | Notes |
|---|---|---|
| multistream-select v1 | ✅ Implemented | 20 tests |
| multistream-select v1 Lazy (0-RTT) | ❌ Not implemented |
| Component | Status | Notes |
|---|---|---|
| SWIM membership | ✅ Implemented | |
| mDNS local discovery | ✅ Implemented | |
| PeerStore (basic, in-memory, LRU) | ✅ Implemented | |
| AddressBook (scoring, priority) | ✅ Implemented | |
| Bootstrap (seed peers) | ✅ Implemented | |
| PeerStore TTL-based garbage collection | ✅ Implemented | LRU + TTL-based GC |
| ProtoBook (per-peer protocol tracking) | ✅ Implemented | |
| KeyBook (per-peer public key storage) | ✅ Implemented | |
| CYCLON random peer sampling | ✅ Implemented | 27 tests |
| Component | Status | Notes |
|---|---|---|
| Identify (+ Push) | ✅ Implemented | SignedPeerRecord verification included |
| Ping | ✅ Implemented | Single/multiple ping + statistics |
| Circuit Relay v2 | ✅ Implemented | Client + Server, 72 tests |
| DCUtR (hole punching) | ✅ Implemented | 50 tests |
| AutoNAT v1 | ✅ Implemented | Rate limiting, 74 tests |
| GossipSub v1.1 | ✅ Implemented | Peer scoring, signature verification |
| GossipSub IDONTWANT (v1.2 wire format) | ✅ Implemented | Full encode/decode + send/receive |
| GossipSub per-topic scoring | ❌ Not implemented | Global scoring only |
| Kademlia DHT | ✅ Implemented | 65 tests, record validation |
| Kademlia client/server mode restriction | Config exists but doesn't restrict behavior | |
| Kademlia RecordValidator.Select | ❌ Not implemented | Best record selection missing |
| Kademlia persistent storage | ❌ Not implemented | In-memory only |
| Plumtree (Epidemic Broadcast Trees) | ✅ Implemented | 50 tests |
| Component | Status | Notes |
|---|---|---|
| NAT port mapping (UPnP + NAT-PMP) | ✅ Implemented | macOS-only gateway detection |
| Circuit Relay v2 (client + server) | ✅ Implemented | |
| AutoNAT | ✅ Implemented | |
| DCUtR | ✅ Implemented |
| Component | Status | Notes |
|---|---|---|
| Node (actor) | ✅ Implemented | |
| ConnectionPool (limits, trimming) | ✅ Implemented | |
| ConnectionUpgrader | ✅ Implemented | Sequential 2-phase multistream-select |
| ConnectionGater | ✅ Implemented | |
| HealthMonitor | ✅ Implemented | |
| ReconnectionPolicy | ✅ Implemented | |
| Resource Manager (multi-scope) | ✅ Implemented | System-wide and per-peer resource accounting |
- Swift 6.2+
- macOS 15+ / iOS 18+ / tvOS 18+ / watchOS 11+ / visionOS 2+
Add swift-libp2p to your Package.swift:
dependencies: [
.package(url: "https://github.com/example/swift-libp2p.git", from: "0.1.0")
]Then add the necessary targets:
.target(
name: "YourApp",
dependencies: [
"P2P",
"P2PTransportTCP",
"P2PSecurityNoise",
"P2PMuxYamux"
]
)import P2P
import P2PTransportTCP
import P2PSecurityNoise
import P2PMuxYamux
// Create a node
let keyPair = KeyPair.generateEd25519()
let config = NodeConfiguration(
keyPair: keyPair,
listenAddresses: [Multiaddr("/ip4/0.0.0.0/tcp/4001")!],
transports: [TCPTransport()],
security: [NoiseUpgrader()],
muxers: [YamuxMuxer()],
limits: ConnectionLimits(maxConnections: 100)
)
let node = Node(configuration: config)
// Register protocol handler
await node.handle("/chat/1.0.0") { stream in
// Handle incoming chat messages
let data = try await stream.read()
print("Received: \(String(data: data, encoding: .utf8)!)")
}
// Start the node
try await node.start()
print("Node started with PeerID: \(await node.peerID)")
// Connect to a peer
let remotePeer = try await node.connect(
to: Multiaddr("/ip4/192.168.1.100/tcp/4001/p2p/12D3KooW...")!
)
// Open a stream
let stream = try await node.newStream(to: remotePeer, protocol: "/chat/1.0.0")
try await stream.write(Data("Hello!".utf8))+----------------------------------------------------------------------+
| Application Layer |
| (Your protocols, GossipSub, Plumtree, Kademlia DHT, SWIM, CYCLON) |
+----------------------------------------------------------------------+
| Protocol Negotiation (multistream-select) |
+----------------------------------------------------------------------+
| Stream Multiplexing (Yamux, Mplex) |
+----------------------------------------------------------------------+
| Security Layer (TLS 1.3, Noise XX) |
+----------------------------------------------------------------------+
| Transport Layer (TCP, QUIC, WebRTC, Memory, Circuit Relay) |
+----------------------------------------------------------------------+
| NAT Traversal (Circuit Relay v2, AutoNAT, DCUtR) |
+----------------------------------------------------------------------+
| Core Types (PeerID, Multiaddr, KeyPair) |
+----------------------------------------------------------------------+
| Module | Description |
|---|---|
P2PCore |
Core types: PeerID, Multiaddr, KeyPair, EventEmitting |
P2PTransport |
Transport protocol definition |
P2PTransportTCP |
TCP transport implementation (SwiftNIO) |
P2PTransportQUIC |
QUIC transport implementation (RFC 9000) |
P2PTransportWebRTC |
WebRTC Direct transport (DTLS 1.2 + SCTP) |
P2PTransportMemory |
In-memory transport for testing |
P2PSecurity |
Security protocol definition |
P2PSecurityTLS |
TLS 1.3 implementation (swift-tls) |
P2PSecurityNoise |
Noise Protocol implementation |
P2PSecurityPlaintext |
Plaintext security for testing |
P2PMux |
Muxer protocol definition |
P2PMuxYamux |
Yamux multiplexer implementation |
P2PMuxMplex |
Mplex multiplexer implementation |
P2PNegotiation |
multistream-select protocol |
P2PDiscovery |
Discovery protocol definition |
P2PDiscoverySWIM |
SWIM membership protocol |
P2PDiscoveryMDNS |
mDNS local network discovery |
P2PDiscoveryCYCLON |
CYCLON random peer sampling |
P2P |
Integration layer (Node, ConnectionPool) |
P2PProtocols |
Protocol service definitions |
P2PIdentify |
Identify protocol |
P2PPing |
Ping protocol |
P2PCircuitRelay |
Circuit Relay v2 for NAT traversal |
P2PGossipSub |
GossipSub pubsub protocol |
P2PKademlia |
Kademlia DHT implementation |
P2PAutoNAT |
AutoNAT protocol for NAT detection |
P2PDCUtR |
Direct Connection Upgrade through Relay |
P2PPlumtree |
Plumtree epidemic broadcast trees |
This implementation follows the official libp2p specifications for wire-protocol compatibility:
| Protocol | Protocol ID | Specification |
|---|---|---|
| multistream-select | /multistream/1.0.0 |
spec |
| TLS | /tls/1.0.0 |
spec |
| Noise | /noise |
spec |
| Yamux | /yamux/1.0.0 |
spec |
| Mplex | /mplex/6.7.0 |
spec |
| Identify | /ipfs/id/1.0.0 |
spec |
| Ping | /ipfs/ping/1.0.0 |
spec |
| Circuit Relay v2 | /libp2p/circuit/relay/0.2.0/hop, /libp2p/circuit/relay/0.2.0/stop |
spec |
| GossipSub | /meshsub/1.1.0 |
spec |
| Kademlia DHT | /ipfs/kad/1.0.0 |
spec |
| AutoNAT | /libp2p/autonat/1.0.0 |
spec |
| DCUtR | /libp2p/dcutr |
spec |
| WebRTC Direct | /udp/<port>/webrtc-direct |
spec |
| Plumtree | /plumtree/1.0.0 |
paper |
| CYCLON | /cyclon/1.0.0 |
paper |
- async/await everywhere: No EventLoopFuture, pure Swift concurrency
- Value types first: struct over class where appropriate
- Sendable compliance: Thread-safe by design using
Mutex<T> - Protocol-oriented: All major components defined as protocols
| Use Case | Pattern |
|---|---|
| User-facing APIs | actor |
| High-frequency internal state | class + Mutex<T> |
| Data containers | struct |
All services that expose AsyncStream<Event> conform to the EventEmitting protocol:
// Services implement EventEmitting protocol
public final class MyService: EventEmitting, Sendable {
public var events: AsyncStream<MyEvent> { ... }
public func shutdown() { ... } // Required: terminates event stream
}
// Safe usage with automatic cleanup
try await withEventEmitter(myService) { service in
for await event in service.events {
// Handle events
}
}
// shutdown() called automaticallyImportant: Always call shutdown() when done with a service to prevent for await from hanging.
All components are injected via protocols:
let node = Node(configuration: NodeConfiguration(
keyPair: myKeyPair,
transports: [TCPTransport()], // Injectable
security: [NoiseUpgrader()], // Injectable
muxers: [YamuxMuxer()] // Injectable
))let limits = ConnectionLimits(
maxConnections: 100,
maxConnectionsPerPeer: 2,
idleTimeout: .seconds(60)
)let policy = ReconnectionPolicy(
enabled: true,
maxAttempts: 5,
backoff: .exponential(base: .seconds(1), factor: 2.0, maxDelay: .seconds(60))
)let gater = ConnectionGater(
allowlist: [trustedPeerID],
denylist: [blockedPeerID],
customFilter: { peer, direction in
// Custom filtering logic
return true
}
)All event-emitting services follow the EventEmitting protocol pattern:
// Node events
for await event in await node.events {
switch event {
case .peerConnected(let peer):
print("Connected to \(peer)")
case .peerDisconnected(let peer):
print("Disconnected from \(peer)")
case .listenError(let addr, let error):
print("Listen error on \(addr): \(error)")
case .connectionError(let peer, let error):
print("Connection error: \(error)")
}
}
// Service events (e.g., GossipSub)
let gossipsub = GossipSubService(...)
Task {
for await event in gossipsub.events {
switch event {
case .messageReceived(let topic, let message):
print("Received on \(topic): \(message)")
case .peerJoined(let topic, let peer):
print("\(peer) joined \(topic)")
default: break
}
}
}
// Don't forget to shutdown when done!
gossipsub.shutdown()QUIC provides built-in encryption (TLS 1.3) and native stream multiplexing:
import P2PTransportQUIC
// QUIC transport with libp2p certificate
let quicTransport = QUICTransport(configuration: .init(
certificateProvider: .libp2p(keyPair: keyPair)
))
// Listen on QUIC
let listener = try await quicTransport.listen(
Multiaddr("/ip4/0.0.0.0/udp/4001/quic-v1")!
)
// Dial with QUIC
let connection = try await quicTransport.dial(
Multiaddr("/ip4/192.168.1.100/udp/4001/quic-v1/p2p/12D3KooW...")!
)Benefits of QUIC:
- 0-RTT connection establishment
- Native stream multiplexing (no Yamux needed)
- Built-in TLS 1.3 security
- Connection migration support
WebRTC Direct provides UDP-based encrypted transport with native multiplexing via SCTP data channels:
import P2PTransportWebRTC
let webrtcTransport = WebRTCTransport()
// Listen on WebRTC Direct
let listener = try await webrtcTransport.listenSecured(
Multiaddr("/ip4/0.0.0.0/udp/4001/webrtc-direct")!,
localKeyPair: keyPair
)
// Dial with WebRTC Direct (certhash from listener's address)
let connection = try await webrtcTransport.dialSecured(
Multiaddr("/ip4/127.0.0.1/udp/4001/webrtc-direct/certhash/<hash>")!,
localKeyPair: keyPair
)Benefits of WebRTC Direct:
- UDP-based (NAT traversal friendly)
- Built-in DTLS 1.2 security
- SCTP data channel multiplexing (no Yamux needed)
- Browser-compatible transport path
Circuit Relay v2 enables peers behind NATs to communicate through public relay nodes.
A peer behind NAT makes a reservation on a public relay to receive incoming connections:
import P2PCircuitRelay
let client = RelayClient()
await client.registerHandler(registry: node)
// Reserve a slot on the relay
let reservation = try await client.reserve(on: relayPeerID, using: node)
print("Reserved until: \(reservation.expiration)")
print("Relay addresses: \(reservation.addresses)")Another peer can connect to the NAT'd peer through the relay:
// Connect to target through relay
let connection = try await client.connectThrough(
relay: relayPeerID,
to: targetPeerID,
using: node
)
// Use the relayed connection
try await connection.write(Data("Hello through relay!".utf8))To run a public relay node:
let server = RelayServer(configuration: .init(
maxReservations: 128,
maxCircuitsPerPeer: 16,
reservationDuration: .seconds(3600)
))
await server.registerHandler(
registry: node,
opener: node,
localPeer: node.peerID,
getLocalAddresses: { node.listenAddresses }
)Relayed addresses use the p2p-circuit protocol:
/ip4/1.2.3.4/tcp/4001/p2p/{relay-peer-id}/p2p-circuit/p2p/{target-peer-id}
swift test# Start a rust-libp2p node
cargo run --example ping -- --listen /ip4/127.0.0.1/tcp/4001
# Connect from swift-libp2p
swift run swift-libp2p-example dial /ip4/127.0.0.1/tcp/4001/p2p/<peer-id>- swift-nio - Network I/O
- swift-crypto - Cryptographic primitives
- swift-log - Logging
- swift-protobuf - Protocol Buffers
- swift-tls - TLS 1.3 (pure Swift)
- swift-certificates - X.509 certificate handling
- swift-asn1 - ASN.1 DER encoding/decoding
- swift-quic - QUIC protocol (RFC 9000)
- swift-webrtc - WebRTC Direct (DTLS 1.2 + SCTP)
- swift-SWIM - SWIM membership protocol
- swift-mDNS - mDNS/DNS-SD discovery
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
- libp2p Specifications
- rust-libp2p - Reference implementation
- go-libp2p
- SWIM Paper
- Plumtree Paper - Epidemic Broadcast Trees
- CYCLON Paper - Inexpensive Membership Management
MIT License