feat: add QUIC transport backend as default fallback#38
feat: add QUIC transport backend as default fallback#38Jing-yilin wants to merge 2 commits intomainfrom
Conversation
- Define Transport interface in src/transport.ts with TransportManager - Implement YggdrasilTransport wrapping existing daemon management - Implement QUICTransport with UDP socket and STUN NAT traversal - Add automatic transport selection: Yggdrasil preferred, QUIC fallback - Update PeerRecord/PeerAnnouncement for multi-transport endpoints - Update peer-server, peer-discovery, peer-db for transport awareness - Update index.ts to use TransportManager lifecycle - Add 32 new tests for transport abstraction, QUIC, and Yggdrasil Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Jing-yilin
left a comment
There was a problem hiding this comment.
Codex Review
[High] QUIC fallback is not wired into message delivery/discovery paths
TransportManager is started, but outbound paths still hardcode the Yggdrasil HTTP client. No path uses TransportManager.resolveTransport() or QUICTransport.send(). In practice, nodes without Yggdrasil cannot use QUIC endpoints for chat/ping/discovery even when status reports QUIC active.
Refs: src/index.ts:329, src/index.ts:419, src/index.ts:579, src/peer-client.ts:32, src/transport.ts:116
[High] STUN advertises the wrong port (ephemeral temp socket)
STUN discovery is executed on a temporary udp4 socket bound to an ephemeral port, and that mapping is published as this node’s QUIC endpoint. That socket is then closed, while the real listener is a different socket/port (this._socket). This can publish an unreachable endpoint.
Refs: src/transport-quic.ts:172, src/transport-quic.ts:186, src/transport-quic.ts:195, src/transport-quic.ts:197, src/transport-quic.ts:199
[Medium] Endpoint metadata is dropped during bootstrap/gossip ingestion
PeerEndpoint support was added, but several discovery ingestion paths omit endpoints when calling upsertDiscoveredPeer, so transport routing info is lost after bootstrap/fanout/gossip.
Refs: src/peer-discovery.ts:125, src/peer-discovery.ts:186, src/peer-discovery.ts:207, src/peer-discovery.ts:266
[Low] quic_port is read at runtime but missing from plugin config schema
Runtime config reads quic_port, but openclaw.plugin.json has additionalProperties: false and no quic_port field/hint, so this new option cannot be configured through schema-validated config.
Refs: src/types.ts:44, src/index.ts:179, openclaw.plugin.json:14, openclaw.plugin.json:15
Validation run on this PR branch: npm run build and node --test test/*.test.mjs both pass.
- Fix STUN advertising wrong port by binding to actual listener port - Wire QUIC transport into all sendP2PMessage callsites (index, channel) - Forward endpoints in all peer-discovery upsert paths (bootstrap, fanout, gossip) - Add quic_port to openclaw.plugin.json config schema and uiHints Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Jing-yilin
left a comment
There was a problem hiding this comment.
Review findings
I found 3 issues that block the advertised QUIC fallback behavior for nodes running without Yggdrasil.
[P1] Wire inbound QUIC packets into the receive path
src/peer-client.ts:113 currently treats a QUIC send as successful immediately after transport.send(), but nothing in this patch registers QUICTransport.onMessage() or feeds received UDP payloads through the existing signature/TOFU pipeline in peer-server.ts. In the two-peers-without-Yggdrasil case, both sides report successful sends while every inbound datagram is silently discarded.
[P2] Return endpoints in /peer/announce self metadata
src/peer-discovery.ts:125 now reads body.self.endpoints, but src/peer-server.ts still only serializes yggAddr/publicKey/alias/version for self. When a node bootstraps directly against another peer, it stores that peer without its QUIC endpoint and later falls back to Yggdrasil/HTTP even though endpoint exchange was added elsewhere in this PR.
[P1] Use transport endpoints for discovery when QUIC is active
src/peer-discovery.ts:94 still takes a targetYggAddr and builds a Yggdrasil-only HTTP URL. index.ts still runs bootstrap discovery and the gossip loop even when QUIC is the active transport, so hosts without Yggdrasil can never reach the hard-coded 200::/7 bootstrap set and therefore never discover peers automatically.
Summary
Closes #37
Add QUIC as a second transport backend that works zero-install, zero-config as an automatic fallback when Yggdrasil is not available. This is the single biggest adoption blocker fix — it removes the Yggdrasil daemon requirement for 90%+ of potential users.
Changes
New files
src/transport.ts— Transport interface + TransportManager (automatic selection)src/transport-yggdrasil.ts— YggdrasilTransport wrapping existing daemon managementsrc/transport-quic.ts— QUICTransport with UDP socket, STUN NAT traversal, native QUIC detectionModified files
src/types.ts— AddedTransportType,PeerEndpoint, multi-transport fields onPeerAnnouncement/DiscoveredPeerRecord,quic_portconfigsrc/index.ts— Uses TransportManager lifecycle, shows transport info in status/toolssrc/peer-db.ts— Supportsendpointsfield in peer recordssrc/peer-discovery.ts— Passes transport/endpoints through announcementssrc/peer-server.ts— Stores endpoint info from announcementsTests (32 new)
test/transport.test.mjs— TransportManager selection, lifecycle, endpoint resolutiontest/transport-quic.test.mjs— STUN parsing, host:port parsing, QUIC start/stoptest/transport-yggdrasil.test.mjs— Yggdrasil transport interface compliancetest/transport-types.test.mjs— Type structure validationTransport Selection Logic
Test results
All 76 tests pass (44 existing + 32 new).