libp2p.transport.webrtc package

Subpackages

Submodules

libp2p.transport.webrtc.certificate module

WebRTC certificate utilities.

Generates ECDSA P-256 self-signed certificates for WebRTC DTLS and computes SHA-256 fingerprints encoded as multihash/multibase for embedding in /webrtc-direct/certhash/<encoded> multiaddrs.

Spec: https://github.com/libp2p/specs/blob/master/webrtc/webrtc-direct.md

class libp2p.transport.webrtc.certificate.WebRTCCertificate(certificate: Certificate, private_key: EllipticCurvePrivateKey)

Bases: object

Holds an ECDSA P-256 certificate and its SHA-256 fingerprint for WebRTC.

Use generate() to create a fresh self-signed certificate, or from_existing() to wrap an already-created certificate/key pair.

certificate_der() bytes

Certificate in DER encoding.

property fingerprint: bytes

Raw SHA-256 fingerprint of the DER-encoded certificate.

property fingerprint_hex: str

Colon-separated hex fingerprint for SDP a=fingerprint lines.

fingerprint_to_multibase() str

Encode the fingerprint as a multibase base64url string.

The result can be used directly as the certhash component in a /webrtc-direct multiaddr.

Format: u prefix + base64url(multihash(sha256(DER cert)))

fingerprint_to_multihash() bytes

Encode the fingerprint as a multihash (varint code + varint length + digest).

For SHA-256 both code (0x12) and length (32) fit in a single byte, so we avoid a full varint encoder.

classmethod from_aiortc() WebRTCCertificate

Generate a certificate using aiortc’s RTCCertificate.

Preferred when aiortc is installed because it avoids any cryptography ↔ pyOpenSSL conversion — aiortc’s internal cert is already a cryptography.x509.Certificate.

Returns:

A new WebRTCCertificate backed by an aiortc cert.

Raises:
classmethod generate(common_name: str = 'libp2p-webrtc', validity_days: int = 14) WebRTCCertificate

Generate a fresh ECDSA P-256 self-signed certificate.

The spec requires ECDSA with the P-256 curve for browser compatibility.

Parameters:
  • common_name – Certificate subject CN.

  • validity_days – How long the certificate is valid.

Returns:

A new WebRTCCertificate.

Raises:

WebRTCCertificateError – If certificate generation fails.

private_key_der() bytes

Private key in DER encoding (PKCS8, unencrypted).

libp2p.transport.webrtc.certificate.fingerprint_from_multibase(encoded: str) bytes

Decode a multibase-encoded certhash back to raw SHA-256 fingerprint bytes.

Parameters:

encoded – Multibase string (e.g. uEi...).

Returns:

32-byte SHA-256 digest.

Raises:

WebRTCCertificateError – If the encoding is invalid.

libp2p.transport.webrtc.config module

WebRTC transport configuration.

class libp2p.transport.webrtc.config.WebRTCTransportConfig(certificate: ~libp2p.transport.webrtc.certificate.WebRTCCertificate | None = None, ice_disconnection_timeout: float = 20.0, ice_failure_timeout: float = 30.0, ice_keepalive_interval: float = 15.0, handshake_timeout: float = 30.0, stream_open_timeout: float = 10.0, stream_accept_timeout: float = 10.0, max_in_flight_connections: int = 128, accept_queue_size: int = 256, max_concurrent_streams: int = 256, max_message_size: int = 16384, ice_servers: list[str] = <factory>)

Bases: object

Configuration for the WebRTC transport.

Sensible defaults match go-libp2p. Override for testing or constrained environments.

accept_queue_size: int = 256
certificate: WebRTCCertificate | None = None
get_or_generate_certificate() WebRTCCertificate

Return the configured certificate or generate a new one.

Prefers aiortc-native generation when available so the resulting RTCCertificate can be passed directly to RTCPeerConnection(certificates=[...]). Falls back to pure cryptography generation when aiortc is not installed.

handshake_timeout: float = 30.0
ice_disconnection_timeout: float = 20.0
ice_failure_timeout: float = 30.0
ice_keepalive_interval: float = 15.0
ice_servers: list[str]
max_concurrent_streams: int = 256
max_in_flight_connections: int = 128
max_message_size: int = 16384
stream_accept_timeout: float = 10.0
stream_open_timeout: float = 10.0

libp2p.transport.webrtc.connection module

WebRTC connection — dual IRawConnection + IMuxedConn interface.

Follows the same pattern as QUICConnection: WebRTC provides native stream multiplexing via data channels, so the connection implements both the raw transport and the muxer interface. The swarm skips the TransportUpgrader for native-muxing transports.

Each outbound stream gets an even data-channel ID starting at 2. Each inbound stream gets an odd data-channel ID starting at 1. Channel 0 is reserved for the Noise handshake.

