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

Add WebRTC Signaling Protocol Spec #159

Closed
wants to merge 4 commits into from

Conversation

albrow
Copy link

@albrow albrow commented Mar 22, 2019

Summary

This PR introduces a draft specification for a WebRTC Signaling Protocol which can be used to establish a WebRTC connection between two peers.

At this time, the specification should be considered a work in progress. There are still some details to work out regarding signatures, message encoding, and error handling.

Additional Context

I know that there already has been some work done to add WebRTC support in the JavaScript implementation of libp2p (see js-libp2p-webrtc-direct and js-libp2p-webrtc-star) and there is also a community-contributed WebRTC transport for the Go implementation (see backkem/go-libp2p-webrtc-direct). However, AFAIK we do not yet have an agreed upon standard for establishing WebRTC connections.

The goal of this proposal is to have a single standard that can be used across all language implementations and is compatible with both non-browser peers (which have the ability to serve as the signaler themselves) and browser peers (which must rely on a third-party signaler).

@raulk
Copy link
Member

raulk commented Mar 23, 2019

Thanks for contributing! There is opportunity to generalise hole punching/STUN outside the domain of WebRTC.

I’m thinking we should architect this solution around two components:

  1. the signalling engine.
  2. interface façades for interoperability with things like WebRTC.

@albrow
Copy link
Author

albrow commented Mar 25, 2019

@raulk I would be receptive to making this spec more generalized as long as it still suits our needs for WebRTC. Can you elaborate on what you have in mind?

@backkem
Copy link

backkem commented Mar 27, 2019

Making an interface for signaling seems to fit the libp2p philosophy. I also think the current WebRTC implementations could be split further to fut the different interfaces: libp2p/go-libp2p-webrtc-direct#9, libp2p/go-libp2p-webrtc-direct#7.
There has been some discussion around signaling in libp2p/go-libp2p-webrtc-direct#14. Main points:

  • It would be cool to enable many types of signaling. Both in format (JSON, ProtoBufs, ...) and transport (HTTP, 'Star', GossipSub, ... ). Maybe it's worth expressing this in some form of Multi Address?
  • It seems advisable to investigate limiting the amount/size of the signals that are exchanged. These channels should not be abused to send content other than for signaling.

@albrow
Copy link
Author

albrow commented Mar 28, 2019

@backkem

It would be cool to enable many types of signaling. Both in format (JSON, ProtoBufs, ...) and transport (HTTP, 'Star', GossipSub, ... ). Maybe it's worth expressing this in some form of Multi Address?

Yes, that's exactly what I was thinking. In my prototype transport I was using Multiaddresses that look like:

/ip4/<signaler-ipv4-address>/tcp/<signaler-port>/ws/p2p-webrtc-signal/<peer-id>

This means you are dialing the peer identified by peer-id via the signaler at signaler-ipv4-address and signaler-port and using WebSockets as the transport. signaler-ipv4-address could be your own address, which eliminates the need for a third-party if you are running a peer which is directly dialable (i.e. not a browser). Peers could even use an array of Multiaddresses if they support more than one signaler or more than one transport/encoding. This flexibility also means we don't need separate webrtc-direct and webrtc-star transports. We can just have one WebRTC transport for each language implementation.

It seems advisable to investigate limiting the amount/size of the signals that are exchanged. These channels should not be abused to send content other than for signaling.

Good idea. Is it more appropriate to let peers decide for themselves what they want the amount/size limits to be? Or do you think it should be part of the protocol? In any case the protocol should at least provide standardized error messages for rate-limiting.

@backkem
Copy link

backkem commented Apr 2, 2019

I wanted to define my meaning of 'signaling': exchange a bit of information between two peers so they can initiate a connection. Note that this doesn't include peer discovery, hole punching or NAT traversal. While also interesting, those aren't what we need to abstract to come to a generic WebRTC transport (a.k.a.: unify libp2p-webrtc-direct and libp2p-webrtc-star).

Next, It seems advisable to determine how far we want to abstract the signaling logic. Example possibilities:

  • Minimal:
    • Underlying transport to signal over directly determined by MultiAddr.
    • Underlying transport (e.g.: HTTP or WS) implemented directly in the signaler, as they are currently implemented directly in the transport.
    • Specific data format specified for signaling over the transport.
  • Maximal:
    • Generic interface to exchange the signaling data (something io.ReadWriteCloser inspired).
    • Multiple signaling strategies available to a transport. The transport automatically figures out which strategy to use based on a 'signaling type' included in the MultiAddr. This also allows multiple data formats to be used.
    • Signaling over the Transports already available to libp2p. libp2p allows the signaler to automatically find the correct one based on the MultiAddr. This would also allow for complex use-cases like exchanging signaling information over already existing WebRTC connections.

