Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(webrtc): add WebRTC (prev. browser-to-browser) spec #497

Merged
merged 58 commits into from
Apr 12, 2023

Conversation

mxinden
Copy link
Member

@mxinden mxinden commented Dec 15, 2022

The libp2p WebRTC (prev. browser-to-browser) specification.

Open items:

@Menduist
Copy link
Contributor

Isn't this "any-to-browser"? "Servers" will also be able to use the same mechanism to dial browsers, right?

@mxinden mxinden changed the title webrtc/: Initialize browser-to-browser specification webrtc/: Add browser-to-browser specification Dec 15, 2022
webrtc/browser-to-browser.md Outdated Show resolved Hide resolved
webrtc/browser-to-browser.md Outdated Show resolved Hide resolved
webrtc/browser-to-browser.md Outdated Show resolved Hide resolved
Copy link
Contributor

@thomaseizinger thomaseizinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exciting!

webrtc/browser-to-browser.md Outdated Show resolved Hide resolved
@thomaseizinger
Copy link
Contributor

Isn't this "any-to-browser"? "Servers" will also be able to use the same mechanism to dial browsers, right?

Even two servers could use if for some reason they wanted to use WebRTC to communicate to each other, right?. Isn't it more like dcutr for WebRTC?

Copy link
Contributor

@marten-seemann marten-seemann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some high level thougths. This PR is mostly a description of what other protocols do. We need to specify how the libp2p-specific parts work here! This means especially that we need to specify how the signaling channel works.

In particular, (1) - (3) is just a description of Circuit v2, (4) - (6) is just a rehearsal of how a WebRTC handshake looks like between two browsers.

To make this an actual spec of how libp2p does browser-to-browser WebRTC, the following needs to be taken into account.

STUN

If I understand browser WebRTC correctly, it is required that browsers use a STUN server. The spec is completely silent about this. At the very least, we should acknowledge that implementations will need to pick a STUN server, and leave it up to the implementation to pick one (or to expose a configuration option).

Most people would then probably Google's public (and free) STUN service, as this seems to be the most popular service around. This might be considered problematic, as we might not want to have a large fraction of STUN traffic sent to Google. We could also mention that setting up a dedicated STUN server yourself (as a project maintainer) is within the realm of possibilities, and we might even consider doing that for the IPFS network.

The trade-offs need to be mentioned in the specification.

TURN

Not every (WebRTC-initiated) hole punching attempt will be successful. Many video-conferencing tools therefore set up TURN servers to help those 5-15% of unlucky users connect throught that TURN server. This is a problem, since we (as PL) can't and won't provide a (centralized) TURN server.

If you need a TURN server or not depends very much on the use case. It's useful for video conferencing, but not so much for WebTorrent, for example. I found the discussion of the tradeoffs invovled in this podcast episode very interesting.

Anyway, this should be mentioned and discussed in this specification.

Signaling Channel

DCUtR, again ☹️

I’m a bit sad to see that this PR still contains the original proposal to use DCUtR. I really don’t understand where the desire to shoehorn DCUtR into something that it’s never been designed to do is coming from. I had already flagged this in my review of the original PR in September.

DCUtR is an exceptionally bad fit here:

  1. DCUtR is a two-step protocol, consisting of a SYNC and a CONNECT step. We don’t need that here, we only need to exchange the peers’ SDPs, unless... we trickle.
  2. In libp2p hole punching, there's no need to trickle address candidates, as nodes learn about their addresses (often times long) before the connection attempt. In WebRTC, as we’re restricted by the browser API, address discovery happen during the connection attempt, and trickling addresses can greatly speed up the handshake (at least this is claimed by various sources around the internet).
  3. DCUtR sends addresses as multiaddrs. WebRTC on the other hand needs to exchange entire SDPs.

I also don't understand how identify push would solve the problem here.

libp2p protocol identifiers are not a limited resource by any means. Small, single-purpose protocols are a much better design pattern than big, bloated protocols.

What we should do instead

To understand how signaling works, I found it instructive to go through this WebRTC tutorial (with code) to see how signaling actually works.