Spec: https://github.com/libp2p/specs/blob/master/webrtc/webrtc.md

class libp2p.transport.webrtc.connection.WebRTCConnection(peer_id: ID, bridge: AsyncioBridge, is_initiator: bool, config: WebRTCTransportConfig | None = None, remote_addrs: list[Multiaddr] | None = None)

Bases: IRawConnection, IMuxedConn

A WebRTC peer connection providing native stream multiplexing.

Wraps an aiortc RTCPeerConnection (via AsyncioBridge) and maps each data channel to a WebRTCStream.

This class does NOT import or call aiortc directly. All aiortc interaction happens through the bridge and the _send_on_channel / _create_channel / _close_pc callbacks set by the transport. This keeps the connection testable without aiortc installed.

async accept_stream() IMuxedStream

Accept an inbound stream (waits for a remote data channel).

Returns:

A WebRTCStream.

Raises:

WebRTCStreamError – If the connection is closed.

async close() None

Close the peer connection and all streams.

get_connection_type() ConnectionType

Get the type of connection (direct, relayed, etc.)

get_remote_address() tuple[str, int] | None

Return the remote address of the connected peer.

Returns:

A tuple of (host, port) or None if not available

get_transport_addresses() list[Multiaddr]

Get the actual transport addresses used by this connection.

Returns the real IP/port addresses, not peerstore addresses. For relayed connections, should include /p2p-circuit in the path.

property is_closed: bool

Check if the connection is fully closed.

Returns:

True if the connection is closed, otherwise False.

property is_established: bool

Check if the connection is fully established and ready for streams.

Returns:

True if the connection is established, otherwise False.

property is_initiator: bool

Determine if this connection is the initiator.

Returns:

True if this connection initiated the connection, otherwise False.

on_channel_closed(channel_id: int) None

Handle data-channel close event (safe from any thread).

on_channel_message(channel_id: int, data: bytes) None

Route a received data-channel message to the correct stream.

Stream-level routing is done on whatever thread we’re called from; the stream itself (WebRTCStream.on_data()) handles the foreign-thread hand-off to Trio.

on_datachannel(channel_id: int) WebRTCStream

Register an inbound data channel as a new stream.

Called by the transport layer when a remote peer creates a data channel. May be invoked from the asyncio bridge thread; any Trio-side enqueueing is routed through _run_on_trio_thread().

Parameters:

channel_id – The data channel ID.

Returns:

The created WebRTCStream.

async open_stream() IMuxedStream

Open a new outbound stream (creates a WebRTC data channel).

Returns:

A WebRTCStream ready for reading/writing.

Raises:

WebRTCStreamError – If the connection is closed or stream limit is reached.

async read(n: int | None = None) bytes
remove_stream(channel_id: int) None

Remove a stream from the registry (called on cleanup).

async start() None

Mark the connection as started. Called after Noise handshake.

async write(data: bytes) None

libp2p.transport.webrtc.constants module

WebRTC transport constants.

Protocol codes, message size limits, and data-channel ID allocation rules per the libp2p WebRTC specification.

Spec: https://github.com/libp2p/specs/tree/master/webrtc

libp2p.transport.webrtc.exceptions module

WebRTC transport exception hierarchy.

WebRTCConnectionError also subclasses OpenConnectionError so that generic transport error handling in the swarm layer catches WebRTC dial failures the same way it catches TCP/QUIC failures.

exception libp2p.transport.webrtc.exceptions.WebRTCCertificateError

Bases: WebRTCError

Certificate generation, parsing, or fingerprint errors.

exception libp2p.transport.webrtc.exceptions.WebRTCConnectionError

Bases: WebRTCError, OpenConnectionError

ICE negotiation, DTLS, or peer connection failure.

exception libp2p.transport.webrtc.exceptions.WebRTCError

Bases: BaseLibp2pError

Base exception for all WebRTC transport errors.

exception libp2p.transport.webrtc.exceptions.WebRTCHandshakeError

Bases: WebRTCError

Noise handshake failure over data-channel 0.

exception libp2p.transport.webrtc.exceptions.WebRTCMultiaddrError

Bases: WebRTCError

Invalid or unparseable WebRTC multiaddr.

exception libp2p.transport.webrtc.exceptions.WebRTCSignalingError

Bases: WebRTCError

SDP/ICE signaling exchange failure (private-to-private mode).

exception libp2p.transport.webrtc.exceptions.WebRTCStreamError

Bases: WebRTCError

Data-channel stream read/write or lifecycle error.

libp2p.transport.webrtc.listener module

WebRTC Direct listener.

Runs a lightweight HTTP signaling server on TCP (same port number as the WebRTC UDP endpoint) that accepts SDP offers and returns answers. After the SDP exchange each incoming connection completes ICE/DTLS, a Noise XX handshake over data-channel 0, and then hands the fully-authenticated WebRTCConnection to the registered handler.

