# WebRTC (Browser-to-Server) in libp2p

This is the second entry in the Universal Browser Connectivity series on how libp2p achieves browser connectivity. Read about WebTransport in the first post (opens new window).

# Overview

The libp2p project (opens new window) supports many transport protocols (opens new window) across a variety of implementations. These transport protocols enable applications using libp2p to run as server nodes (on a personal laptop or in a datacenter) or as browser nodes (inside a Web browser).

Historically, libp2p has bridged these runtime environments with different node connectivity options in varying degrees:

Today our focus is on advancements in the browser to public server use case... * drumroll please * 🥁 We're excited to present a new paradigm for browser-to-server connectivity and announce, native support for WebRTC now exists in libp2p across three implementations!

Browser to server offerings, old and new, came with their own set of shortcomings. This new libp2p WebRTC solution establishes browser-to-server connectivity in a decentralized way across a broad spectrum of browsers and in multiple libp2p implementations.

If you're familiar with the libp2p ecosystem, you may wonder, is this new? Hasn't there already been support for WebRTC in libp2p? The answer to both questions is yes - although support has existed, this new WebRTC solution is a fresh departure from older uses for WebRTC in libp2p.

In this post we go over:

# Acknowledgements

We would like to recognize and express our gratitude to Little Bear Labs (opens new window) and Parity Technologies (opens new window) for their contributions to the development of the WebRTC specification (opens new window) and implementation in libp2p.

Little Bear Labs worked in collaboration with Protocol Labs and the libp2p community to define the WebRTC specification, and also focused on the Go and JavaScript implementations. Parity Technologies focused on the Rust implementation and initiated this effort several years ago (opens new window). We appreciate the time and effort that both of these organizations have put into this project, and their invaluable input has been instrumental in its success.

Before diving into the details of the WebRTC implementation in libp2p, let's first understand what WebRTC is and how it is used in the context of browser-based use cases.

# WebRTC in the browser

WebRTC, or Web Real-Time Communication, is a set of standards (opens new window) that enables peer-to-peer connections between browsers, clients, and servers and the exchange of audio, video, and data in real-time. It is built directly into modern browsers and is straightforward to use via its API.

While WebRTC handles audio, video, and data traffic, we're just going to focus on the data aspect because that's the API leveraged in libp2p WebRTC.

In most cases, peers directly connect to other peers, improving privacy and requiring fewer hops than on a relay. Peers connect via an RTCPeerConnection (opens new window) interface. Once connected, RTCDataChannels (opens new window) can be added to the connection to send and receive binary data.

To connect to each other, peers need to learn their public IP address and any router restrictions along the path that would prohibit peer-to-peer communication. WebRTC specifies the STUN (opens new window) protocol for that. In the case of a restriction, TURN (opens new window) servers relay data between peers using a Signaling Channel. In libp2p, we don't use these protocols.

Once public IP addresses are obtained, a peer sends an Offer SDP (opens new window) to the other peer. This Offer SDP details how the initiating peer can communicate (IP address, protocols, fingerprints, encryption, etc.). The other peer sends an Answer SDP to the initiating peer. Both peers now have enough information to start the DTLS handshake.

The DTLS handshake is performed using fingerprints contained in the Offer and Answer SDPs. After the handshake is complete, data is sent between peers using the SCTP (Stream Control Transmission Protocol) protocol, encrypting messages with DTLS over UDP or TCP.

# WebRTC in libp2p

Connecting from a browser to a public server in the WebRTC implementation in libp2p has some similarities but differs in several ways. Many of the features supported in the WebRTC standard, such as video, audio, and centralized STUN and Turn servers, are not needed in libp2p. The primary WebRTC component that libp2p leverages is the RTCDataChannels (opens new window).

# Server Setup

To prepare for a connection from the browser, the server:

  1. Generates a self-signed TLS certificate.
  2. Listens on a UDP port for incoming STUN packets.

# Browser Connection

To initiate a connection, the browser:

  1. Assembles the multiaddress of the server, which is either known upfront or discovered.
  2. Creates an RTCPeerConnection (opens new window).
  3. Generates the server's Answer SDP using the components in the multiaddress.
  4. Modifies the SDP, or "munges" it, to include an auto-generated ufrag and password, as well as the server's IP and port.
  5. Creates an Offer SDP and modifies it with the same values.
  6. Sets the Offer and Answer SDP on the browser, which triggers the sending of STUN packets to the server.

# Server Response

The server responds by creating the browser's Offer SDP using the values in the STUN Binding Request.

# DTLS Handshake

The browser and server then engage in a DTLS handshake to open a DTLS connection that WebRTC can run SCTP on top of. A Noise handshake (opens new window) is initiated by the server using the fingerprints in the SDP as input to the prologue data (opens new window), and completed by the browser over the Data Channel. This handshake authenticates the browser and the server, although Noise is not used for the encryption of data. A total of six roundtrips (five without HelloVerifyRequest DTLS DOS prottection) are performed.

Once the DTLS and Noise handshakes are complete, DTLS-encrypted SCTP data is ready to be exchanged over the UDP socket.

💡 Unlike standard WebRTC, signaling is completely removed in libp2p browser-to-server communication, and Signal Channels are not needed. Removing signaling results in fewer roundtrips to establish a Data Channel and reduces complexity by eliminating the need for signaling.

# Message Framing

Since the browser's implementation of WebRTC doesn't support stream resets or half-closing of streams, message framing was implemented on the data channels to achieve those goals.

# Multiaddress

The multiaddress (opens new window) of a WebRTC address begins like a standard UDP address, but adds three additional protocols: webrtc, hash, and p2p.

