fix(p2p): repair peer discovery — tracker domain, KV binding, loopback bind, relay - #22
Merged
Conversation
…oopback bind, no relay Discovery has never worked. Four independent blockers, each sufficient on its own to keep the swarm empty: 1. The default tracker URL pointed at usefolklore.com — a domain we do not own, which 301s to an unrelated site. Every install announced into someone else's website and got HTML back. Now https://usefolklore.sh, the deployed Pages domain. 2. TRACKER_KV was never bound, so /tracker/* answered 500 "tracker KV not bound". The namespace is now declared in wrangler.toml so the binding travels with the repo instead of living only in dashboard state. 3. peer.listen_host defaulted to 127.0.0.1. The daemon is the only consumer of that key (CLI and MCP nodes hardcode loopback at their own createNode call), so the default made the daemon announce /ip4/127.0.0.1/… — an address every other peer dials against itself — and silently disabled UPnP. Now 0.0.0.0. 4. No relay existed, leaving CGNAT and symmetric-NAT peers unreachable even with 1-3 fixed. The relay now listens for WebSockets behind a TLS edge on 443 rather than raw TCP on 4103: 443 is the one port hostile networks allow, and it removes the dedicated-IPv4 requirement of a raw-TCP listener. @libp2p/websockets is wired into transports on every node (a leaf must be able to DIAL wss even though it never listens), with peer.ws_port and peer.announce for the edge-fronted case. Hardening so the failure cannot recur silently: peers filter loopback, link-local and LAN-private addresses out of announces (circuit addresses always survive), the tracker rejects non-routable pointers server-side, and `folklore doctor` probes the tracker — it reports the exact 500 above rather than leaving discovery to fail invisibly inside a best-effort loop. uint8arraylist is pinned to ^3 via overrides: @libp2p/websockets pulls v3 while several older libp2p packages still declare ^2, and two copies of the type break every it-length-prefixed call site at compile time.
…emory stack `daemon _run` boots ingestion, embedders and consolidation to relay a connection, which OOM-kills a 512MB relay box and would file graph data on a machine documented to hold none. The relay command starts only the libp2p node with circuitRelayServer, reading the same peer.* config (ws_port, announce, listen_host), and runs until SIGTERM.
folklore-relay.fly.dev is live (circuit-relay-v2 over wss on 443). Verified from outside Fly: dial 1.3s, reservation granted, leaf advertises /dns4/folklore-relay.fly.dev/tcp/443/wss/p2p/<relay>/p2p-circuit/p2p/<leaf>. Shipped as the default so a fresh install is dialable with zero configuration — a CGNAT peer with no relay is discoverable via the tracker but unreachable, which is the same empty swarm by another route. FOLKLORE_RELAYS overrides; setting it empty is an explicit opt-out.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Discovery has never worked. Not "worked poorly" — it was structurally impossible for two peers to find each other over the WAN. Four independent blockers, each sufficient alone:
https://usefolklore.com, which we do not own; it 301s tolovefolklore.com. Every install announced into an unrelated website and parsed HTML as a peer list.TRACKER_KVnever bound./tracker/*answered500 {"error":"tracker KV not bound"}. The binding lived only in dashboard state that was never configured.peer.listen_hostdefaulted to127.0.0.1. The daemon is the only consumer of that key (CLI and MCP nodes hardcode loopback at their owncreateNodecall), so the default made it announce/ip4/127.0.0.1/…— an address every other peer dials against itself — and silently disabled UPnP.What changed
Tracker. Default URL →
https://usefolklore.sh. KV namespace created and declared inwrangler.toml, so the binding travels with the repo rather than living in dashboard state. Merging this is what activates it — until the Pages build runs, the tracker still 500s.Bind.
peer.listen_hostdefaults to0.0.0.0. Exposed surface is the libp2p TCP listener only — noise-encrypted, peer-id-authenticated, rate-limited. No plaintext endpoint is opened.Relay (WebSockets on 443).
@libp2p/websocketsis wired into transports on every node — a leaf never listens on ws but must be able to dial the relay'swssaddress. Newpeer.ws_portandpeer.announceconfig keys cover the edge-fronted case: the container listens plain ws on 8080, TLS terminates at the edge, andannouncepublishes the…/tcp/443/wssform. 443 is chosen over raw TCP because it is the one port hostile networks allow, and because an anycast/shared IPv4 forwards only 80/443 — raw TCP would need a dedicated IP.folklore relayis a new command that runs only the libp2p relay server.daemon _runwould relay too, but it boots ingestion, embedders and consolidation to do it — that OOM-killed the 512MB relay box, and it would file graph data on a machine documented to hold none.The relay is deployed and verified from outside Fly: wss dial 1.3s, reservation granted, leaf advertises
/dns4/folklore-relay.fly.dev/tcp/443/wss/p2p/<relay>/p2p-circuit/p2p/<leaf>. Shipped as the defaultFOLKLORE_RELAYS.So it cannot fail silently again
announceableAddrs); circuit addresses always survive. A node with nothing publishable falls back to a read-only fetch — it still discovers, it just stops poisoning the directory.folklore doctorprobes the tracker. On this branch before deploy it prints the exact bug this PR fixes:Dependency note
uint8arraylistis pinned to^3viaoverrides.@libp2p/websocketspulls v3 while several older libp2p packages still declare^2; two copies of the type break everyit-length-prefixedcall site at compile time.deploy/relay/Dockerfilenow fetches by ref-or-SHA instead ofclone --branch: the old form is a byte-identical RUN line on every build, so the builder reused a stale checkout and deployed old code (it shipped a binary without therelaycommand during this work).Tests
announceableAddrsfiltering, including the escape hatch for a self-hosted LAN trackersanitizeAddrsrejects loopback /0.0.0.0/ link-local / IPv6 link-localtrackerTickwith only non-routable addrs falls back to fetch and stays out of the directory0.0.0.0bind, tracker domain,ws_port/announceround-trip, default relay is wss-on-443