swift-libp2p

main

Modern libp2p networking stack for Swift
1amageek/swift-libp2p

swift-libp2p

A modern Swift implementation of the libp2p networking stack, designed for wire-protocol compatibility with Go and Rust implementations.

Features

  • 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

Implementation Status

Core Stack

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

Transport

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

Security

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

Multiplexing

Component Status Notes
Yamux ✅ Implemented Flow control, keep-alive, 42+ tests
Mplex ✅ Implemented Frame-level tests; connection/stream tests limited
QUIC native multiplexing ✅ Implemented

Protocol Negotiation

Component Status Notes
multistream-select v1 ✅ Implemented 20 tests
multistream-select v1 Lazy (0-RTT) ❌ Not implemented

Discovery

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

Protocols

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 ⚠️ Partial 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

NAT Traversal

Component Status Notes
NAT port mapping (UPnP + NAT-PMP) ✅ Implemented macOS-only gateway detection
Circuit Relay v2 (client + server) ✅ Implemented
AutoNAT ✅ Implemented
DCUtR ✅ Implemented

Integration

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

Requirements

  • Swift 6.2+
  • macOS 15+ / iOS 18+ / tvOS 18+ / watchOS 11+ / visionOS 2+

Installation

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"
    ]
)

Quick Start

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

Architecture

+----------------------------------------------------------------------+
|  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 Structure

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

Wire Protocol Compatibility

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

Design Principles

Modern Swift Idioms

  • 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

Concurrency Model

Use Case Pattern
User-facing APIs actor
High-frequency internal state class + Mutex<T>
Data containers struct

EventEmitting Pattern

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 automatically

Important: Always call shutdown() when done with a service to prevent for await from hanging.

Dependency Injection

All components are injected via protocols:

let node = Node(configuration: NodeConfiguration(
    keyPair: myKeyPair,
    transports: [TCPTransport()],      // Injectable
    security: [NoiseUpgrader()],       // Injectable
    muxers: [YamuxMuxer()]             // Injectable
))

Configuration

Connection Limits

let limits = ConnectionLimits(
    maxConnections: 100,
    maxConnectionsPerPeer: 2,
    idleTimeout: .seconds(60)
)

Reconnection Policy

let policy = ReconnectionPolicy(
    enabled: true,
    maxAttempts: 5,
    backoff: .exponential(base: .seconds(1), factor: 2.0, maxDelay: .seconds(60))
)

Connection Gating

let gater = ConnectionGater(
    allowlist: [trustedPeerID],
    denylist: [blockedPeerID],
    customFilter: { peer, direction in
        // Custom filtering logic
        return true
    }
)

Events

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 Transport

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 Transport

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

NAT Traversal with Circuit Relay

Circuit Relay v2 enables peers behind NATs to communicate through public relay nodes.

Making a Reservation

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

Connecting Through a Relay

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

Running a Relay Server

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

Relayed addresses use the p2p-circuit protocol:

/ip4/1.2.3.4/tcp/4001/p2p/{relay-peer-id}/p2p-circuit/p2p/{target-peer-id}

Testing

Unit Tests

swift test

Integration Tests with Other Implementations

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

Dependencies

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

References

License

MIT License

Description

  • Swift Tools
View More Packages from this Author

Dependencies

  • None
Last updated: Tue Feb 03 2026 17:12:29 GMT-1000 (Hawaii-Aleutian Standard Time)