From 52e5ed990cd4f8c03a345d86000187cd95610b9c Mon Sep 17 00:00:00 2001 From: Nico Krause Date: Fri, 12 Jun 2026 20:59:59 +0200 Subject: [PATCH 1/2] fix(js-peer): hardcode bootstrap relay multiaddrs --- js-peer/src/context/ctx.tsx | 2 -- js-peer/src/lib/constants.ts | 13 +++++---- js-peer/src/lib/libp2p.ts | 51 +++++++----------------------------- 3 files changed, 17 insertions(+), 49 deletions(-) diff --git a/js-peer/src/context/ctx.tsx b/js-peer/src/context/ctx.tsx index 56743506..51e820c1 100644 --- a/js-peer/src/context/ctx.tsx +++ b/js-peer/src/context/ctx.tsx @@ -4,14 +4,12 @@ import { ChatProvider } from './chat-ctx' import type { Libp2p, PubSub } from '@libp2p/interface' import type { Identify } from '@libp2p/identify' import type { DirectMessage } from '@/lib/direct-message' -import type { DelegatedRoutingV1HttpApiClient } from '@helia/delegated-routing-v1-http-api-client' import { Booting } from '@/components/booting' export type Libp2pType = Libp2p<{ pubsub: PubSub identify: Identify directMessage: DirectMessage - delegatedRouting: DelegatedRoutingV1HttpApiClient }> export const libp2pContext = createContext<{ libp2p: Libp2pType }>({ diff --git a/js-peer/src/lib/constants.ts b/js-peer/src/lib/constants.ts index dbbcdd43..d7198a0b 100644 --- a/js-peer/src/lib/constants.ts +++ b/js-peer/src/lib/constants.ts @@ -8,8 +8,11 @@ export const CIRCUIT_RELAY_CODE = 290 export const MIME_TEXT_PLAIN = 'text/plain' -// 👇 App specific dedicated bootstrap PeerIDs -// Their multiaddrs are ephemeral so peer routing is used to resolve multiaddr -export const WEBTRANSPORT_BOOTSTRAP_PEER_ID = '12D3KooWFhXabKDwALpzqMbto94sB7rvmZ6M28hs9Y9xSopDKwQr' - -export const BOOTSTRAP_PEER_IDS = [WEBTRANSPORT_BOOTSTRAP_PEER_ID] +export const BOOTSTRAP_MULTIADDRS = [ + '/dns4/burden-sphere-chuckle-fever.2n6.me/tcp/443/tls/ws/p2p/16Uiu2HAm2BG1xBWz8YyqFiERC7vJzUX2mpyF1vNyVMJajQcebVeB', + '/dns6/burden-sphere-chuckle-fever.2n6.me/tcp/443/tls/ws/p2p/16Uiu2HAm2BG1xBWz8YyqFiERC7vJzUX2mpyF1vNyVMJajQcebVeB', + '/ip4/37.114.50.44/udp/24204/quic-v1/webtransport/p2p/16Uiu2HAm2BG1xBWz8YyqFiERC7vJzUX2mpyF1vNyVMJajQcebVeB', + '/ip6/2a0e:97c0:3e3:54b:3:1e9a:2ca2:2ab1/udp/24204/quic-v1/webtransport/p2p/16Uiu2HAm2BG1xBWz8YyqFiERC7vJzUX2mpyF1vNyVMJajQcebVeB', + '/ip4/37.114.50.44/udp/24204/webrtc-direct/p2p/16Uiu2HAm2BG1xBWz8YyqFiERC7vJzUX2mpyF1vNyVMJajQcebVeB', + '/ip6/2a0e:97c0:3e3:54b:3:1e9a:2ca2:2ab1/udp/24204/webrtc-direct/p2p/16Uiu2HAm2BG1xBWz8YyqFiERC7vJzUX2mpyF1vNyVMJajQcebVeB', +] as const diff --git a/js-peer/src/lib/libp2p.ts b/js-peer/src/lib/libp2p.ts index 3c2b40af..122dab5d 100644 --- a/js-peer/src/lib/libp2p.ts +++ b/js-peer/src/lib/libp2p.ts @@ -1,15 +1,11 @@ -import { - createDelegatedRoutingV1HttpApiClient, - DelegatedRoutingV1HttpApiClient, -} from '@helia/delegated-routing-v1-http-api-client' import { createLibp2p } from 'libp2p' +import { bootstrap } from '@libp2p/bootstrap' import { identify } from '@libp2p/identify' -import { peerIdFromString } from '@libp2p/peer-id' import { noise } from '@chainsafe/libp2p-noise' import { yamux } from '@chainsafe/libp2p-yamux' import { Multiaddr } from '@multiformats/multiaddr' import { sha256 } from 'multiformats/hashes/sha2' -import type { Connection, Message, SignedMessage, PeerId, Libp2p } from '@libp2p/interface' +import type { Connection, Message, SignedMessage, Libp2p } from '@libp2p/interface' import { gossipsub } from '@chainsafe/libp2p-gossipsub' import { webSockets } from '@libp2p/websockets' import { webTransport } from '@libp2p/webtransport' @@ -17,8 +13,7 @@ import { webRTC, webRTCDirect } from '@libp2p/webrtc' import { circuitRelayTransport } from '@libp2p/circuit-relay-v2' import { pubsubPeerDiscovery } from '@libp2p/pubsub-peer-discovery' import { ping } from '@libp2p/ping' -import { BOOTSTRAP_PEER_IDS, CHAT_FILE_TOPIC, CHAT_TOPIC, PUBSUB_PEER_DISCOVERY } from './constants' -import first from 'it-first' +import { BOOTSTRAP_MULTIADDRS, CHAT_FILE_TOPIC, CHAT_TOPIC, PUBSUB_PEER_DISCOVERY } from './constants' import { forComponent, enable } from './logger' import { directMessage } from './direct-message' import type { Libp2pType } from '@/context/ctx' @@ -29,10 +24,7 @@ export async function startLibp2p(): Promise { // enable verbose logging in browser console to view debug logs enable('ui*,libp2p*,-libp2p:connection-manager*,-*:trace') - const delegatedClient = createDelegatedRoutingV1HttpApiClient('https://delegated-ipfs.dev') - - const relayListenAddrs = await getRelayListenAddrs(delegatedClient) - log('starting libp2p with relayListenAddrs: %o', relayListenAddrs) + log('starting libp2p with bootstrap multiaddrs: %o', BOOTSTRAP_MULTIADDRS) let libp2p: Libp2pType @@ -41,7 +33,6 @@ export async function startLibp2p(): Promise { listen: [ // 👇 Listen for webRTC connection '/webrtc', - ...relayListenAddrs, ], }, transports: [ @@ -59,6 +50,11 @@ export async function startLibp2p(): Promise { denyDialMultiaddr: async () => false, }, peerDiscovery: [ + bootstrap({ + list: [...BOOTSTRAP_MULTIADDRS], + timeout: 10_000, + tagName: 'uc-bootstrap', + }), pubsubPeerDiscovery({ interval: 10_000, topics: [PUBSUB_PEER_DISCOVERY], @@ -71,9 +67,6 @@ export async function startLibp2p(): Promise { msgIdFn: msgIdFnStrictNoSign, ignoreDuplicatePublishError: true, }), - // Delegated routing helps us discover the ephemeral multiaddrs of the dedicated go and rust bootstrap peers - // This relies on the public delegated routing endpoint https://docs.ipfs.tech/concepts/public-utilities/#delegated-routing - delegatedRouting: () => delegatedClient, identify: identify(), // Custom protocol for direct messaging directMessage: directMessage(), @@ -148,32 +141,6 @@ export const connectToMultiaddr = (libp2p: Libp2p) => async (multiaddr: Multiadd } } -// Function which resolves PeerIDs of rust/go bootstrap nodes to multiaddrs dialable from the browser -// Returns both the dialable multiaddrs in addition to the relay -async function getRelayListenAddrs(client: DelegatedRoutingV1HttpApiClient): Promise { - const peers = await Promise.all(BOOTSTRAP_PEER_IDS.map((peerId) => first(client.getPeers(peerIdFromString(peerId))))) - - const relayListenAddrs = [] - for (const p of peers) { - if (p && p.Addrs.length > 0) { - for (const maddr of p.Addrs) { - const protos = maddr.protoNames() - // Note: narrowing to Secure WebSockets and IP4 addresses to avoid potential issues with ipv6 - // https://github.com/libp2p/js-libp2p/issues/2977 - if (protos.includes('tls') && protos.includes('ws')) { - if (maddr.nodeAddress().address === '127.0.0.1') continue // skip loopback - relayListenAddrs.push(getRelayListenAddr(maddr, p.ID)) - } - } - } - } - return relayListenAddrs -} - -// Constructs a multiaddr string representing the circuit relay v2 listen address for a relayed connection to the given peer. -const getRelayListenAddr = (maddr: Multiaddr, peer: PeerId): string => - `${maddr.toString()}/p2p/${peer.toString()}/p2p-circuit` - export const getFormattedConnections = (connections: Connection[]) => connections.map((conn) => ({ peerId: conn.remotePeer, From 9851d10e2779b47b4064a9f0188521e8d5f0beee Mon Sep 17 00:00:00 2001 From: Nico Krause Date: Fri, 12 Jun 2026 21:14:55 +0200 Subject: [PATCH 2/2] refactor(js-peer): remove redundant discovery dial helper --- js-peer/src/lib/libp2p.ts | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/js-peer/src/lib/libp2p.ts b/js-peer/src/lib/libp2p.ts index 122dab5d..ecbf9e3b 100644 --- a/js-peer/src/lib/libp2p.ts +++ b/js-peer/src/lib/libp2p.ts @@ -95,7 +95,13 @@ export async function startLibp2p(): Promise { return } - dialWebRTCMaddrs(libp2p, multiaddrs) + // libp2p.dial() already deduplicates existing and in-flight connection attempts + // for the same peer and can try multiple candidate multiaddrs until one succeeds. + log(`dialling discovered multiaddrs: %o`, multiaddrs) + + void libp2p.dial(multiaddrs).catch((error) => { + log.error(`failed to dial discovered multiaddrs for peer %s: %o`, id, error) + }) }) return libp2p @@ -112,23 +118,6 @@ export async function msgIdFnStrictNoSign(msg: Message): Promise { return await sha256.encode(encodedSeqNum) } -// Function which dials one maddr at a time to avoid establishing multiple connections to the same peer -async function dialWebRTCMaddrs(libp2p: Libp2p, multiaddrs: Multiaddr[]): Promise { - // Filter webrtc (browser-to-browser) multiaddrs - const webRTCMadrs = multiaddrs.filter((maddr) => maddr.protoNames().includes('webrtc')) - log(`dialling WebRTC multiaddrs: %o`, webRTCMadrs) - - for (const addr of webRTCMadrs) { - try { - log(`attempting to dial webrtc multiaddr: %o`, addr) - await libp2p.dial(addr) - return // if we succeed dialing the peer, no need to try another address - } catch (error) { - log.error(`failed to dial webrtc multiaddr: %o`, addr) - } - } -} - export const connectToMultiaddr = (libp2p: Libp2p) => async (multiaddr: Multiaddr) => { log(`dialling: %a`, multiaddr) try {