Note: This specification is under active development and therefore incomplete. If you’re looking for an overview of the proposal, please refer to the Explainer .
1. Introduction
This section is non-normative.
The Local Peer-to-Peer API aims to give browsers the means to communicate directly, without the aid of a server in the middle. It is designed to enable this communication within the confines of a local communication medium such as the Local Area Network.
Many modern Web security measures rely on the presence of naming, signaling and certificate authorities. Local use-cases where these authorities are not readily available have started lagging behind in user experience or are not supported altogether. The Local Peer-to-Peer API aims to bring back first-class support for local communication use-cases while working within the same strict user-friendliness, security and privacy requirements.
Examples of potential uses of this API include: Collaboration tools that work during an internet outage or emergency situations, connecting to your NAS, your home security system, your robotic assistant doing the dishes or your GPU farm in the basement that’s running your personalized virtual assistant. See also Use Cases .
This specification aims to strike a balance between creating a powerful new building block for developers and providing a seamless, secure and privacy preserving experience for browser users. As an example: while the API doesn’t provide raw socket access, it does aim to give developers the flexibility to innovate on top by providing a persistent, two-way communication channel with little overhead.
The API is designed to be backed by an authenticated, streams-based transport. As a commitment to an open standards-based implementation path, this specification describes how the API can be implemented on top of the Open Screen Protocol . While not described here, the API is expected to be implementable on top of other transports when technically feasible.
2. Permission Policy Integration
The
Local
Peer-to-Peer
API
defines
a
policy-controlled
feature
identified
by
the
token
"local-peer-to-peer".
Its
default
allowlist
is
"self"
.
Workers (dedicated and shared) adhere to the permission policy set by their owning document(s):
- Dedicated workers can be created from other workers, in which case the permission policy of the first owning document (in case of a dedicated worker) or owning documents (in case of a shared worker) up the owner chain will be used.
- Shared workers often have multiple owning documents as they can be obtained by other documents with the same origin . In this case, all owning documents must be allowed to use the policy-controlled feature defined by this specification.
Note: There has been discussion on allowing setting permission policy directly on a worker on creation, in which case that would have to be consulted as well.
The
default
allowlist
of
"self"
allows
usage
in
same-origin
nested
frames
but
prevents
third-party
content
from
using
the
feature.
Third-party
content
usage
can
be
selectively
enabled
by
adding
allow="local-peer-to-peer"
attribute
to
the
frame
container
element:
< iframe src= "https://third-party.com" allow= "local-peer-to-peer" />< /iframe>
Alternatively, the Local Peer-to-Peer API can be disabled completely by specifying the permissions policy in a HTTP response header:
Permissions- Policy: local- peer- to- peer= ()
See [PERMISSIONS-POLICY] for more details.
3. Peer Management
Note: This section and its subsections use RFC 2119 terminology in a relaxed manner. These sections will be converted into well-defined algorithmic normative prose informed by further implementation experience. For now, this relaxed description better allows for rapid prototyping.
The user agent is in charge of managing peers.
A peer is an equal participants in the network that forms a peer-to-peer network of nodes. These nodes can communicate without the need for a central coordination by a server.
A user agent has an associated local peer-to-peer manager in charge of managing peers . Its responsibility is to start local peer discovery , establish local peer connections and acquire a local peer grant on a per-origin basis. This is done to avoid exposing information about a user’s local network topology.
3.1. Known peers
The user agent should maintain a list of peers that it has knowledge of.
The local peer-to-peer manager has an associated known peers map , an ordered map of known peers . Each known peer is a peer that has an associated authentication state and peer grant state . The user agent keeps track of the authentication state per peer and the peer grant state per peer, per origin. Unless persisted , both the states are initially false.
Note: The peer grant state is origin-level while the authentication state is peer-level. The rationale is to not require the user to undergo authentication multiple times between the same peers. This design may change informed by security and privacy considerations.
See also related [Issue #wicg/local-peer-to-peer#24]
The user agent may persist known peers, their authentication states and/or peer grant states . If the user agent chooses to do so, it must do so in accordance with the Open Screen Protocol Persistent State rules. Such a peer or its state is said to be persisted .
3.2. Peer advertisement
The user agent can make itself discoverable by advertising using the advertise agent capability.
Note: The user agent must get the user’s explicit consent in order to start advertising.
When advertising, the user agent must listen for incoming peer connections. In case a connection is received, the peer is added to the known peers map . The user agent must not directly provide access to the incoming peer connection to any origin. Instead, the user agent must prompt to grant peer access to the origin that initiated peer advertisement.
3.3. Peer discovery
The user agent can discover local peers using the discover agents capability.
When asked to start local peer discovery , the user agent discovers local peers using the discover agents capability.
If a peer is discovered, the user agent should add it to the known peers map . The user agent must never expose the full result of peer discovery with an origin. Instead, the user agent must acquire a local peer grant to grant access to a peer for the origin that initiated the peer request.
3.4. Peer authentication
The user agent can initiate authentication with a peer using the authenticate an agent capability.
When asked to authenticate a local peer , the user agent must initiate authentication with a local peer using the authenticate an agent capability. The peer’s authentication state must be set to true if authentication succeeds, otherwise false.
A peer is said to be authenticated when its authentication state is true.
3.5. Peer grant
The user agent, with the user’s consent, must acquire a local peer grant for an origin to get access to a peer.
Note: The user agent must get the user’s explicit consent in order to grant an origin access to a peer. For enhanced privacy protection, the user agent must provide means to dismiss any related user interface and this action must not be detectable by script or have script-observable side-effects. The user interface may provide means to revoke the grant or to make the grant persisted .
When asked to acquire a local peer grant , the user agent displays the known peers to the user through its native user interface. When the user selects a peer from the user interface, the user agent must check the selected peer is authenticated , and run the authenticate a local peer algorithm otherwise, and set the peer grant state according to the user’s explicit selection.
4. Authentication
The core concept of the local peer to peer is authentication. Modern secure communications protocols, such as TLS 1.3 [RFC8446] rely on certificates for authentication. When connecting over the internet, the Public Key Infrastructure establishes a trust anchor to validate certificates. This specification introduces a way to establish a trust anchor between two peers on the local network, in the absence of a central authority. Authentication establishes a mutual trust anchor between peers, as represented by a an X.509 v3 certificate.
4.1. PAKE
The main method for authentication is by means of Password-Authenticated Key Agreement (PAKE).
Integrate with peer-authentication section above.
4.2. Signaling
In the context of local peer to peer, signaling is a way to automate the Password-Authenticated Key Agreement in case two peers share a earlier establisched communication channel. An application may use this to avoid a manual authentication interaction.
detail signaling protocol & API signature.
4.3. Certification Path Validation
X.509 Certification Path Validation can be used to validate the chain of trust to an earlier establisched trust anchor. A user agent may use this to establish longer lived trust anchors, reducing the number of manual authentication actions required.
4.4. Other methods
Other means of authentication may be supported. One noteworthy example is the use of an existing Matter Fabric .
See also related [Issue #w3c/openscreenprotocol#308]
5. Protocol concepts
When asked to establish local peer connection , the user agent ...
A Local Peer-to-Peer session represents an authenticated QUIC connection as defined in Open Screen Protocol or OSP.
A Local Peer-to-Peer session has the following capabilities:
capability | definition |
---|---|
advertise agent | [openscreenprotocol] discovery |
discover agents | [openscreenprotocol] discovery |
discover metadata | [openscreenprotocol] transport |
authenticate an agent | [openscreenprotocol] authentication |
open a data channel |
|
send data on a channel |
|
open a WebTransport session |
|
Part of these capabilities are defined below as protocol extensions to the Open Screen Protocol .
4.1.
5.1.
Data
channel
extension
In order to signal support for this protocol extension, the agent should include the data-channels agent-capability as part of the agent-info-response message exchanged during discover metadata .
To open a data channel an agent may send a data-channel-open-request message on a new QUIC stream. The Local Peer-to-Peer session must be authenticated. The message must contain the following values:
- channel-id
-
An ID number (between 0 and 65,534) which uniquely identifies the data channel. Must not be empty.
- label
-
a string that contains a name describing the data channel. These labels are not required to be unique.
- protocol
-
a string containing the name of the subprotocol in use. If no protocol was specified when the data channel was created, then this property’s value is the empty string ("").
When the receiver receives the data-channel-open-request , it should send back a data-channel-open-response message. The response must include the following:
- result
-
a code indicating success or failure, and the reason for the failure.
If the data-channel-open-response message indicates success, the data channel is considered open. Agents can now send data on a channel by sending data-frame messages on the same QUIC stream the data channel was opened. The message must include the following:
- encoding-id
-
Determines the encoding of the data being sent. The values are specified as data-channel-encoding-id : 0: Blob ; 1: String ; 2: ArrayBuffer .
- payload
-
The binary representation of the data being sent.
4.2.
5.2.
WebTransport
extension
The protocol extension to send data on a channel provides an ergonomic way to send simple messages. An agent can open a WebTransport session for communication that requires low overhead and more granular streams control.
In order to signal support for this protocol extension, the agent should include the quick-transport agent-capability as part of the agent-info-response message exchanged during discover metadata .
An agent can open a WebTransport session by dialing a new QUIC connection using the agent certificates established during authenticate an agent . During connection establishment, the ALPN token "q2q" must be used in the TLS handshake.
The capabilities of the Local WebTransport session are defined in [webtransport] .
Note: The WebTransport-over-QUIC protocol is yet to be defined. Potentially considering earlier work such as draft-vvv-webtransport-quic .
5.
6.
LP2PReceiver
Interface
The
LP2PReceiver
interface
allows
advertising
on
the
local
network,
enabling
other
peers
to
discover
and
connect.
[Exposed =(Window ,Worker ),SecureContext ]interface LP2PReceiver :EventTarget {(
constructor optional LP2PReceiverOptions = {});
options attribute EventHandler onconnection ;Promise <undefined >(); };
start
5.1.
6.1.
LP2PReceiverOptions
[Exposed =(Window ,Worker ),SecureContext ]dictionary {
LP2PReceiverOptions DOMString ; };
nickname
5.2.
6.2.
LP2PConnectionEvent
In general, when defining a new interface that inherits from Event please always ask feedback from the WHATWG or the W3C WebApps WG community. See defining event interfaces .
[Exposed =(Window ,Worker ),SecureContext ]interface :
LP2PConnectionEvent Event {(
constructor DOMString ,
type LP2PConnectionEventInit );
connectionEventInitDict readonly attribute LP2PConnection ; };
connection dictionary :
LP2PConnectionEventInit EventInit {required LP2PConnection ; };
connection
5.3.
6.3.
Events
The
following
event
is
fired
at
the
LP2PReceiver
object:
Event name | Interface | Fired when… |
---|---|---|
connection
|
LP2PConnectionEvent
| An incoming connection is received. |
5.4.
6.4.
Event
handlers
The following are the event handlers (and their corresponding event handler event types ) that must be supported, as event handler IDL attributes , by all objects implementing the LP2PReceiver interface:
event handler | event handler event type |
---|---|
onconnection
|
connection
|
5.5.
6.5.
Examples
Example: Setting up a receiver to listen for connections:
const receiver= new LP2PReceiver({ nickname: "example-receiver" , }); receiver. onconnection= e=> { console. log( "Connection established!" ); const conn= e. connection; }; // Blocks until permission is received. await receiver. start();
6.
7.
The
LP2PRequest
Interface
The
LP2PRequest
interface
represents
a
request
for
a
connection
to
another
local
peer.
[Exposed =(Window ,Worker ),SecureContext ]interface LP2PRequest {(
constructor optional LP2PRequestOptions = {});
options Promise <LP2PConnection >(); };
start
6.1.
7.1.
LP2PRequestOptions
[Exposed =(Window ,Worker ),SecureContext ]dictionary {
LP2PRequestOptions DOMString ; };
nickname
6.2.
7.2.
Examples
Example: Setting up a request for a connection:
const request= new LP2PRequest({ nickname: "example-request" , }); // Blocks until connection is received. const conn= await request. start(); console. log( "Connection established!" );
7.
8.
The
LP2PConnection
Interface
The
LP2PConnection
interface
represents
a
connection
with
another
local
peer.
[Exposed =(Window ,Worker ),SecureContext ]interface LP2PConnection :EventTarget { };
7.1.
8.1.
LP2PQuicTransport
Interface
Extensions
This
LP2PQuicTransport
extension
allows
opening
a
QuicTransport
using
an
existing
connection.
In
this
case,
the
already
established
permission
and
grants
should
be
used.
[Exposed =(Window ,Worker ),SecureContext ]partial interface LP2PQuicTransport {(
constructor LP2PConnection ,
connection optional LP2PQuicTransportInit = {}); };
quicTransportDict
7.2.
8.2.
Examples
Example:
Open
a
LP2PQuicTransport
using
an
existing
connection
as
receiver:
const receiver= new LP2PReceiver({ nickname: "example-receiver" , }); receiver. onconnection= async e=> { const conn= e. connection; const transport= new LP2PQuicTransport( conn); // Blocks until transport is ready. await transport. ready; }; // Blocks until permission is received. await receiver. start();
Example:
Open
a
LP2PQuicTransport
using
an
existing
connection
as
requester:
const request= new LP2PRequest({ nickname: "example-request" , }); // Blocks until connection is received. const conn= await request. start(); const transport= new LP2PQuicTransport( conn); // Blocks until transport is ready. await transport. ready;
8.
9.
The
LP2PDataChannel
Interface
The LP2PDataChannel interface represents a bi-directional data channel between two peers. An LP2PDataChannel is created via a factory method on a LP2PConnection object.
Note: The LP2PDataChannel interface is purposefully kept as close as possible to the RTCDataChannel interface defined in [webrtc] . The aim is to allow seamless transition of developers that are familiar with WebRTC as well as allowing libraries to easily work with both the [webrtc] and LP2P API.
[Exposed =(Window ,Worker ),SecureContext ]interface :
LP2PDataChannel EventTarget {(
constructor USVString ,
label optional LP2PDataChannelInit = {});
dataChannelDict readonly attribute USVString ;
label readonly attribute USVString ;
protocol readonly attribute unsigned short ?;
id attribute EventHandler onopen ;attribute EventHandler onerror ;attribute EventHandler onclosing ;attribute EventHandler onclose ;undefined ();
close attribute EventHandler onmessage ;attribute BinaryType ;
binaryType undefined ((
send USVString or Blob or ArrayBuffer or ArrayBufferView )); };
data
8.1.
9.1.
LP2PDataChannelInit
[Exposed =(Window ,Worker ),SecureContext ]dictionary {
LP2PDataChannelInit USVString = ""; [
protocol EnforceRange ]unsigned short ; };
id
8.2.
9.2.
Events
The
following
event
is
fired
at
the
LP2PDataChannel
object:
Event name | Interface | Fired when… |
---|---|---|
open
|
Event
| The data channel is opened. |
message
|
MessageEvent
[html]
| An incoming message is received. |
error
|
Event
| An error occurred. |
closing
|
Event
| The data channel is closing. |
close
|
Event
| The data channel is closed. |
8.3.
9.3.
Event
handlers
The following are the event handlers (and their corresponding event handler event types ) that must be supported, as event handler IDL attributes , by all objects implementing the LP2PDataChannel interface:
event handler | event handler event type |
---|---|
onopen
|
open
|
onmessage
|
message
|
onerror
|
error
|
onclosing
|
closing
|
onclose
|
close
|
8.4.
9.4.
LP2PConnection
Interface
Extensions
[Exposed =(Window ,Worker ),SecureContext ]partial interface LP2PConnection {LP2PDataChannel (
createDataChannel USVString ,
label optional LP2PDataChannelInit = {});
dataChannelDict attribute EventHandler ondatachannel ; };
8.5.
9.5.
LP2PDataChannelEvent
In general, when defining a new interface that inherits from Event please always ask feedback from the WHATWG or the W3C WebApps WG community. See defining event interfaces .
[Exposed =(Window ,Worker ),SecureContext ]interface :
LP2PDataChannelEvent Event {(
constructor DOMString ,
type LP2PDataChannelEventInit );
DataChannelEventInitDict readonly attribute LP2PDataChannel ; };
channel dictionary :
LP2PDataChannelEventInit EventInit {required LP2PDataChannel ; };
channel
8.6.
9.6.
Extensions
Events
The
following
event
is
fired
at
the
LP2PConnection
object:
Event name | Interface | Fired when… |
---|---|---|
datachannel
|
LP2PDataChannelEvent
| An incoming data channel is received. |
8.7.
9.7.
Extensions
Event
handlers
The
following
are
the
event
handlers
(and
their
corresponding
event
handler
event
types
)
that
must
be
supported,
as
event
handler
IDL
attributes
,
by
all
objects
implementing
the
LP2PConnection
interface:
event handler | event handler event type |
---|---|
ondatachannel
|
datachannel
|
8.8.
9.8.
Examples
Example:
Receive
a
LP2PDataChannel
on
an
existing
connection
as
receiver:
const receiver= new LP2PReceiver({ nickname: "example-receiver" , }); receiver. onconnection= e=> { const conn= e. connection; console. log( "Receiver: Got a connection!" ); conn. ondatachannel= e=> { const channel= e. channel; channel. onmessage= e=> { const message= e. data; console. log( `Receiver: Received message: ${ message} ` ); }; channel. send( "Good day to you, requester!" ); }; }; await receiver. start();
Example:
Create
a
LP2PDataChannel
on
an
existing
connection
as
requester:
const request= new LP2PRequest({ nickname: "example-request" , }); const conn= await request. start(); console. log( "Requester: Got a connection!" ); const channel= conn. createDataChannel( "My Channel" ); channel. onopen= e=> { channel. onmessage= e=> { const message= e. data; console. log( `Requester: Received message: ${ message} ` ); }; channel. send( "Good day to you, receiver!" ); };
9.
10.
The
LP2PQuicTransport
Interface
The
LP2PQuicTransport
Interface
allows
opening
a
[webtransport]
between
peers.
If
the
LP2PRequest
or
LP2PReceiver
is
not
yet
started,
it
must
be
started
when
the
LP2PQuicTransport
is
constructed.
[Exposed =(Window ,Worker ),SecureContext ]interface LP2PQuicTransport :WebTransport {((
constructor LP2PRequest or LP2PReceiver ),
source optional LP2PQuicTransportInit = {}); }; [
quicTransportDict Exposed =(Window ,Worker ),SecureContext ]dictionary { };
LP2PQuicTransportInit
Define LP2PQuicTransportInit as needed using WebTransportOptions as reference.
9.1.
10.1.
LP2PQuicTransportListener
Interface
The
LP2PQuicTransportListener
Interface
allows
listening
for
incoming
[webtransport]
transports.
If
the
LP2PRequest
or
LP2PReceiver
is
not
yet
started,
it
must
be
started
when
the
LP2PQuicTransport
is
constructed.
[Exposed =(Window ,Worker ),SecureContext ]partial interface LP2PQuicTransportListener {((
constructor LP2PRequest or LP2PReceiver ),
source optional LP2PQuicTransportListenerInit = {});
quicTransportListenerDict readonly attribute Promise <undefined >; /* a ReadableStream of LP2PQuicTransport objects */
ready readonly attribute ReadableStream ; }; [
incomingTransports Exposed =(Window ,Worker ),SecureContext ]dictionary { };
LP2PQuicTransportListenerInit
Define LP2PQuicTransportListenerInit as needed using WebTransportOptions as reference.
9.2.
10.2.
Examples
Example: Setting up a request for a LP2PQuicTransport:
const request= new LP2PRequest({ nickname: "example-request" , }); const transport= new LP2PQuicTransport( request); // Blocks until transport is ready. await transport. ready;
Example: Receiving a LP2PQuicTransport:
const receiver= new LP2PReceiver({ nickname: "example-receiver" , }); const listener= new LP2PQuicTransportListener( receiver); for await ( const transportof listener. incomingTransports) { // Blocks until transport is ready. await transport. ready; }
Refer to the WebTransport examples for usage of a [webtransport] object.
Appendix A: OSP Extension Messages
The following messages are defined according to and as an extension to the Open Screen Protocol Messages .
Note: The type keys and capability IDs for these extensions are not officially registered yet. They will be registered as this specification matures.
agent-capability = & ( data-channels : 11 OO quick-transport : 12 OO ) data-channel-encoding-id = & ( encoding-id-blob : 0 encoding-id-string : 1 encoding-id-array-buffer : 2 ) ; type key 24 data-frame = { 0 : data-channel-encoding-id ; encoding-id 4 : bytes; payload } ; type key 1101 data-channel-open-request = { request 1 : uint; channel-id 2 : text; label 3 : text; protocol } ; type key 1102 data-channel-open-response = { response 1 : & result ; result }