The former would probably remain rather WebRTC specific. The latter is highly abstract and would be more generically applicable in libp2p. That being said, I'm not sure if we need to make it that generic. I don't know if/how many other protocols rely on an exchange of information between peers before a connection can be made, as is the case with WebRTC. Maybe the solution lies somewhere in the middle.

@albrow
Copy link
Author

albrow commented Apr 3, 2019

@backkem I can't speak for everyone who might want to make use of a signaling server. For our use case (0x Mesh), the only bullet point in the "maximal" abstraction category that is useful is the last one: Signaling over the Transports already available to libp2p.

There are two benefits to building the Signal Protocol to take advantage of existing libp2p Transports:

  1. It reduces the amount of code that we need to write to implement the WebRTC Transport. If we can take advantage of the Transports that already exist in libp2p, we don't need to write lower-level network code. It is especially important if we ever wanted to handle more than one underlying network transport (e.g. TCP and WebSockets), because the Transport interface in libp2p already abstracts away the differences for us.
  2. It is much easier to make our signaling process fully decentralized. If the Signal Protocol is designed to work with the libp2p Transports that peers already know how to speak, it is a small step to allow virtually any peer to be a signaler. In a way, this brings the signaling process inside of the existing libp2p ecosystem instead of having it external to libp2p itself. If the Signal Protocol can use any libp2p Transport, it is even possible for a browser peer to serve as a signaler over an existing WebRTC connection (provided that both peers that wish to connect are connected to the same third-party peer). On the other hand, if we were implement the Signal Protocol over e.g., TCP only, it would not be possible for a browser-based peer to serve as a signaler because they cannot be dialed via TCP. In the future, it could even be possible to build "auto-signal" functionality similar to the existing autorelay option. This is a huge win for decentralization.

@albrow albrow changed the title Add WebRTC Signal Protocol Add WebRTC Signaling Protocol May 21, 2019
@albrow
Copy link
Author

albrow commented May 21, 2019

Just updated the spec proposal with some more information about multiaddress formats and how any existing libp2p transport can be used for signaling.

@yusefnapora
Copy link
Contributor

Hey @albrow, I really like where this spec is going 👍

I think we should merge it in soon as a Working Draft and iterate on it in some more.

I'm down to be on the interest group, so we just need two more people. @jacobheun @backkem @raulk - would any of you want to join as well?

@jacobheun
Copy link
Contributor

would any of you want to join as well?

Yes, count me in. The foundations here look good and the overall direction would be of huge benefit to helping with moving browser solutions more decentralized.

I agree with working to get this merged in as a working draft and iterating on it.

Having an implementation of this that also supported an "auto-signal" option could provide a significant performance improvement to systems like the DHT.

- Signaling over HTTP using a domain name:
`/dns6/signaler.myapp.com/tcp/80/http/p2p-webrtc-signal/QmZbw3TKr3dxhHXiPkbNraWaeGoqPNXAXfAcV8RP2Eqngj`
- Signaling via a common peer:
`/p2p-webrtc-signal/QmWeRHDDiwuGnS4xbjF2zXETucL7xQLjadoaTZ4yJE3hQs`
Copy link
Member

Choose a reason for hiding this comment

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

Not sure I get this one? Is it missing the ID of the common peer acting as a signaller?

Copy link
Contributor

Choose a reason for hiding this comment

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

It says above

<signaler-multiaddr> may be omitted to use an existing connection to <signaler-peer-id> for signaling.

But I'm also not entirely clear on the use case. Does this only work if I've already established a separate connection to the peer I want to reach (e.g. through a circuit relay)?

Or if we've both connected to any common peer that has the p2p-webrtc-signal capability, could I discover and use that without caring who the signaller peer is?

Copy link

Choose a reason for hiding this comment

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

Or if we've both connected to any common peer that has the p2p-webrtc-signal capability, could I discover and use that without caring who the signaller peer is?