We should:

  1. Define an identifier for the libp2p WebRTC protocol. Proposal: /webrtc-direct, but feel free to bike-shed this.
  2. This protocol is used on a (bidirectional) stream, peers send each other WebRTC messages, and close the stream once the WebRTC connection has been established (or the connection attempt has failed).
  3. Define how messages are serialized on the stream.

While it would be possible to define a Protobuf serialization, this would be a quite cumbersome task. Some of the data structures exchanged are pretty large, for example the RTCIceCandidate. It probably makes more sense to either serialize the entire message to JSON (like in the linked example), or to define a protobuf for the message type (offer, answer, ICE candidate, etc.) that then carries the actual message as a JSON string.

Authentication

The SDPs are exchanged over an (authenticated) libp2p connection, and they contain the certificate hash of their respective endpoint (not sure if that's what Chinmay was pointing out). So technically, there’s no need to run a Noise handshake, given that the browser checks the certificate hashes. Nodes can just “transfer” pubkeys / peer IDs from that connection to the newly established WebRTC connection and expose them on the connection API.

webrtc/browser-to-browser.md Outdated Show resolved Hide resolved
webrtc/browser-to-browser.md Outdated Show resolved Hide resolved
webrtc/browser-to-browser.md Outdated Show resolved Hide resolved
webrtc/browser-to-browser.md Outdated Show resolved Hide resolved
webrtc/browser-to-browser.md Outdated Show resolved Hide resolved
@mxinden
Copy link
Member Author

mxinden commented Jan 5, 2023

Isn't this "any-to-browser"? "Servers" will also be able to use the same mechanism to dial browsers, right?

Even two servers could use if for some reason they wanted to use WebRTC to communicate to each other, right?. Isn't it more like dcutr for WebRTC?

Agreed with both of you @Menduist and @thomaseizinger. That said, I don't think "any-to-any" or "direct-webrtc-connection-upgrade-through" is very intuitive. Thus I suggest we stick with browser-to-browser, as it is the most intuitive name I can think of.

@mxinden
Copy link
Member Author

mxinden commented Jan 5, 2023

@marten-seemann thanks for the review.

29d166b addresses some of your comments. Still lots more to come.

Experimenting with whether this makes reviewing and git diffs simpler.
@Menduist
Copy link
Contributor

Menduist commented Jan 5, 2023

Maybe just "webrtc-holepunching"? I'd rather have something correct than something catchy (and B2B isn't that catchy anyway, since server to browser is as important as B2B, imo)

Copy link
Contributor

@ckousik ckousik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what we should use as the protocol id. webrtc-direct is already in use, but I have been using webrtc-peer in my experiments.

Signaling

Signaling here can be very simple. For eg. I have been using the following protobuf:

syntax = "proto2";

message Message {
    enum Type {
        OFFER = 0;
        ANSWER = 1;
        CANDIDATE = 2;
    }

    required Type type = 1;
    required string data = 2;
}

The types are self explanatory. In the data section:

  1. If encoding an offer/answer, the data field contains the sdp string from the RTCSessionDescription.
  2. If encoding a candidate, I have been using the JSON created by using the toJSON method on the RTCIceCandidate. This allows the receiver to simply deserialize the JSON and pass it to the RTCIceCandidate constructor. If the candidate is null or undefined, we can just send an empty string.

STUN/TURN

For browsers, it seems out of scope of this spec to define STUN or TURN server usage. It should be left to implementations, and depends a lot on the individual network situation of a node. If a node is publicly accessible, it doesn't need either. STUN works in a general case, and users can choose between running their own STUN server or using a public one. Some special cases may need a TURN server.

webrtc/browser-to-browser.md Outdated Show resolved Hide resolved
@thomaseizinger
Copy link
Contributor

I'm not sure what we should use as the protocol id. webrtc-direct is already in use, but I have been using webrtc-peer in my experiments.

Is it? The protocols.csv table only mentions /p2p-webrtc-direct: https://github.com/multiformats/multiaddr/blob/master/protocols.csv

Is it too confusing to use /webrtc-direct now, i.e. just dropping the /p2p prefix?

@mxinden
Copy link
Member Author

mxinden commented Jan 16, 2023

I'm not sure what we should use as the protocol id. webrtc-direct is already in use, but I have been using webrtc-peer in my experiments.

