🗺 What's left for release
<List of items with remaining PRs and/or Issues to be considered for this release>
🔦 Highlights
WebRTC Direct v2
Chromium's WebRTC-NoSdpMangleUfrag field trial takes away the SDP munging that /webrtc-direct v1 depends on. Once it hits stable, browsers can no longer dial a v1 go-libp2p server, which is the entire point of the transport. #3520 implements v2 (libp2p/specs#715): the browser's own ICE password rides in the server ufrag, so the server recovers it from the STUN USERNAME instead of predicting it.
The listener accepts v1 and v2 on the same port and picks the flow from the ICE username fragment prefix, so servers need no config change. Go dialers still default to v1; webrtc.WithDialerVersion(2) opts into v2. Browsers will get v2 from js-libp2p (libp2p/js-libp2p#3480).
Stable /certhash for WebRTC Direct
webrtc-direct used to mint a random DTLS cert at every process start, so the /certhash in the listen multiaddr changed on every restart and invalidated everything that had pinned it: peerstore TTLs, DHT records, external address books. #3512 derives the cert from the host key with HKDF, so the certhash depends only on that key. A node upgrading to v0.49 changes its certhash once, then keeps it.
The same PR fixes a serial number that could come out negative, which RFC 5280 forbids and x509.CreateCertificate rejects. webtransport shared that bug and is fixed too.
WebSocket listeners can share their port with an HTTP handler
websocket.WithHTTPHandler(http.Handler) makes a /ws or /tls/ws listener answer non-upgrade requests with your handler instead of a 404, on the same TCP port. websocket.WithHTTPServerConfig(func(*http.Server)) tunes that server's timeouts and HTTP/2 settings. Both are opt-in and default behavior is unchanged. #3509
Two things this buys you: a libp2p WebSocket port that also serves an ordinary HTTPS site, which is much harder to censor on 443, and an HTTP API served under the AutoTLS cert. Kubo exposes its trustless gateway that way in ipfs/kubo#11333.
WebTransport ready for the next browser draft
WebTransport over HTTP/3 is still an IETF draft, and draft-15 renamed both values that identify a session on the wire: the Extended CONNECT :protocol token (webtransport becomes webtransport-h3) and the SETTINGS codepoint (0x2b603742 becomes 0x2c7cf000). Every shipping browser still sends the old pair, but Firefox is already implementing the new one (mozilla/neqo#3646), and a server that only speaks the old one stops answering the day that lands.
webtransport-go v0.11.1 advertises and accepts both spellings, so a v0.49 server keeps working with today's browsers and with tomorrow's, without a config change. It also sends the SETTINGS_WT_MAX_SESSIONS value Safari 26.4+ expects.
One regression, and it is go-to-go only: a v0.49 node can no longer dial a peer on older go-libp2p over WebTransport. Those peers can still dial it. In practice this is invisible, because go-to-go connections use /quic-v1, which nodes announce on the same UDP port and the dial ranker ignores webtransport address if a quic address is available for the same (IP, Port). WebTransport is there for browsers, and browsers are unaffected.
AutoNAT v2 confirmed addresses
This release fixes a bug with the address reachability logic where /webrtc-direct addresses were dropped on a shared UDP port. A node that AutoNAT had just confirmed reachable over webrtc-direct previously reported that it was not. This release fixes this correctly advertising /webrtc-direct address if it's reachable.
Fewer stale and junk addresses
Changelog
< changelog generated by scripts/mkreleaselog >
✅ Release Checklist
🗺 What's left for release
<List of items with remaining PRs and/or Issues to be considered for this release>
🔦 Highlights
WebRTC Direct v2
Chromium's
WebRTC-NoSdpMangleUfragfield trial takes away the SDP munging that/webrtc-directv1 depends on. Once it hits stable, browsers can no longer dial a v1 go-libp2p server, which is the entire point of the transport. #3520 implements v2 (libp2p/specs#715): the browser's own ICE password rides in the server ufrag, so the server recovers it from the STUNUSERNAMEinstead of predicting it.The listener accepts v1 and v2 on the same port and picks the flow from the ICE username fragment prefix, so servers need no config change. Go dialers still default to v1;
webrtc.WithDialerVersion(2)opts into v2. Browsers will get v2 from js-libp2p (libp2p/js-libp2p#3480).Stable
/certhashfor WebRTC Directwebrtc-direct used to mint a random DTLS cert at every process start, so the
/certhashin the listen multiaddr changed on every restart and invalidated everything that had pinned it: peerstore TTLs, DHT records, external address books. #3512 derives the cert from the host key with HKDF, so the certhash depends only on that key. A node upgrading to v0.49 changes its certhash once, then keeps it.The same PR fixes a serial number that could come out negative, which RFC 5280 forbids and
x509.CreateCertificaterejects. webtransport shared that bug and is fixed too.WebSocket listeners can share their port with an HTTP handler
websocket.WithHTTPHandler(http.Handler)makes a/wsor/tls/wslistener answer non-upgrade requests with your handler instead of a 404, on the same TCP port.websocket.WithHTTPServerConfig(func(*http.Server))tunes that server's timeouts and HTTP/2 settings. Both are opt-in and default behavior is unchanged. #3509Two things this buys you: a libp2p WebSocket port that also serves an ordinary HTTPS site, which is much harder to censor on 443, and an HTTP API served under the AutoTLS cert. Kubo exposes its trustless gateway that way in ipfs/kubo#11333.
WebTransport ready for the next browser draft
WebTransport over HTTP/3 is still an IETF draft, and draft-15 renamed both values that identify a session on the wire: the Extended CONNECT
:protocoltoken (webtransportbecomeswebtransport-h3) and the SETTINGS codepoint (0x2b603742becomes0x2c7cf000). Every shipping browser still sends the old pair, but Firefox is already implementing the new one (mozilla/neqo#3646), and a server that only speaks the old one stops answering the day that lands.webtransport-go v0.11.1 advertises and accepts both spellings, so a v0.49 server keeps working with today's browsers and with tomorrow's, without a config change. It also sends the
SETTINGS_WT_MAX_SESSIONSvalue Safari 26.4+ expects.One regression, and it is go-to-go only: a v0.49 node can no longer dial a peer on older go-libp2p over WebTransport. Those peers can still dial it. In practice this is invisible, because go-to-go connections use
/quic-v1, which nodes announce on the same UDP port and the dial ranker ignores webtransport address if a quic address is available for the same (IP, Port). WebTransport is there for browsers, and browsers are unaffected.AutoNAT v2 confirmed addresses
This release fixes a bug with the address reachability logic where /webrtc-direct addresses were dropped on a shared UDP port. A node that AutoNAT had just confirmed reachable over webrtc-direct previously reported that it was not. This release fixes this correctly advertising /webrtc-direct address if it's reachable.
Fewer stale and junk addresses
libp2p.NonPublicAddrPublishing(false)stops a host from advertising addresses outside globally routable ranges (RFC 1918, CGNAT, loopback, link-local, ULA, IPv6 reserved space) through identify and DHT. Default staystrue. feat: NonPublicAddrPublishing option #3489pstoremem.WithMaxAddressesPerPeer(n). feat(pstore): cap unconnected addrs per peer #3486/and rejected the address. fix(peer): drop empty addrs in peer records #3494Changelog
< changelog generated by scripts/mkreleaselog >
✅ Release Checklist
go get -u ./...to see if there are any out-of-date deps that look important. If there are, bubble them. Try to avoid directly updating indirect deps in go-libp2p'sgo.modwhen possible.