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.
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. Terminology
The term local communication medium refers to a communication medium that provides a local, direct communication channel between the communicating devices. This is a purposefully broad term that covers any medium that can be used to establish a an authenticated, streams-based transport between the devices. Examples may include but are not limited to: a wired or wireless local area network, a Thread network, Bluetooth Low Energy or Wi-Fi Direct. This spec will not mandate which underlying technologies must be supported.
The term local communication refers to data communication over the local communication medium.
The term communication medium topology refers to the composition of the local communication medium which may include sensitive information such as IP or other address information or the number of devices connected to the medium.
3. 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.
4. 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 participant in the local communication. 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 the communication medium topology.
4.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.
4.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.
4.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.
4.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.
4.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.
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 | § 5.1 Data channel extension |
send data on a channel | § 5.1 Data channel extension |
open a WebTransport session | § 5.2 WebTransport extension |
Part of these capabilities are defined below as protocol extensions to the Open Screen Protocol.
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.
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.
6. Local HTTPS
Authentication establishes a mutual trust anchor between peers on the local communication medium. This trust anchor can also be used to validate certificates used by HTTP servers on the local communication medium. An Open Screen Protocol agent certificate can be identified by the common name of its subject ending in "._openscreen._udp". When a user agent loads a webpage and finds an agent certificate in the certificate chain, it can use it as a trust anchor for authentication of the server. If the corresponding agent is not authenticated, the user agent should prompt the user to authenticate the agent before proceeding with certificate validation.
6.1. Endpoint discovery
If a peer is authenticated, it may advertize one or more HTTPS endpoints it provides. A user agent may display these to the user to connect to servers on the local communication medium without knowledge of the corresponding hostname and/or IP address.7. LP2PReceiver Interface
The LP2PReceiver
interface allows advertising on the local communication medium, enabling other peers to discover and connect.
[Exposed =(Window ,Worker ),SecureContext ]interface LP2PReceiver :EventTarget {(
constructor optional LP2PReceiverOptions = {});
options attribute EventHandler onconnection ;Promise <undefined >(); };
start
7.1. LP2PReceiverOptions
[Exposed =(Window ,Worker ),SecureContext ]dictionary {
LP2PReceiverOptions DOMString ; };
nickname
7.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
7.3. Events
The following event is fired at the LP2PReceiver
object:
Event name | Interface | Fired when… |
---|---|---|
connection
| LP2PConnectionEvent
| An incoming connection is received. |
7.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
|
7.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();
8. 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
8.1. LP2PRequestOptions
[Exposed =(Window ,Worker ),SecureContext ]dictionary {
LP2PRequestOptions DOMString ; };
nickname
8.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!" );
9. The LP2PConnection Interface
The LP2PConnection
interface represents a connection with another local peer.
[Exposed =(Window ,Worker ),SecureContext ]interface LP2PConnection :EventTarget { };
9.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
9.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;
10. 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
10.1. LP2PDataChannelInit
[Exposed =(Window ,Worker ),SecureContext ]dictionary {
LP2PDataChannelInit USVString = ""; [
protocol EnforceRange ]unsigned short ; };
id
10.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. |
10.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
|
10.4. LP2PConnection Interface Extensions
[Exposed =(Window ,Worker ),SecureContext ]partial interface LP2PConnection {LP2PDataChannel (
createDataChannel USVString ,
label optional LP2PDataChannelInit = {});
dataChannelDict attribute EventHandler ondatachannel ; };
10.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
10.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. |
10.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
|
10.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!" ); };
11. 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.
11.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.
11.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.
12. Security and privacy considerations
12.1. Open Screen Protocol
The Local Peer-to-Peer API is meant to be implementable on the Open Screen Protocol. The Security and Privacy considerations of the Open Screen Protocol must therefore be considered when implementing this API.12.2. Personally identifiable information
The peer attributes, such as the nickname and device model, are provided to give minimal context to a connected peer. This information could be used in conjunction with other information for fingerprinting the user. However, this information is only available to an origin after it has been authenticated and a user has given explicit consent to make the connection to the remote peer.No information is exposed to the origin during service discovery. No IP information is exposed to an origin. This is fully managed by the user agent.
Refine peer attributes, related: [Issue #WICG/local-peer-to-peer#15]
12.3. User interface guidelines
When the user is asked to grant permission to connect to a peer, the user agent should make it clear what origin the request is coming from.
Define filtering to provide additional context, related: [Issue #WICG/local-peer-to-peer#15]
12.4. Impact on same-origin policy
This document extends the Web platform with the ability to set up real-time, direct communication between browsers and other devices, including other browsers, within a local communication medium.
This means that data and media can be shared between applications running in different browsers, or between an application running in the browser and another user agent that is not a browser such as a headless service provided by a smart TV or smart fridge. This extends the usual barriers in the Web’s security model that prevents sending data between entities with different origins.
12.5. Device Access
The Local Peer-to-Peer API requires user permission for a page to access any peers. The API uses purpose-fit protocols for data communication. It cannot be used to connect to raw sockets or unknowing HTTP servers. In addition, a user must acquire a local peer grant by explicitly providing consenting for an origin to connect to a peer. In addition, a user must authenticate a local peer before use.
12.6. Persistent State
The persisted state considerations of the Open Screen Protocol must be followed when implementing this API.12.7. Secure Contexts
The Local Peer-to-Peer API must only be provided in a secure context.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 }