-
Notifications
You must be signed in to change notification settings - Fork 7
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
libp2p-webrtc-browser-to-server blog post #18
Changes from 2 commits
2286498
514db39
a627e96
31b1550
eac587c
fccf8b9
3da034a
ef35faf
e9b7253
23d6b5b
90d2674
9dac046
047bae8
a51f0c9
56093d2
09f8d98
b82bbdd
e990ab8
e824967
636e455
00c18e3
81876a4
1c39d6d
1ba1293
8c729d5
66fc8f3
2390141
6c4f12b
4c50e05
9b42b76
3c85be9
f6fbfd3
04a1514
99e0678
9196b43
9cfafb4
a6caa0d
75d1542
f6a7e43
07fc6aa
67774a5
260fb2c
acf7c8c
fd0b8ed
249d769
fc28c41
afa75de
b5f855c
fcfc991
67482d2
7459fae
60aa500
35eee32
f639c08
82631a6
527ba38
8e997f9
b3f188e
058bf88
2a1f04b
733fe9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
--- | ||
tags: | ||
- browser | ||
- transport | ||
- webrtc | ||
title: WebRTC in libp2p | ||
description: | ||
date: | ||
permalink: "" | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
translationKey: '' | ||
header_image: /_.png | ||
author: David Di Maria | ||
--- | ||
|
||
# WebRTC (Browser to Server) in libp2p | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
<!-- | ||
WebTransport in libp2p: Part 1 of Universal Browser Connectivity | ||
WebRTC (Browser to Server): Part 2 of Universal Browser Connectivity | ||
WebRTC (Browser to Browser): Part 3 of Universal Browser Connectivity | ||
--> | ||
This is the second entry in a series of posts on how libp2p achieves browser connectivity. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Read about the [first article on WebTransport here](https://blog.libp2p.io/2022-12-19-libp2p-webtransport/). | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
**Table of Contents** | ||
|
||
_IN DRAFT, NOT READY FOR REVIEW_ | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
[[toc]] | ||
|
||
## Overview | ||
|
||
The [libp2p project](https://libp2p.io) specifies many [transports](https://libp2p.io/implementations/#transports) developed in a variety of implementations (Rust, Go, and JavaScript). While connecting servers to servers has been around for a while via TCP and QUIC, there haven't been optimal solutions for connecting browsers to servers that work on a broad spectrum of browsers. Additionally, there was a need to establish browser-to-browser connectivity in a decentralized way. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Other transports laid the groundwork for a WebRTC implementation in libp2p, but had some shortcomings, including centralized signaling and the need for a trusted TLS certificate, that created a need for a formal specification. [Little Bear Labs](https://littlebearlabs.io/) teamed up with Protocol Labs and the libp2p community to define a specification and work on the SDK implementation. Protocol Labs authored the [Rust](https://github.com/libp2p/rust-libp2p) implementation, while Little Bear Labs focused on the [Go](https://github.com/libp2p/go-libp2p) and [JavaScript](https://github.com/libp2p/js-libp2p-webrtc) implementations. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Before we deep dive into the WebRTC implementation within libp2p, let's first understand what WebRTC is and how it's currently being used. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## WebRTC in the Browser | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
`Web Real-Time Communication`, commonly referred to as `WebRTC`, is a [set of standards](https://w3c.github.io/webrtc-pc/) that allows browsers, as well as other clients and servers, to connect to other peers in order to exchange audio, video, and data. In most cases, peers are directly connected to other peers, allowing for a more private experience and fewer hops than on a relay. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
While WebRTC handles audio, video, and data traffic, we're just going to focus on the data aspect because that's the API leveraged in libp2p-webrtc. | ||
|
||
WebRTC is built directlry into browsers, so using the API is a simple and straightfoward task. Peers connect to each other via a `RTCPeerConnection` interface. Once connected, `RTCDataChannels` can be added to the connection to send and receive binary data. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
![](https://i.imgur.com/Zv221BT.png) | ||
|
||
<!-- | ||
participantspacing 7 | ||
entryspacing 0.6 | ||
|
||
Peer A->STUN: Who Am I? | ||
STUN->Peer A: Symmetric Nat | ||
Peer A->TURN: Channel Please | ||
Peer A->Signaling Channel: Offer SDP | ||
Signaling Channel->Peer B: Offer SDP | ||
Peer B->STUN: Who Am I? | ||
STUN->Peer B: 159.225.242.189 | ||
Peer B->Signaling Channel: Answer SDP | ||
Signaling Channel->Peer A: Answer SDP | ||
|
||
|
||
Peer A->(1)Signaling Channel: DTLS Handshake | ||
Signaling Channel->(1)Peer B: DTLS Handshake | ||
Peer B->(1)Signaling Channel: DTLS Handshake | ||
Signaling Channel->(1)Peer A: DTLS Handshake | ||
|
||
|
||
Peer A<->Signaling Channel:Duplex Send/Receive SCTP data, encrypted with DTLS | ||
Signaling Channel<->Peer B:Duplex Send/Receive SCTP data, encrypted with DTLS | ||
--> | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
<div style="font-size: 1.25rem; text-align: center; margin-bottom: 1rem; font-style: italic;">2 browsers connecting via WebRTC, where Peer A has router restrictions (i.e. behind a firewall)</div> | ||
|
||
Peers use external [STUN](https://datatracker.ietf.org/doc/html/rfc3489) servers to determine their public address, as well as any router restrictions that prohibit peer-to-peer communications. In the case of a restriction, [TURN](https://datatracker.ietf.org/doc/html/rfc8656) servers are used to relay data between peers using a Signaling Channel. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Once IP addresses are obtained, a peer sends an Offer [SDP](https://datatracker.ietf.org/doc/html/rfc4566) to the other peer. This Offer SDP details the ways that the initiating peer can communicate (IP address, protocols, fingerprints, encryption,...etc.). The other peer sends an Answer SDP to the initiating peer. Both peers now have enough information to start the DTLS handshake. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The DTLS handshake is performed using fingerprints contained in the Offer and Answer SDPs. After the handshake is complete, data is sent between peers using the SCTP (Stream Control Transmission Protocol) protocol, encrypting messages with DTLS over UDP. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
## WebRTC in LibP2P | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
<!-- | ||
participantspacing 20 | ||
entryspacing 0.75 | ||
|
||
box over Server: Generate TLS Certificate | ||
box over Server: Listen on UDP Port | ||
box over Browser: Create RTCPeerConnection | ||
box over Browser: Create Server's Answer SDP | ||
box over Browser: Create Offer SDP | ||
Browser->Server:STUN Binding Request | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
box over Server: Create Browser's Offer SDP | ||
|
||
Server->(1)Browser:DTLS Handshake | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Browser->(1)Server:DTLS Handshake | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since WebRTC uses DTLS 1.2, the handshake takes 2 roundtrips. The diagram should probably reflect that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can add that level of fidelity. IIRC there are 3 RTs for DTLS (6 flights total), assuming no retransmissions. I'm wondering if it would be OK to add some messaging around this w/o 6 lines (noisy) 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a box beneath the handshake to provide info about the 3 RTs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I am not mistaken 3 round trips are only needed as a denial-of-service countermeasure, see https://www.rfc-editor.org/rfc/rfc6347#section-4.2.1.
I am not sure what the default in major implementations is, 3 RT or 2 RT. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the server here? I'm surprised to see a Hello Verify Request here. That's an anti-DoS measure in DTLS (similar to the Retry mechanism in QUIC, for those familiar with the QUIC spec). It adds 1 RTT to the handshake. If that's either rust-libp2p or go-libp2p, it would be worth investigating how / if we can turn this off. In terms of DoS defense, it doesn't buy us a lot here since we already create state when we receive the STUN packet, so doing a DoS defense in DTLS arguably comes to late. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The server is a Go libp2p server. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To summarize:
The discussion on whether to tackle the optimization to only send To move forward I suggest going with the conservative (current) 3 RTT (including - box over Browser,Server: Full DTLS Handshake is 3 round trips
+ box over Browser,Server: Full DTLS Handshake is 3 round trips (2 without `HelloVerifyRequest` DOS prottection) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change has been made. |
||
|
||
Server->(1)Browser:LibP2P Noise Handshake | ||
Browser->(1)Server:LibP2P Noise Handshake | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
Browser<->Server:Multiplex Send/Receive Framed Data | ||
--> | ||
![](https://i.imgur.com/fIg6zOh.png) | ||
|
||
Connecting to a server from a browser in the WebRTC implementation in libp2p has some similarities but is differnet in several ways. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
The server first generates a self-signed TLS certificate and listens on a UDP port for incoming STUN packets. Whether known upfront or discovered, the assembled multiaddress of the server is an input into the browser. | ||
|
||
The browser creates a [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection). Using the components in the server's multiaddress, the browser creates the server's Answer SDP. The SDP is edited, or `munged`, to include an auto-generated ufrag and password, as well as the server's components (IP and Port). Similarly, the browser creates an Offer SDP and munges it witj the same values. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Setting the Offer and Answer SDP on the browser triggers the sending of STUN packets to the server. The server then creates the browser's Offer SDP using the values in the STUN Binding Request. | ||
|
||
The browser and server then engage in a DTLS handshake, opening the UDP Data Channel. Since the server does not know the TLS certificate of the browser, a [Noise handshake](https://noiseprotocol.org/noise.html) is initiated by the server using the fingerprints in the SDP and completed by the browser over the Data Channel. DTLS-encrypted SCTP data is now ready to be exchanged over the UDP socket. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### Multiaddress | ||
|
||
The [multiaddress](https://docs.libp2p.io/concepts/fundamentals/addressing/) of a WebRTC address begins like a standard UDP address, but adds 3 additional components: `webrtc`, `hash`, and `p2p`. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```shell | ||
/ip4/1.2.3.4/udp/1234/webrtc/certhash/<hash>/p2p/<peer-id> | ||
``` | ||
|
||
* `webrtc`: the name of this transport | ||
* `hash`: the [multihash](https://github.com/multiformats/multihash) of the libp2p node | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* `p2p`: the peer-id of the libp2p node (optional) | ||
|
||
### Benefits | ||
|
||
#### Self-Signed Certificate | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
WebRTC enables browsers to connect to public libp2p nodes without the nodes requiring a TLS certificate in the browser's [certificate chain](https://en.wikipedia.org/wiki/X.509#Certificate_chains_and_cross-certification). Because the server can use a self-signed TLS certificate, WebRTC removes the need to include additional services like DNS and Let's Encrypt. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### Peer-to-Peer | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
WebRTC allows for peer-to-peer connections, opening up the browser-to-browser use case in libp2p. While this [specification](https://github.com/libp2p/specs/pull/497) is still a work in progress, the potential is very exciting as no other non-WebRTC transport offers this. | ||
|
||
#### Broad Support | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Chrome has supported WebRTC since 2012. Other browsers soon followed, achieving support [on all evergreen browsers](https://caniuse.com/?search=webrtc). WebRTC is literally everywhere. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### Signaling Removed | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
In contrast to standard WebRTC siglaling, you might notice signaling is completely removed in libp2p browser-to-server communication and that Signal Channels aren't needed. Removing singaling results in fewer roundrips needed to establish a Data Channel as well as the added complexity of creating signaling. Additionally, in situations in standard WebRTC where Signal Channels were needed due to router restrictions, latency is lowered on all traffic using direct communication in libp2p. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### Limitations | ||
|
||
#### Setup and Configuration | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Because WebRTC represents a collection of technologies, it requires extensive setup and configuration when compared to other transports. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### Usage | ||
|
||
The complexity of WebRTC is abstracted away in the implementations, making it seamless to swap out your existing transport with WebRTC. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Let's look at the JavaScript implementation as an example: | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
```javascript | ||
import { webRTC } from 'js-libp2p-webrtc' | ||
|
||
const node = await createLibp2p({ | ||
transports: [webRTC()], | ||
connectionEncryption: [() => new Noise()], | ||
}); | ||
``` | ||
|
||
The only difference from other transports is initializing with `webRTC()`. That's all you need to do to implement WebRTC in the browser. Easy, right? | ||
|
||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Alternative Transports | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
WebRTC isn't the only way to connect browsers to a libp2p node. [Choosing the transport](https://connectivity.libp2p.io/) that fit's your use case is one of the many unique strengths of libp2p. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### WebSocket | ||
|
||
The [WebSocket RFC](https://datatracker.ietf.org/doc/html/rfc6455) dates back to 2011 and specifies the opening of a 2-way socket from a browser to a server over TCP. WebSocket is implemented in the [Rust](https://github.com/libp2p/rust-libp2p/tree/master/transports/websocket), [Go](https://github.com/libp2p/go-libp2p/tree/master/p2p/transport/websocket), and [JavaScript](https://github.com/libp2p/js-libp2p-websockets) implementations. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
#### Benefits | ||
|
||
Since the WebSocket transport is already implemented in libp2p, it can be used today in a browser-to-server scenario. WebSockets are well supported in browsers and are easy to implement. Using TCP, WebSockets are more reliable than WebRTC's UDP. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### Limitations | ||
|
||
WebSockets run over TCP, which is inherently slower than WebRTC's UDP. The various upgrades and handshakes add up to 6 round trips before data can be exchanged. Additionally, while WebRTC can leverage self-signed certificates, WebSockets cannot as they require the server to have a trusted TLS certificate using TCP. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### WebTransport | ||
|
||
[WebTransport](https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-overview) is the new kid on the block for real-time communication in the browser. WebTransport is implemented in the [Rust](https://github.com/libp2p/rust-libp2p/tree/master/transports/quic), [Go](https://github.com/libp2p/go-libp2p/tree/master/p2p/transport/webtransport), and [JavaScript](https://github.com/libp2p/js-libp2p-webtransport) implementations. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### Benefits | ||
|
||
WebTransport has many of the same features of WebRTC (fast, secure, multiplexed), but without requiring servers to implement the WebRTC stack. Tney also get around the valid TLS certificate requirement. After the noise handshake completes, libp2p can use raw WebTransport streams and avoid the need for double encryption. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There’s no double encryption in WebRTC either. I assume you mean to contrast this with WebSocket?
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### Limitations | ||
|
||
You might be asking yourself, why pick WebRTC over WebTransport in libp2p? It's like WebRTC, but easier to implement and with less complexity. WebTransport is not without it's limitations. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
WebTransport isn't supported [in all browsers](https://caniuse.com/webtransport). This lack of support is a big concern as it's unreasonable to expect users to _not_ use Firefox or Safari, or even older versions of Chromium-based browsers. | ||
|
||
Another issue is that browsers utilizing the WebTransport API can only connect to servers. This severly limits the utility of the transport as browser-to-browser communication is critical to closing the loop on full interopability in libp2p. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Prior WebRTC Implementations | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The new WebRTC transport was built on the shoulder of giants. These legacy transports proved that WebRTC was a viable solution for libp2p. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### libp2p-webrtc-star | ||
|
||
libp2p-webrtc-star was released earlier this year. This transport utilizes centralized STUN and TURN servers to handle signaling, and was [never intended](https://github.com/libp2p/js-libp2p/issues/385) to be a long-term solution. The repository was archived in November in favor of the new js-libp2p-webrtc tranport due to the dependance on centralized servers. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### libp2p-webrtc-direct | ||
libp2p-webrtc-direct utizes websockets to exchange SDPs, removing the need for the centralzed dependency in libp2p-webrtc-star. While the centralized problem was solved, the servers need to have valid TLS certificates for websocket connectivity. The repository was archived in November. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Can I use WebRTC Now? | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Yes, you can use libp2p-webrtc in the [Rust](https://github.com/libp2p/rust-libp2p) and [JavaScript](https://github.com/libp2p/js-libp2p-webrtc) implementations! The [Go](https://github.com/libp2p/go-libp2p) implementation is close to complete. Follow the [PR](https://github.com/libp2p/go-libp2p/pull/1655) to get notified when merged. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## What's Next? | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
WebRTC offers the capability for browsers to connect to each other 🎉. This isn't currently possible in any of the active libp2p transports and represents a major achievement in libp2p. | ||
p-shahi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The [WebRTC browser-to-browser connectivity spec](https://github.com/libp2p/specs/pull/497) is currently being authored and development will soon start. Follow the [PR](https://github.com/libp2p/specs/pull/497) for up-to-date information. | ||
ddimaria marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't forget to add a date. Otherwise it seems to show up as the last post on the blog.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@p-shahi should we populate this now, or is it's population part of a release prcoess?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a single update right before merging is the way to go. I am not aware of a formal "release process".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ddimaria This is still missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I read @mxinden's comment as the date should be added immediately before merging, so this is intentionally unaddressed until we get the green light to merge. If you have a date that you think it will be merged ahead of time, I can add that in? I can optionally add today's date and just update again prior to merging? I don't have a strong opinion on any approach.