I think that not a good idea, a better solution could be to just indicate on the DHT we are reachable with WebRTC-signal and use a circuit relay to establish the connection.
So for that we need first a ws[s] connection to establish circuit relay and discover the DHT.

Adding some special nodes is a very good way to allow anybody to get a giant control on the network.
When these kind of option are made a very few people activate them, that made people less interested in making these because we know that gonna generate a big traffic.
In fact my solution add a problem about stun server but I think thats more a client side problem (by that I mean not a DHT one) 2, because 2 totaly separated STUN server produce some compatible result (except if you are not in the same network, example: internet and hyperboria), and some STUN server are region locked (China).
We may can just allow peers to run integrated STUN server and broadcast it to the DHT (to be apocalypsical proof) since that give you way less information (a meeting server know who talk to who, can chose who talk to who but can't create fake connection like Qmxxxx... is listening for WebRTC if we require signing these kind of information to a server capable of knowing this ip (so easly matchable with a peer id via DHT), want to connect to a WebRTC node).

PS: Meeting server can maybe be used only to broadcast "Qmxxxx is listening for WebRTC, contact him via circuit relay at this node" (signed by Qmxxxx for obvious reason) but in this case why not just putting these directly in DHT listen address of Qmxxxx ?

@raulk
Copy link
Member

raulk commented May 28, 2019

This is awesome, thank you. Yes, I'd be down to be in the Interest Group for this one, and I also would like @vyzo to sit on it.

I'd like to explore integrating this protocol into the existing relay protocol, or finding angles for them to cooperate. Thoughts:

  • The current relay protocol offers only TURN capabilities (circuit relaying traffic).
  • We want to extend it with STUN capabilities (signalling for hole punching). Two peers connected to the same relay should be able to coordinate via it for hole punching.
  • When two peers establish a connection via a relay, besides exchanging traffic immediately they should start the whole punching procedure in the background. Basically the relay would moonlight as a signaller.
  • If they manage to pierce through their NATs, they should migrate their relayed connection state to the direct connection.
  • Unfortunately multistream-select 1.0 is a blocker here, as it's an interactive protocol that relies on initiator/responder roles. So it's enemies with TCP simultaneous open which is likely to happen during TCP hole punching.
  • Autorelay is a feature that detects when the node is running behind a NAT, connects to relays and advertises the relay addrs to the world, to enable peers wishing to establish inbound connections to do so via the relay.
  • We could decentralise signalling by making autorelay use a non-random heuristic like the Kademlia distance to select a relay. That way, node A wishing to connect to node B (both behind a NAT) can use the relay with Kad distance closest to node B as a probable signaller.

@jacobheun
Copy link
Contributor

I'd like to explore integrating this protocol into the existing relay protocol, or finding angles for them to cooperate.

We should be able to have this work pretty easily leveraging circuit as the transport if the destination peer was also a signaler and we supported self signaling. Once we establish the connection over relay we can perform the signaling handshake. The destination peer could supply its own answer to the signaling offer and then establish the direct connection to the dialer, at which point we could end the relayed connection. Ideally the nodes would use autorelay to advertise their relay address, but in reality all we need is /p2p-circuit/p2p-webrtc-signal/QmTarget, as long as we have a common relay. In this approach the relay doesn't need to be a signaler, although it could be, and the protocols don't have any dependency on one another.

@vyzo
Copy link
Contributor

vyzo commented May 28, 2019

consider me counted as part of the interest group.

@raulk
Copy link
Member

raulk commented May 29, 2019

@albrow @backkem Hey folks, @vyzo has filed a Working Draft for a generic hole punching procedure over the relay protocol. Could you do a quick review of this and post your thoughts? #173

@albrow
Copy link
Author

albrow commented May 29, 2019

Just wanted to point out that pion/webrtc just added support for a feature called Trickle ICE in pion/webrtc#691. This will probably affect this spec.

For some background, ICE involves gathering a list of "candidates" which are potential ways to connect to a peer. Candidates can be created by using a combination of STUN and TURN. So for example, as part of the "candidate gathering" process, you might perform hole punching with a specific STUN server. This takes time becaue it often involves sending requests to third-party servers and waiting for a response. Up until now I have written the spec with the assumption that all candidates will be gathered synchronously and the answers and offers that are passed between peers are complete (because up until now that was the way pion/webrtc worked).

Trickle ICE is an optimization that makes the ICE candidate gathering process asynchronous instead of synchronous. It lets peers select the first candidate that works instead of waiting for all potential candidates to be gathered. In effect, this decreases the time it takes for two peers to connect.

I'm going to update this spec with Trickle ICE in mind. This will probably mean adding a way for peers to update their answers/offers by adding new candidates as they are gathered.

@raulk
Copy link
Member

raulk commented May 30, 2019

@albrow 👏

@Qmstream
Copy link

What about this proposal by @mkg20001 to do the signaling through a lightweight circuit protocol?

libp2p/js-libp2p-webrtc-star#148

From what it seems, with it no intermediary peer needs to be specified.

See also:

libp2p/js-libp2p#361

Messages sent to the signaler are signed with a private key
corresponding to that PeerID. This prevents tampering and impersontation. In
other words, it ensures that a peer can only be connected to the peer that they
intended to connect to.
Copy link
Member

Choose a reason for hiding this comment

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

This step is done for free when using libp2p Circuit Relay.

use the following multiaddress format for dialing and listening:

```
<signaler-multiaddr>/p2p-webrtc-signal/<signaler-peer-id>
Copy link
Member

Choose a reason for hiding this comment

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

The signaler-multiaddr can just be the relay address.

@daviddias
Copy link
Member

@albrow thank you so much for taking on this spec. Getting to a clear understanding of how WebRTC connections are established in the "normal world" and how they can be established in libp2p will get us to implement it correctly without using anything else other than libp2p nodes (so that any node can play the role, making the network more resilient).

@Qmstream you hit the bullseye, in fact hat has been the goal since the very beginning 😁 I'll also add that there is actually no need to create yet another libp2p interface for data-exchange. It is literally just sending two (or a few) JSON objects with the respective SDP offers over any libp2p connection for the nodes that want to connect to each other.

libp2p/js-libp2p#385 should provide more clarity of where we want to take the *-star protocols (i.e. to cease to exist).

A few remarks/comments:

  • SDP exchange can be done wholesale or Trickle. This is something that we actually don't have to think much because are all libp2p connections are streams (vs. for example doing http requests without streaming). Independent of the mode that the user picks, libp2p is a perfect match.
  • SDP exchange is not defined by the WebRTC Spec, it is really up to the implementer. For libp2p's case what makes most sense is for this to happen over a libp2p connection (which for most use cases, it will be a relayed libp2p connection).
  • Connecting over WebRTC in libp2p language looks more like a transport upgrade than a dialing. Peers will:
    • Find each other through a Peer Discovery mechanism or the Rendezvous protocol
    • Establish a Circuit Relay connection (from here on, they can communicate)
    • Multistream a WebRTC-Exchange protocol and check if the other peer supports it
    • If the peer supports it, establish the WebRTC connection and drop the Circuit Relay connection

@albrow
Copy link
Author

albrow commented Jul 22, 2019

@daviddias

Connecting over WebRTC in libp2p language looks more like a transport upgrade than a dialing

That approach makes a lot of sense and would solve some of the problems in the current version of the spec (mainly that it requires a custom multiaddr format and a unique multiaddr per peer that you're connected to, which is impractical). Can you point me to an existing spec for a transport upgrade that I could base this on?

@rayj00
Copy link

rayj00 commented Dec 24, 2019

I'm very interested in working on a WebRTC project, using IPFS to distribute the video/audio. What is the status of any of the WebRTC/IPFS work that is in progress? I am particularly interested in using WebRTC One to Many broadcasting with IPFS distribution. Just not sure how to handle signaling?

@backkem
Copy link

backkem commented Dec 29, 2019

The current status is that there are a couple implementations:

This PR is an attempt at abstracting the signaling away from the transport so there would only be one WebRTC transport (per language) that can plug-in different signaling strategies. Another point of research is decentralizing the signaling. For example, by relaying it over already connected peers.

If you're interested in video streaming over WebRTC you may be interested in adding WebRTC support to LivePeer media server: livepeer/lpms#38

@backkem
Copy link

backkem commented May 22, 2020

You may also want to take go-webrtc-aside-transport into account. This uses WebRTC's native transport security and muxing and does signaling over existing connections.

@mxinden mxinden changed the title Add WebRTC Signaling Protocol Add WebRTC Signaling Protocol Spec Apr 16, 2021
@mxinden mxinden mentioned this pull request May 25, 2021
14 tasks
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants