@relay-federation/bridge 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli.js CHANGED
@@ -320,9 +320,10 @@ async function cmdStart () {
320
320
  })
321
321
 
322
322
  // Wire peer health tracking
323
- peerManager.on('peer:connect', ({ pubkeyHex }) => {
323
+ peerManager.on('peer:connect', ({ pubkeyHex, endpoint }) => {
324
324
  peerHealth.recordSeen(pubkeyHex)
325
325
  scorer.setStakeAge(pubkeyHex, 7)
326
+ if (endpoint) gossipManager.addSeed({ pubkeyHex, endpoint, meshId: config.meshId })
326
327
  })
327
328
 
328
329
  peerManager.on('peer:disconnect', ({ pubkeyHex }) => {
@@ -430,6 +431,8 @@ async function cmdStart () {
430
431
  const { extractOpReturnData, decodePayload, PROTOCOL_PREFIX } = await import('../registry/lib/cbor.js')
431
432
  const { Transaction: BsvTx } = await import('@bsv/sdk')
432
433
 
434
+ // Registry bootstrapped via discoverNewPeers() after server start (no WoC dependency)
435
+
433
436
  watcher.on('utxo:received', async ({ txid, hash160 }) => {
434
437
  if (hash160 !== beaconHash160) return
435
438
 
@@ -641,14 +644,17 @@ async function cmdStart () {
641
644
  let gossipStarted = false
642
645
 
643
646
  // Start gossip after first peer connection completes.
644
- // With crypto handshake, peer:connect fires AFTER handshake verification,
645
- // so gossip won't race the handshake anymore.
647
+ // Delay 5s so all seed handshakes finish before gossip broadcasts
648
+ // (immediate broadcast would send announce/getpeers through connections
649
+ // whose inbound side is still waiting for verify — breaking the handshake).
646
650
  peerManager.on('peer:connect', () => {
647
651
  if (!gossipStarted) {
648
652
  gossipStarted = true
649
- gossipManager.start()
650
- gossipManager.requestPeersFromAll()
651
- console.log('Gossip started')
653
+ setTimeout(() => {
654
+ gossipManager.start()
655
+ gossipManager.requestPeersFromAll()
656
+ console.log('Gossip started')
657
+ }, 5000)
652
658
 
653
659
  // Periodic peer refresh — re-request peer lists every 10 minutes
654
660
  // Catches registrations missed during downtime or initial gossip
@@ -703,6 +709,7 @@ async function cmdStart () {
703
709
  // Connect to seed peers (accept both string URLs and {pubkeyHex, endpoint} objects)
704
710
  console.log(`Connecting to ${seedPeers.length} seed peer(s)...`)
705
711
  for (let i = 0; i < seedPeers.length; i++) {
712
+ if (i > 0) await new Promise(r => setTimeout(r, 2000)) // stagger to avoid handshake races
706
713
  const seed = seedPeers[i]
707
714
  const endpoint = typeof seed === 'string' ? seed : seed.endpoint
708
715
  const pubkey = typeof seed === 'string' ? `seed_${i}` : seed.pubkeyHex
@@ -770,6 +777,47 @@ async function cmdStart () {
770
777
  statusServer.startAppMonitoring()
771
778
  console.log(` Status: http://127.0.0.1:${statusPort}/status`)
772
779
 
780
+ // ── Peer discovery — bootstrap registry from seed peers, then periodic refresh ──
781
+ const knownEndpoints = new Set()
782
+ for (const sp of (config.seedPeers || [])) knownEndpoints.add(sp.endpoint)
783
+ knownEndpoints.add(config.endpoint)
784
+
785
+ async function discoverNewPeers () {
786
+ const peersToQuery = [...(config.seedPeers || [])]
787
+ for (const [, conn] of peerManager.peers) {
788
+ if (conn.endpoint && conn.readyState === 1) peersToQuery.push({ endpoint: conn.endpoint })
789
+ }
790
+ for (const peer of peersToQuery) {
791
+ try {
792
+ const ep = peer.endpoint || ''
793
+ const u = new URL(ep)
794
+ const statusUrl = 'http://' + u.hostname + ':' + (parseInt(u.port, 10) + 1000) + '/discover'
795
+ const res = await fetch(statusUrl, { signal: AbortSignal.timeout(5000) })
796
+ if (!res.ok) continue
797
+ const data = await res.json()
798
+ if (!data.bridges) continue
799
+ for (const b of data.bridges) {
800
+ if (!b.endpoint) continue
801
+ if (b.pubkeyHex) registeredPubkeys.add(b.pubkeyHex)
802
+ seedEndpoints.add(b.endpoint)
803
+ if (knownEndpoints.has(b.endpoint)) continue
804
+ knownEndpoints.add(b.endpoint)
805
+ const conn = peerManager.connectToPeer({ endpoint: b.endpoint, pubkeyHex: b.pubkeyHex })
806
+ if (conn) {
807
+ conn.on('open', () => performOutboundHandshake(conn))
808
+ const msg = `Discovered new peer: ${b.name || b.pubkeyHex?.slice(0, 16) || b.endpoint}`
809
+ console.log(msg)
810
+ statusServer.addLog(msg)
811
+ }
812
+ }
813
+ } catch {}
814
+ }
815
+ }
816
+ await discoverNewPeers()
817
+ console.log(` Registry: ${registeredPubkeys.size} trusted pubkeys after peer discovery`)
818
+ setTimeout(discoverNewPeers, 5000)
819
+ setInterval(discoverNewPeers, 300000)
820
+
773
821
  // ── 9. Log events (dual: console + status server ring buffer) ──
774
822
  peerManager.on('peer:connect', ({ pubkeyHex }) => {
775
823
  const msg = `Peer connected: ${pubkeyHex.slice(0, 16)}...`
@@ -169,7 +169,7 @@ export class PeerManager extends EventEmitter {
169
169
  // If cryptographic handshake is available, use it
170
170
  if (opts.handshake && msg.nonce && Array.isArray(msg.versions)) {
171
171
  const isSeed = opts.seedEndpoints && opts.seedEndpoints.has(msg.endpoint)
172
- const result = opts.handshake.handleHello(msg, isSeed ? null : (opts.registeredPubkeys || null))
172
+ const result = opts.handshake.handleHello(msg, null) // cryptographic handshake is the security layer — no whitelist gate
173
173
  if (result.error) {
174
174
  ws.send(JSON.stringify({ type: 'error', error: result.error }))
175
175
  ws.close()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relay-federation/bridge",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Bridge server — WebSocket peering, header sync, tx relay, CLI",
5
5
  "type": "module",
6
6
  "bin": {