/ip4/1.2.3.4/udp/1234/webrtc/certhash/<hash>/p2p/<peer-id>
  • webrtc: the name of this transport
  • hash: the multihash (opens new window) of the certificate used in the DTLS handshake
  • p2p: the peer-id of the libp2p node (optional)

# Benefits

# Self-signed Certificate

WebRTC enables browsers to connect to public libp2p nodes without the nodes requiring a TLS certificate in the browser's certificate chain (opens new window). WebRTC allows the server to use a self-signed TLS certificate, eliminating the need for additional services like DNS and Let's Encrypt.

# Broad support

WebRTC has been supported in Chrome since 2012, and support has since been added to all evergreen browsers (opens new window). This makes WebRTC widely available and easy to implement in libp2p.

# Limitations

While WebRTC has several advantages, it also has some limitations to consider:

# Setup and configuration

WebRTC is a complex set of technologies that requires extensive server setup and configuration. While libraries like Pion and webrtc-rs abstract away this functionality, the additional complexity introduced and the configuration fine-tuning required can be a drawback for some users.

# Extensive Roundtrips

Another limitation is the 6 roundtrips required before data is exchanged. This may make other transports, such as WebTransport (opens new window), more appealing where the browser supports it.

# Usage

The complexity of WebRTC is abstracted in the libp2p implementations, making it easy to swap in WebRTC as the transport. In the JavaScript implementation, for example, all you need to do is initialize with:

import { webRTC } from 'js-libp2p-webrtc'

const node = await createLibp2p({
  transports: [webRTC()],
  connectionEncryption: [() => new Noise()],
});

The only difference from other transports is initializing with webRTC(). That's all you need to do to implement WebRTC in the browser. Easy, right?

# Alternative transports

WebRTC is just one option for connecting browsers to libp2p nodes. libp2p supports a variety of transports, and choosing the right one for your use case is an important consideration. The libp2p connectivity site (opens new window) was designed to help developers to consider the available options.

# WebSocket

The WebSocket protocol, defined in the WebSocket RFC (opens new window), allows for the opening of a two-way socket between a browser and a server over TCP. It is supported in the Rust (opens new window), Go (opens new window), and JavaScript (opens new window) libp2p implementations.

# Limitations

One limitation of WebSocket is the number of roundtrips required to establish a connection. Handshakes and other upgrades add up to six roundtrips, which can be slower than other transports. Additionally, WebSocket requires the server to have a trusted TLS certificate using TCP, unlike WebRTC which can use a self-signed certificate.

# WebTransport

WebTransport (opens new window) is the new kid on the block for communication in the browser. WebTransport is implemented in Go (opens new window) and JavaScript (opens new window) implementations.

# Benefits

WebTransport has many of the same benefits as WebRTC, such as fast, secure, and multiplexed connections, without requiring servers to implement the stack. It also allows libp2p to use raw WebTransport streams and avoid double encryption. Additionally, WebTransport requires fewer roundtrips to establish a connection than WebRTC, making it the preferred choice when supported.

As opposed to WebSockets, libp2p can use raw WebTransport streams and avoid the need for double encryption.

# Limitations

You might be asking yourself, why pick WebRTC over WebTransport in libp2p? It's like WebRTC but easier to implement and with less complexity. Still, WebTransport is not without its limitations.

Currently, it is only implemented in Chrome and is still under development. Until WebTransport is supported by all major browsers, WebRTC can serve as a good fallback option.

# Legacy WebRTC implementations in libp2p

This new implementation of WebRTC in libp2p is a departure from previous, less effective solutions that were previously used to establish connectivity.

# libp2p-webrtc-star

libp2p-webrtc-star was released (opens new window) in 2016. This transport utilizes centralized STUN and TURN servers to handle signaling and was never intended (opens new window) to be a long-term solution. The repository was archived in November 2022 in favor of the new js-libp2p-webrtc transport due to libp2p-webrtc-star's dependence on centralized servers.

# libp2p-webrtc-direct

libp2p-webrtc-direct utilizes WebSockets to exchange SDPs, removing the need for centralized dependency in libp2p-webrtc-star. While libp2p-webrtc-direct solved the centralized problem, the servers must have valid TLS certificates for WebSocket connectivity. The repository was also archived in November 2022.

# Can I use WebRTC now?

Yes, you can use libp2p-webrtc in the Rust (opens new window) and JavaScript (opens new window) implementations! The Go (opens new window) implementation is close to completion. Follow the development in go-libp2p (opens new window) to get notified when it gets shipped.

In fact, the Rust implementation of WebRTC has already been put into use by the Parity team! It has been enabled as an experimental feature and added to Smoldot (opens new window) (a lightweight client for Substrate (opens new window) and Polkadot (opens new window)). There is also ongoing work to enable it directly in Substrate (opens new window).

This is exciting news as WebRTC is already contributing to Parity's roadmap to enable browser to server connectivity (opens new window) on their network!

For how to use WebRTC browser-to-server, you can take a look at the examples in the js-libp2p-webrtc repo (opens new window). Lastly, you can also take a look at this WebRTC demo given on libp2p Day (opens new window).

# What's next?

WebRTC offers the capability for browsers to connect to browsers 🎉. This isn't currently possible in any of the active libp2p transports and represents a significant achievement in libp2p.

The WebRTC browser-to-browser connectivity spec (opens new window) is currently being authored and development will soon start. Follow the PR (opens new window) for up-to-date information as well as the overall tracking issue (opens new window).

# Resources and how you can help contribute

If you would like to read further about WebRTC. Please see the libp2p:

If you would like to contribute, please connect with the libp2p maintainers (opens new window).

Thank you for reading!