Is it? The protocols.csv table only mentions /p2p-webrtc-direct: https://github.com/multiformats/multiaddr/blob/master/protocols.csv

Unless I am missing something, the discussion about protocol names referred to the name of the stream protocol, not the name of the Multiaddr protocol component.

That said, you are raising a good point, namely whether we need a Multiaddr protocol for this specification as well. The rational is documented in 78fae26:

  • Do we need a mechanism for browsers to advertise support for WebRTC browser-to-browser?

    Say that browser B supports WebRTC browser-to-browser.
    B listens via a relay and advertises its relayed address.
    A discovers B's relayed address.
    At this point A does not know whether B is a browser and thus supports WebRTC browser-to-browser, or whether B is e.g. a laptop potentially supporting TCP and QUIC hole punching via DCUtR but not WebRTC browser-to-browser.
    In the latter case, A can not establish a direct connection to B.

    Potential solution would be for B to advertise some protocol after the /p2p-circuit within its Multiaddr, e.g. /ip6/<RELAY_IP>/udp/4001/p2p/<RELAY_PEER_ID>/p2p-circuit/webrtc-direct/p2p/<B_PEER_ID>.
    As an alternative, A can discover B's support via the identify protocol on the relayed connection or by optimistically opening a stream using the signaling protocol.
    Both of the latter options would on failure happen at the expense of a wasted relayed connection.

webrtc/browser-to-browser.md Outdated Show resolved Hide resolved
webrtc/browser-to-browser.md Outdated Show resolved Hide resolved
Copy link
Contributor

@thomaseizinger thomaseizinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall ACK with a few nits

webrtc/README.md Show resolved Hide resolved
webrtc/README.md Outdated Show resolved Hide resolved
webrtc/webrtc.md Outdated Show resolved Hide resolved
webrtc/webrtc.md Outdated Show resolved Hide resolved
webrtc/webrtc.md Outdated Show resolved Hide resolved
webrtc/webrtc.md Outdated Show resolved Hide resolved
webrtc/webrtc.md Outdated Show resolved Hide resolved
webrtc/webrtc.md Show resolved Hide resolved
webrtc/webrtc.md Outdated Show resolved Hide resolved
webrtc/webrtc.md Outdated Show resolved Hide resolved
webrtc/webrtc.md Outdated Show resolved Hide resolved
webrtc/webrtc.md Outdated Show resolved Hide resolved
webrtc/webrtc.md Outdated Show resolved Hide resolved
@mxinden
Copy link
Member Author

mxinden commented Mar 27, 2023

Thanks for the reviews @thomaseizinger and @marten-seemann.

@marten-seemann mind taking another look?

@mxinden
Copy link
Member Author

mxinden commented Apr 4, 2023

Friendly ping @marten-seemann.

webrtc/webrtc.md Outdated Show resolved Hide resolved
Co-authored-by: Chad Nehemiah <chad.nehemiah94@gmail.com>
@BigLep
Copy link
Contributor

BigLep commented Apr 10, 2023

@mxinden : what's remaining given libp2p/js-libp2p-webrtc#90 was merged and there are approvals here?

- Bump webrtc-direct revision to r1 given the multiaddr change
- Move webrtc to lifecycle 2A Candidate Recommendation
- Updates dates
@mxinden mxinden changed the title webrtc/: Add WebRTC (prev. browser-to-browser) spec feat(webrtc): add WebRTC (prev. browser-to-browser) spec Apr 12, 2023
@mxinden mxinden merged commit f8f32f7 into master Apr 12, 2023
@mxinden mxinden deleted the webrtc-browser-to-browser branch April 12, 2023 07:30
@mxinden
Copy link
Member Author

mxinden commented Apr 12, 2023

With /webtransport, /webrtc-direct and here /webrtc we now have all protocols specified that are needed for universal libp2p connectivity, more specifically integrating the browser. 🎉 thanks to everyone for making this possible 🎉

mxinden added a commit to mxinden/specs that referenced this pull request May 31, 2023
With libp2p#412 and
libp2p#497 merged, this roadmap item can be moved
to "done".
mxinden added a commit that referenced this pull request Jun 1, 2023
With #412 and
#497 merged, this roadmap item can be moved
to "done".

Co-authored-by: Prithvi Shahi <50885601+p-shahi@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.