Lifecycle Stage | Maturity | Status | Latest Revision |
---|---|---|---|
1A | Working Draft | Active | r3, 2021-07-12 |
Authors: @vyzo
Interest Group: @daviddias, @whyrusleeping, @Stebalien, @jacobheun, @yusefnapora, @vasco-santos
See the lifecycle document for context about the maturity level and spec status.
The protocol described in this specification is intended to provide a lightweight mechanism for generalized peer discovery. It can be used for purposes like bootstrapping, real-time peer discovery, and application-specific routing. Any node implementing the rendezvous protocol can act as a rendezvous point, allowing the discovery of relevant peers in a decentralized manner.
Depending on the application, the protocol could be used in the following context:
- During bootstrap, a node can use known rendezvous points to discover peers that provide critical services. For instance, rendezvous can be used to discover circuit relays for connectivity-restricted nodes.
- During initialization, a node can use rendezvous to discover peers to connect with the rest of the application. For instance, rendezvous can discover pubsub peers within a topic.
- In a real-time setting, applications can poll rendezvous points in order to discover new peers in a timely fashion.
- In an application-specific routing setting, rendezvous points can be used to progressively discover peers that can answer specific queries or host shards of content.
We intend to replace ws-star-rendezvous with a few rendezvous daemons and a fleet of p2p-circuit relays. Real-time applications will utilize rendezvous both for bootstrap and in a real-time setting. During bootstrap, rendezvous will be used to discover circuit relays that provide connectivity for browser nodes. Subsequently, rendezvous will be utilized throughout the application's lifetime for real-time peer discovery by registering and polling rendezvous points. This allows us to replace a fragile centralized component with a horizontally scalable ensemble of daemons.
Rendezvous can be naturally combined with pubsub for effective real-time discovery. At a basic level, rendezvous can bootstrap pubsub: nodes can utilize rendezvous to discover their peers within a topic. Alternatively, pubsub can also be used to build rendezvous services. In this scenario, several rendezvous points can federate using pubsub for internal real-time distribution while still providing a simple interface to clients.
The rendezvous protocol provides facilities for real-time peer discovery within application-specific namespaces. Peers connect to the rendezvous point and register their presence in one or more namespaces. It is not allowed to register arbitrary peers in a namespace; only the peer initiating the registration can register itself. The register message contains a serialized signed peer record created by the peer, which others can validate.
Other nodes can discover peers registered with the rendezvous point by querying the rendezvous point. The query specifies the namespace for limiting application scope and, optionally, a maximum number of peers to return. The namespace can be omitted in the query, which asks for all peers registered to the rendezvous point.
The query can also include a cookie obtained from the response to a previous query, such that only registrations that weren't included in the previous response will be returned. This lets peers progressively refresh their network view without overhead, simplifying real-time discovery. It also allows for the pagination of query responses so peers can manage large numbers of peer registrations.
The rendezvous protocol runs over libp2p streams using the protocol id /rendezvous/1.0.0
.
An optional TTL parameter in
the REGISTER
message controls the registration lifetime. If a TTL is
specified, then the registration persists until the TTL expires. If no
TTL was set, then a default of 2hrs is implied. There may be a rendezvous
point-specific upper bound on TTL, with a maximum value of 72hrs. If the
TTL of a registration is inadmissible, the rendezvous point may reject
the registration with an E_INVALID_TTL
status.
Peers can refresh their registrations at any time with a new
REGISTER
message; the TTL of the new message supersedes previous
registrations. Peers can also cancel existing registrations at any
time with an explicit UNREGISTER
message. An UNREGISTER
message does
not have an explicit response. UNREGISTER
messages for a namespace
that a client is not registered for should be treated as a no-op.
The registration response includes the actual TTL of the registration, so that peers know when to refresh.
Clients A
and B
connect to the rendezvous point R
and register for namespace
my-app
with a REGISTER
message:
A -> R: REGISTER{my-app, {QmA, AddrA}}
R -> A: {OK}
B -> R: REGISTER{my-app, {QmB, AddrB}}
R -> B: {OK}
Client C
connects and registers for namespace another-app
:
C -> R: REGISTER{another-app, {QmC, AddrC}}
R -> C: {OK}
Another client D
can discover peers in my-app
by sending a DISCOVER
message; the
rendezvous point responds with the list of current peer reigstrations and a cookie.
D -> R: DISCOVER{ns: my-app}
R -> D: {[REGISTER{my-app, {QmA, Addr}}
REGISTER{my-app, {QmB, Addr}}],
c1}
If D
wants to discover all peers registered with R
, then it can omit the namespace
in the query:
D -> R: DISCOVER{}
R -> D: {[REGISTER{my-app, {QmA, Addr}}
REGISTER{my-app, {QmB, Addr}}
REGISTER{another-app, {QmC, AddrC}}],
c2}
If D
wants to poll for real-time discovery progressively, it can use
the cookie obtained from a previous response only ask for
new registrations.
So here we consider a new client E
registering after the first query,
and a subsequent query that discovers just that peer by including the cookie:
E -> R: REGISTER{my-app, {QmE, AddrE}}
R -> E: {OK}
D -> R: DISCOVER{ns: my-app, cookie: c1}
R -> D: {[REGISTER{my-app, {QmE, AddrE}}],
c3}
The protocol, as described so far, is susceptible to spam attacks from adversarial actors who generate a large number of peer identities and register under a namespace of interest (e.g., the relay namespace).
It is TBD how exactly the protocol will mitigate such attacks. See #341 for a discussion on this topic.
syntax = "proto2";
message Message {
enum MessageType {
REGISTER = 0;
REGISTER_RESPONSE = 1;
UNREGISTER = 2;
DISCOVER = 3;
DISCOVER_RESPONSE = 4;
}
enum ResponseStatus {
OK = 0;
E_INVALID_NAMESPACE = 100;
E_INVALID_SIGNED_PEER_RECORD = 101;
E_INVALID_TTL = 102;
E_INVALID_COOKIE = 103;
E_NOT_AUTHORIZED = 200;
E_INTERNAL_ERROR = 300;
E_UNAVAILABLE = 400;
}
message Register {
optional string ns = 1;
optional bytes signedPeerRecord = 2;
optional uint64 ttl = 3; // in seconds
}
message RegisterResponse {
optional ResponseStatus status = 1;
optional string statusText = 2;
optional uint64 ttl = 3; // in seconds
}
message Unregister {
optional string ns = 1;
// optional bytes id = 2; deprecated as per https://github.com/libp2p/specs/issues/335
}
message Discover {
optional string ns = 1;
optional uint64 limit = 2;
optional bytes cookie = 3;
}
message DiscoverResponse {
repeated Register registrations = 1;
optional bytes cookie = 2;
optional ResponseStatus status = 3;
optional string statusText = 4;
}
optional MessageType type = 1;
optional Register register = 2;
optional RegisterResponse registerResponse = 3;
optional Unregister unregister = 4;
optional Discover discover = 5;
optional DiscoverResponse discoverResponse = 6;
}
Rendezvous points should have well-defined configurations to enable libp2p nodes running the rendezvous protocol to have friendly defaults, as well as to guarantee the security and efficiency of a Rendezvous point. This will be particularly important in a federation, where rendezvous points should share the same expectations.
Regarding the validation of registrations, rendezvous points should have the following:
- a minimum acceptable ttl of
2H
- a maximum acceptable ttl of
72H
- a maximum namespace length of
255
Rendezvous points are also recommended to allow:
- a maximum of
1000
registration for each peer- defend against trivial DoS attacks
- a maximum of
1000
peers should be returned per namespace query