Published multiaddr format:

/ip4/<bound-ip>/udp/<bound-port>/webrtc-direct/certhash/<hash>/p2p/<peer-id>

Spec: https://github.com/libp2p/specs/blob/master/webrtc/webrtc-direct.md

class libp2p.transport.webrtc.listener.WebRTCDirectListener(handler_function: Callable[[ReadWriteCloser], Awaitable[None]], private_key: PrivateKey, certificate: WebRTCCertificate, config: WebRTCTransportConfig, bridge_factory: Callable[[], Awaitable[Any]], local_peer_id: ID)

Bases: IListener

Listens for incoming WebRTC Direct connections.

Created by WebRTCDirectTransport.create_listener().

async close() None

Stop listening and close all accepted connections.

get_addrs() tuple[Multiaddr, ...]

Return the listening multiaddrs (includes certhash and peer ID).

async listen(maddr: Multiaddr) None

Start listening for incoming WebRTC Direct connections.

Starts an HTTP signaling server on TCP that accepts SDP offers. The published multiaddr advertises the same port on UDP (for WebRTC data channels) and includes the DTLS certificate hash.

Parameters:

maddr – A /webrtc-direct multiaddr.

Raises:

WebRTCConnectionError – If binding fails.

libp2p.transport.webrtc.multiaddr_utils module

WebRTC multiaddr utilities.

Parse and construct /webrtc-direct and /webrtc multiaddrs.

WebRTC Direct format:

/ip4/<ip>/udp/<port>/webrtc-direct/certhash/<multibase-multihash>/p2p/<peer-id>

WebRTC (relay-based) format:

<relay-multiaddr>/p2p-circuit/webrtc/p2p/<peer-id>

Spec: https://github.com/libp2p/specs/blob/master/webrtc/webrtc-direct.md

libp2p.transport.webrtc.multiaddr_utils.build_webrtc_direct_multiaddr(host: str, port: int, certhash_multibase: str, peer_id: str | None = None) Multiaddr

Construct a /webrtc-direct multiaddr.

Parameters:
  • host – IPv4 or IPv6 address string.

  • port – UDP port number.

  • certhash_multibase – Multibase-encoded certificate hash (e.g. uEi...).

  • peer_id – Optional base58 peer ID.

Returns:

A Multiaddr.

Raises:

WebRTCMultiaddrError – If inputs are invalid.

libp2p.transport.webrtc.multiaddr_utils.is_webrtc_direct_multiaddr(maddr: Multiaddr) bool

Check whether maddr is a valid WebRTC Direct address.

Parameters:

maddr – Multiaddr to test.

Returns:

True if the address contains /webrtc-direct.

libp2p.transport.webrtc.multiaddr_utils.is_webrtc_multiaddr(maddr: Multiaddr) bool

Check whether maddr is a relay-based WebRTC address.

A valid address contains /p2p-circuit/webrtc/.

libp2p.transport.webrtc.multiaddr_utils.parse_webrtc_direct_multiaddr(maddr: Multiaddr) tuple[str, int, str | None, str | None]

Extract components from a /webrtc-direct multiaddr.

Parameters:

maddr – A WebRTC Direct multiaddr.

Returns:

Tuple of (host, port, certhash_multibase, peer_id_str), where the last two may be None if absent in the multiaddr.

Raises:

WebRTCMultiaddrError – If the multiaddr is malformed.

libp2p.transport.webrtc.noise_handshake module

Noise XX handshake over WebRTC data channel 0.

Per the libp2p WebRTC spec, after a DTLS connection is established the two peers perform a Noise XX handshake over data channel 0 to mutually authenticate. The Noise prologue binds the handshake to the DTLS session by incorporating both peers’ certificate fingerprints.

Prologue format:

b"libp2p-webrtc-noise:" + encode(local_fp) + encode(remote_fp)

Where encode(fp) is the multihash-encoded SHA-256 fingerprint of the peer’s DTLS certificate.

Spec: https://github.com/libp2p/specs/blob/master/webrtc/webrtc.md#noise-handshake

class libp2p.transport.webrtc.noise_handshake.DataChannelReadWriter(send_cb: Callable[[bytes], Awaitable[None]], recv_cb: Callable[[], Awaitable[bytes]], is_initiator: bool)

Bases: IRawConnection

Wraps a WebRTC data channel (stream) as an IRawConnection so the existing Noise handshake code (PatternXX) can read/write over it without modification.

The data channel is represented by send_cb and recv_cb callables rather than a direct aiortc reference.

async close() None

No-op — the channel lifecycle is managed by the connection.

get_connection_type() ConnectionType

Get the type of connection (direct, relayed, etc.)

get_remote_address() tuple[str, int] | None

Return the remote address of the connected peer.

Returns:

A tuple of (host, port) or None if not available

get_transport_addresses() list[Multiaddr]

Get the actual transport addresses used by this connection.

Returns the real IP/port addresses, not peerstore addresses. For relayed connections, should include /p2p-circuit in the path.

async read(n: int | None = None) bytes

Read the next message from the data channel.

async write(data: bytes) None

Write a message to the data channel.

libp2p.transport.webrtc.noise_handshake.build_noise_prologue(local_fingerprint: bytes, remote_fingerprint: bytes) bytes

Build the Noise prologue that binds the handshake to the DTLS session.

Parameters:
  • local_fingerprint – Raw SHA-256 of the local DTLS certificate.

  • remote_fingerprint – Raw SHA-256 of the remote DTLS certificate.

Returns:

The prologue bytes for NoiseState.set_prologue().

async libp2p.transport.webrtc.noise_handshake.perform_noise_handshake(conn: IRawConnection, local_peer: ID, libp2p_privkey: PrivateKey, noise_static_key: PrivateKey, local_fingerprint: bytes, remote_fingerprint: bytes, is_initiator: bool, remote_peer: ID | None = None) ID

Run the Noise XX handshake over a data-channel-0 connection.

Parameters:
  • conn – A IRawConnection wrapping data channel 0.

  • local_peer – The local peer’s ID.

  • libp2p_privkey – The local peer’s libp2p identity private key.

  • noise_static_key – An ephemeral X25519 key for the Noise session.

  • local_fingerprint – Raw SHA-256 of the local DTLS certificate.

  • remote_fingerprint – Raw SHA-256 of the remote DTLS certificate.

  • is_initiator – True if this peer initiated the connection.

  • remote_peer – Expected remote peer ID (for outbound connections).

Returns:

The authenticated remote peer ID.

Raises:

WebRTCHandshakeError – If the handshake fails.

libp2p.transport.webrtc.private_listener module

WebRTC private-to-private listener.

Registers as a stream handler for /webrtc-signaling/0.0.1 on the local host. When a remote peer sends a signaling stream through a relay, this listener handles the SDP exchange, establishes a direct WebRTC connection, and calls the handler function.

The listener advertises multiaddrs of the form:

<relay-multiaddr>/p2p-circuit/webrtc/p2p/<local-peer-id>

Spec: https://github.com/libp2p/specs/blob/master/webrtc/webrtc.md

class libp2p.transport.webrtc.private_listener.WebRTCPrivateListener(handler_function: Callable[[ReadWriteCloser], Awaitable[None]], private_key: PrivateKey, certificate: WebRTCCertificate, config: WebRTCTransportConfig, bridge_factory: Callable[[], Awaitable[Any]], local_peer_id: ID, host: object | None = None)

Bases: IListener

Listens for incoming WebRTC signaling over Circuit Relay v2.

Created by WebRTCPrivateTransport.create_listener().

async close() None

Stop listening and deregister the stream handler.

get_addrs() tuple[Multiaddr, ...]

Return the listening multiaddrs.

async listen(maddr: Multiaddr) None

Start listening for WebRTC signaling.

Registers a stream handler for /webrtc-signaling/0.0.1 on the host. The multiaddr should be a relay address ending with /webrtc.

Parameters:

maddr – A /p2p-circuit/webrtc multiaddr.

libp2p.transport.webrtc.private_transport module

WebRTC private-to-private transport.

Implements ITransport for the /webrtc multiaddr scheme where both peers are behind NAT. Uses Circuit Relay v2 for the signaling channel, then upgrades to a direct WebRTC data-channel connection.

Multiaddr format:

<relay-multiaddr>/p2p-circuit/webrtc/p2p/<remote-peer-id>

The dial sequence:

  1. Open a relayed connection to the remote peer.

  2. Open a stream with /webrtc-signaling/0.0.1.

  3. Exchange SDP offer/answer via SignalingSession.

  4. Trickle ICE candidates with bilateral ICE_DONE (specs#585 fix).

  5. Wait for direct WebRTC connection to establish.

  6. Perform Noise XX handshake over data channel 0.

  7. Return WebRTCConnection.

Spec: https://github.com/libp2p/specs/blob/master/webrtc/webrtc.md

class libp2p.transport.webrtc.private_transport.WebRTCPrivateTransport(private_key: PrivateKey, host: object | None = None, config: WebRTCTransportConfig | None = None)

Bases: ITransport

WebRTC transport for private-to-private connections (/webrtc).

Both peers are behind NAT. Signaling happens over a Circuit Relay v2 stream, then a direct WebRTC connection is established.

Usage:

transport = WebRTCPrivateTransport(private_key=my_key, host=my_host)
conn = await transport.dial(
    Multiaddr("/ip4/.../udp/.../quic-v1/p2p/<relay>/p2p-circuit/webrtc/p2p/<remote>")
)
can_dial(maddr: Multiaddr) bool

Return True if this transport can dial the given multiaddr.

The TransportManager calls this method before attempting a dial to route the connection to the correct transport.

Parameters

maddrMultiaddr

The multiaddress to check.

Returns

bool

True if this transport can dial maddr, False otherwise.

Examples

  • TCP returns True for /ip4/127.0.0.1/tcp/4001

  • WebSocket returns True for /ip4/127.0.0.1/tcp/8080/ws

  • QUIC returns True for /ip4/127.0.0.1/udp/4001/quic-v1

can_listen(maddr: Multiaddr) bool

Return True if this transport can listen on the given multiaddr.

Often identical to can_dial() but may differ — e.g. a relay transport can dial outbound but cannot accept inbound connections.

Parameters

maddrMultiaddr

The multiaddress to check.

Returns

bool

True if this transport can listen on maddr, False otherwise.

async close() None

Shut down the transport and its asyncio bridge.

Acquires the same lock as _ensure_bridge() so a concurrent dial cannot resurrect the bridge mid-shutdown.

create_listener(handler_function: Callable[[ReadWriteCloser], Awaitable[None]]) WebRTCPrivateListener

Create a listener for incoming WebRTC signaling.

The listener registers a stream handler for /webrtc-signaling/0.0.1 on the host so that remote peers can initiate WebRTC connections through a relay.

Parameters:

handler_function – Called with each new inbound connection.

Returns:

A WebRTCPrivateListener.

async dial(maddr: Multiaddr) WebRTCConnection

Dial a remote peer over WebRTC via a relay.

Parameters:

maddr – A /p2p-circuit/webrtc/p2p/<peer-id> multiaddr.

Returns:

A WebRTCConnection.

Raises:
  • NotImplementedError – The aiortc / signaling integration is not yet wired up. Returning a bare WebRTCConnection here would make the swarm treat the peer as connected while streams silently drop data. The full sequence (relay dial, SDP/ICE signaling with bilateral ICE_DONE, Noise handshake) lands in a follow-up PR.

  • WebRTCConnectionError – If the multiaddr is malformed.

protocols() list[str]

Return the list of multiaddr protocol names this transport handles.

Used by TransportManager as a fast pre-filter: if the multiaddr contains none of the listed protocol names, can_dial / can_listen are not called.

Returns

list[str]

Protocol name strings, e.g. ["tcp"], ["ws", "wss"], or ["quic", "quic-v1"].

provides_native_muxing: bool = True

libp2p.transport.webrtc.sdp module

SDP construction for WebRTC Direct.

For WebRTC Direct, there is no signaling exchange — the client constructs an SDP offer locally from the server’s multiaddr (IP, port, certificate hash). The server answers with its own locally-constructed SDP.

All ICE credential injection is isolated in SDPBuilder._apply_ice_credentials() so that when Chrome removes ICE credential munging (libp2p/specs#672) only that single method needs to change.

Spec: https://github.com/libp2p/specs/blob/master/webrtc/webrtc-direct.md

class libp2p.transport.webrtc.sdp.SDPBuilder(certificate: WebRTCCertificate, max_message_size: int = 16384)

Bases: object

Builds SDP offer/answer for WebRTC Direct connections.

Usage:

builder = SDPBuilder(certificate=my_cert)
offer_sdp = builder.build_offer(host="127.0.0.1", port=9090)
answer_sdp = builder.build_answer(
    host="127.0.0.1", port=9090, remote_ufrag="...", remote_pwd="..."
)
build_answer(host: str, port: int, remote_ufrag: str, remote_pwd: str) tuple[str, str, str]

Build an SDP answer in response to a remote offer.

Per RFC 8445 §5.2, the answer includes its own fresh ufrag/pwd for connectivity checks. The remote credentials are passed through to _apply_ice_credentials for the ICE agent to use during connectivity checks (needed when specs#672 changes the credential injection mechanism).

Parameters:
  • host – Local listening IP address.

  • port – Local listening UDP port.

  • remote_ufrag – ICE ufrag from the remote offer.

  • remote_pwd – ICE pwd from the remote offer.

Returns:

Tuple of (sdp_string, local_ice_ufrag, local_ice_pwd).

build_offer(host: str, port: int) tuple[str, str, str]

Build an SDP offer for initiating a WebRTC Direct connection.

Parameters:
  • host – Remote server IP address.

  • port – Remote server UDP port.

Returns:

Tuple of (sdp_string, ice_ufrag, ice_pwd).

static build_sdp_from_multiaddr(host: str, port: int, certhash_multibase: str) str

Build a server-side SDP from multiaddr components for WebRTC Direct.

For WebRTC Direct the client knows the server’s cert hash from the multiaddr. This constructs the SDP that the server would advertise.

Parameters:
  • host – Server IP address.

  • port – Server UDP port.

  • certhash_multibase – Multibase-encoded certificate fingerprint.

Returns:

SDP string.

property local_fingerprint_bytes: bytes

Raw SHA-256 fingerprint of the local certificate.

property local_fingerprint_hex: str

Colon-separated hex fingerprint for SDP lines.

libp2p.transport.webrtc.sdp.fingerprint_from_sdp(sdp: str) bytes

Extract the DTLS certificate fingerprint from an SDP string.

Looks for the a=fingerprint:sha-256 line and parses the colon-separated hex digest.

Parameters:

sdp – SDP string.

Returns:

Raw SHA-256 fingerprint bytes (32 bytes).

Raises:

WebRTCConnectionError – If no valid fingerprint line found.

libp2p.transport.webrtc.signaling module

WebRTC signaling protocol for private-to-private connections.

Implements /webrtc-signaling/0.0.1 — the protocol used to exchange SDP offers/answers and ICE candidates over a Circuit Relay v2 stream so that two NATed peers can establish a direct WebRTC data-channel connection.

The bilateral ICE_DONE mechanism (libp2p/specs#585 fix) ensures that neither side closes the signaling stream before the other has received all ICE candidates:

Initiator                              Responder (via relay)
  ──── SDP_OFFER ─────────────────────────>
  <─── SDP_ANSWER ─────────────────────────
  <──> ICE_CANDIDATE (trickle, both ways) <>
  ──── ICE_DONE ───────────────────────────>
  <─── ICE_DONE ───────────────────────────
  (both sides close signaling stream)

Messages are varint-length-prefixed protobuf SignalingMessage.

Spec: https://github.com/libp2p/specs/blob/master/webrtc/webrtc.md

class libp2p.transport.webrtc.signaling.SignalingSession(stream: INetStream, timeout: float = 30.0)

Bases: object

Manages a signaling exchange between two peers.

Handles the ordered message flow: SDP_OFFER → SDP_ANSWER → ICE candidates (trickle) → bilateral ICE_DONE.

Usage (initiator side):

session = SignalingSession(stream)
await session.send_offer(sdp_offer_bytes)
answer_bytes = await session.receive_answer()
async for candidate in session.receive_candidates():
    # apply candidate to RTCPeerConnection
    pass
await session.send_candidates(my_candidates)
await session.complete()  # bilateral ICE_DONE

Usage (responder side):

session = SignalingSession(stream)
offer_bytes = await session.receive_offer()
await session.send_answer(sdp_answer_bytes)
async for candidate in session.receive_candidates():
    pass
await session.send_candidates(my_candidates)
await session.complete()
async complete() None

Complete the signaling exchange.

Ensures both sides have sent AND received ICE_DONE before closing the stream. This prevents the race condition in specs#585 where one side closes the stream before the other has received all candidates.

async receive_answer() bytes

Wait for and return the SDP answer.

async receive_candidates() AsyncIterator[bytes]

Yield ICE candidates from the remote peer until ICE_DONE is received.

This is an async generator — iterate it to get candidates as they arrive. The generator completes when the remote sends ICE_DONE.

async receive_offer() bytes

Wait for and return the SDP offer.

async send_answer(sdp: bytes) None

Send an SDP answer.

async send_candidates(candidates: list[bytes]) None

Send all gathered ICE candidates, then send ICE_DONE.

Parameters:

candidates – List of serialized ICE candidate strings.

async send_offer(sdp: bytes) None

Send an SDP offer.

async libp2p.transport.webrtc.signaling.read_signaling_message(stream: INetStream) SignalingMessage

Read a varint-length-prefixed signaling message from the stream.

Parameters:

stream – The relay stream.

Returns:

The parsed protobuf message.

Raises:

WebRTCSignalingError – If reading or parsing fails.

async libp2p.transport.webrtc.signaling.write_signaling_message(stream: INetStream, msg: SignalingMessage) None

Write a varint-length-prefixed signaling message to the stream.

Parameters:
  • stream – The relay stream.

  • msg – The protobuf message to send.

Raises:

WebRTCSignalingError – If writing fails.

libp2p.transport.webrtc.stream module

WebRTC data-channel stream.

Each libp2p stream maps to one WebRTC data channel. Every write is wrapped in a protobuf Message with an optional Flag for lifecycle signaling. The FIN/FIN_ACK/STOP_SENDING/RESET state machine follows the libp2p WebRTC specification exactly.

Spec: https://github.com/libp2p/specs/blob/master/webrtc/webrtc.md

class libp2p.transport.webrtc.stream.StreamState(value)

Bases: Enum

Data-channel stream lifecycle states.

CLOSED = 'closed'
OPEN = 'open'
READ_CLOSED = 'read_closed'
RESET = 'reset'
WRITE_CLOSED = 'write_closed'
class libp2p.transport.webrtc.stream.WebRTCStream(connection: WebRTCConnection, channel_id: int, is_initiator: bool, trio_token: trio.lowlevel.TrioToken | None = None)

Bases: IMuxedStream

A single multiplexed stream over a WebRTC data channel.

Implements IMuxedStream with protobuf framing and the FIN/FIN_ACK lifecycle protocol from the spec.

The stream does not interact with aiortc directly — it sends and receives raw bytes through callbacks registered by WebRTCConnection. This keeps the stream logic testable without an aiortc dependency.

property channel_id: int

The WebRTC data channel ID for this stream.

async close() None

Gracefully close the stream (both read and write sides).

Sends FIN, waits for FIN_ACK (with timeout), then closes.

get_remote_address() tuple[str, int] | None

Delegate to the connection (data channels share its address).

on_channel_close() None

Called when the underlying data channel is closed.

on_data(raw: bytes) None

Called by WebRTCConnection when the data channel receives an SCTP message.

Per the libp2p WebRTC spec each SCTP message carries one uvarint length-prefixed protobuf Message. We decode the length prefix, parse the protobuf, process any flag, and enqueue payload bytes for read().

May be invoked from the asyncio bridge thread (not a Trio task). To stay safe we route every Trio primitive call (memory channel, trio.Event) through trio.from_thread.run_sync() with a captured trio.lowlevel.TrioToken. When called from within a Trio task (for example in unit tests) we execute the mutations inline.

async read(n: int | None = None) bytes

Read up to n bytes from the stream.

Blocks until data is available, the remote sends FIN, or the stream is reset.

Parameters:

n – Maximum bytes to return. None returns whatever is available in the next message.

Returns:

The bytes read (may be shorter than n).

Raises:

WebRTCStreamError – If the stream was reset or closed.

async reset() None

Abruptly terminate the stream.

Sends RESET and immediately tears down without waiting for acknowledgement.

set_deadline(ttl: int) None

Set a deadline for future read operations.

Parameters:

ttl – Seconds from now. 0 removes the deadline.

async write(data: bytes) None

Write data to the stream, length-prefixed-protobuf framed.

Large writes are split into chunks of at most MAX_PAYLOAD_SIZE bytes so the full framed wire message (uvarint length prefix + protobuf) stays within MAX_MESSAGE_SIZE.

Raises:

WebRTCStreamError – If the write side is closed or reset.

libp2p.transport.webrtc.transport module

WebRTC Direct transport.

Implements ITransport for the /webrtc-direct multiaddr scheme. The server publishes its DTLS certificate hash in the multiaddr; the client constructs an SDP locally — no signaling exchange is needed.

This transport provides native stream multiplexing (data channels), so it sets provides_native_muxing = True and the swarm skips the TransportUpgrader.

Spec: https://github.com/libp2p/specs/blob/master/webrtc/webrtc-direct.md

class libp2p.transport.webrtc.transport.WebRTCDirectTransport(private_key: PrivateKey, config: WebRTCTransportConfig | None = None)

Bases: ITransport

WebRTC Direct transport (/webrtc-direct).

Usage:

transport = WebRTCDirectTransport(private_key=my_key)
# Dial a remote peer
conn = await transport.dial(
    Multiaddr("/ip4/1.2.3.4/udp/9090/webrtc-direct/certhash/uEi.../p2p/12D3...")
)
# Or create a listener
listener = transport.create_listener(handler)
await listener.listen(Multiaddr("/ip4/0.0.0.0/udp/9090/webrtc-direct"))
can_dial(maddr: Multiaddr) bool

Return True if this transport can dial the given multiaddr.

The TransportManager calls this method before attempting a dial to route the connection to the correct transport.

Parameters

maddrMultiaddr

The multiaddress to check.

Returns

bool

True if this transport can dial maddr, False otherwise.

Examples

  • TCP returns True for /ip4/127.0.0.1/tcp/4001

  • WebSocket returns True for /ip4/127.0.0.1/tcp/8080/ws

  • QUIC returns True for /ip4/127.0.0.1/udp/4001/quic-v1

can_listen(maddr: Multiaddr) bool

Return True if this transport can listen on the given multiaddr.

Often identical to can_dial() but may differ — e.g. a relay transport can dial outbound but cannot accept inbound connections.

Parameters

maddrMultiaddr

The multiaddress to check.

Returns

bool

True if this transport can listen on maddr, False otherwise.

property certificate: WebRTCCertificate

The local DTLS certificate.

async close() None

Shut down the transport and its asyncio bridge.

Acquires the same lock as _ensure_bridge() so a concurrent dial cannot resurrect the bridge mid-shutdown.

create_listener(handler_function: Callable[[ReadWriteCloser], Awaitable[None]]) WebRTCDirectListener

Create a WebRTC Direct listener.

Parameters:

handler_function – Called with each new inbound connection.

Returns:

A WebRTCDirectListener.

async dial(maddr: Multiaddr) WebRTCConnection

Dial a remote peer over WebRTC Direct.

Parameters:

maddr – A /webrtc-direct multiaddr with certhash.

Returns:

A WebRTCConnection (implements both IRawConnection and IMuxedConn).

Raises:

WebRTCConnectionError – If the connection fails.

protocols() list[str]

Return the list of multiaddr protocol names this transport handles.

Used by TransportManager as a fast pre-filter: if the multiaddr contains none of the listed protocol names, can_dial / can_listen are not called.

Returns

list[str]

Protocol name strings, e.g. ["tcp"], ["ws", "wss"], or ["quic", "quic-v1"].

provides_native_muxing: bool = True

Module contents

WebRTC transport for libp2p.

Provides two transport variants per the libp2p WebRTC specification:

  • WebRTC Direct (/webrtc-direct): Server-to-browser or server-to-server connections where the server publishes its certificate hash in the multiaddr. No relay or signaling server is required.

  • WebRTC (/webrtc): Private-to-private connections where both peers are behind NAT. Uses Circuit Relay v2 for signaling, then upgrades to a direct WebRTC data-channel connection.

Both variants use Noise XX over data-channel 0 for authentication and rely on WebRTC data channels for native stream multiplexing (no Yamux/Mplex needed).

Spec: https://github.com/libp2p/specs/tree/master/webrtc

class libp2p.transport.webrtc.WebRTCCertificate(certificate: Certificate, private_key: EllipticCurvePrivateKey)

Bases: object

Holds an ECDSA P-256 certificate and its SHA-256 fingerprint for WebRTC.

Use generate() to create a fresh self-signed certificate, or from_existing() to wrap an already-created certificate/key pair.

certificate_der() bytes

Certificate in DER encoding.

property fingerprint: bytes

Raw SHA-256 fingerprint of the DER-encoded certificate.

property fingerprint_hex: str

Colon-separated hex fingerprint for SDP a=fingerprint lines.

fingerprint_to_multibase() str

Encode the fingerprint as a multibase base64url string.

The result can be used directly as the certhash component in a /webrtc-direct multiaddr.

Format: u prefix + base64url(multihash(sha256(DER cert)))

fingerprint_to_multihash() bytes

Encode the fingerprint as a multihash (varint code + varint length + digest).

For SHA-256 both code (0x12) and length (32) fit in a single byte, so we avoid a full varint encoder.

classmethod from_aiortc() WebRTCCertificate

Generate a certificate using aiortc’s RTCCertificate.

Preferred when aiortc is installed because it avoids any cryptography ↔ pyOpenSSL conversion — aiortc’s internal cert is already a cryptography.x509.Certificate.

Returns:

A new WebRTCCertificate backed by an aiortc cert.

Raises:
classmethod generate(common_name: str = 'libp2p-webrtc', validity_days: int = 14) WebRTCCertificate

Generate a fresh ECDSA P-256 self-signed certificate.

The spec requires ECDSA with the P-256 curve for browser compatibility.

Parameters:
  • common_name – Certificate subject CN.

  • validity_days – How long the certificate is valid.

Returns:

A new WebRTCCertificate.

Raises:

WebRTCCertificateError – If certificate generation fails.

private_key_der() bytes

Private key in DER encoding (PKCS8, unencrypted).

exception libp2p.transport.webrtc.WebRTCCertificateError

Bases: WebRTCError

Certificate generation, parsing, or fingerprint errors.

exception libp2p.transport.webrtc.WebRTCConnectionError

Bases: WebRTCError, OpenConnectionError

ICE negotiation, DTLS, or peer connection failure.

exception libp2p.transport.webrtc.WebRTCError

Bases: BaseLibp2pError

Base exception for all WebRTC transport errors.

exception libp2p.transport.webrtc.WebRTCHandshakeError

Bases: WebRTCError

Noise handshake failure over data-channel 0.

exception libp2p.transport.webrtc.WebRTCMultiaddrError

Bases: WebRTCError

Invalid or unparseable WebRTC multiaddr.

exception libp2p.transport.webrtc.WebRTCSignalingError

Bases: WebRTCError

SDP/ICE signaling exchange failure (private-to-private mode).

exception libp2p.transport.webrtc.WebRTCStreamError

Bases: WebRTCError

Data-channel stream read/write or lifecycle error.