feat: React Native transport over js-libp2p + authored-data-sync module#83
feat: React Native transport over js-libp2p + authored-data-sync module#83veeso wants to merge 3 commits into
Conversation
Wires `@peerkit/transport-libp2p-react-native` to a libp2p stack configured for mobile: WebSockets (outbound) + WebRTC + circuit-relay-v2 client + noise + yamux + identify. Mobile peers are relay-mediated by design (CGNAT, no inbound listen). Ships: - `createNode` factory wiring the mobile libp2p stack. - `./polyfills` side-effect entry: patches RNG, installs `react-native-quick-crypto`, registers `react-native-webrtc` globals, and assigns `globalThis.Buffer` / `globalThis.process`. - `./quick-crypto-noise` subexport with a JSI-backed `ICryptoInterface` over `react-native-quick-crypto`. Used by `createNode` by default for SHA-256, HKDF, and ChaCha20-Poly1305; X25519 stays on the synchronous pure-JS Noise default. - Ambient module declarations for the React Native-only packages so the workspace `tsc` host build resolves them without installing the native modules. - README documenting Metro / Babel config and runtime scope.
…ills Add the React Native transport's first test suite (22 tests). These cover the RN-specific surface that runs in Node CI; the end-to-end WebRTC path still needs a device/emulator and is tracked separately. - quick-crypto-noise: assert the JSI-backed ICryptoInterface (SHA-256, HKDF, ChaCha20-Poly1305) matches the pure-JS Noise reference byte-for-byte, plus interop, Uint8ArrayList inputs, and AEAD guards. `react-native-quick-crypto` is mocked with Node's API-compatible `node:crypto`. - factories: assert createNode wiring — deferred start (handlers before listen), the WebSockets + WebRTC + circuit-relay transport set, listen addresses, ICE server mapping, and conditional bootstrap-relay dial. - polyfills: assert install order (RNG -> quick-crypto -> WebRTC) and the global Buffer/process assignment. Switch the package test script from a no-op echo to `vitest --run`.
|
Warning Review limit reached
More reviews will be available in 40 minutes and 17 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (20)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
571e01c to
8e93bf2
Compare
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
…ake completes React Native's Hermes runtime lacks browser globals libp2p relies on and ships a WebSocket whose `bufferedAmount` is never assigned. The missing `bufferedAmount` deadlocked the multistream-select write against the relay (libp2p backpressure treats `undefined < max` as "cannot send" and never sees `bufferedAmount === 0` to drain), and the hung write was then aborted with React Native's `undefined` timeout reason, surfacing as an opaque `EncryptionFailedError: undefined`. Add three polyfills, installed from the package entry before any libp2p module evaluates: - event-target: install Event/EventTarget/CustomEvent outright (not `??=`). RN's global Event has a read-only `type` getter and non-configurable phase constants, which break libp2p's field-declaring Event subclasses on Hermes. - text-codec: UTF-8 TextEncoder/TextDecoder. - websocket-buffered-amount: report `0` (RN sends straight to native, no JS-visible buffer) so @libp2p/websockets write backpressure resolves. Also expose a connectionGater passthrough on createNode for cleartext/LAN dev relays. Covered by unit tests for each polyfill.
8e93bf2 to
582aa6c
Compare
|
✔️ 0696ba0...582aa6c - Conventional commits check succeeded. |
| * Recover the first message of inbound in-band WebRTC data channels on React | ||
| * Native. A native thread race lets the first message reach JS before the | ||
| * `RTCDataChannel` object (and its listener) exists, so `react-native-webrtc` | ||
| * drops it. For `@libp2p/webrtc` that lost message is the per-stream | ||
| * multistream-select request, so the stream stalls without it. | ||
| * | ||
| * We subscribe to the native message stream directly and buffer per `reactTag` | ||
| * until a consumer attaches via the `onmessage` setter, then replay | ||
| * synchronously and step aside so live messages flow through the normal path. | ||
| */ |
There was a problem hiding this comment.
This is unfortunate. I imagine it took you some time to figure that out.
Is there anything that we can do in addition to work around it? Can we report this to React Native? Or libp2p?
| Mobile peers cannot accept inbound direct connections (CGNAT, no listen socket | ||
| survives backgrounding on iOS or Android). Reachability is therefore always | ||
| mediated by a relay; direct peer-to-peer is achieved with WebRTC over a | ||
| circuit-relay-v2 reservation, using ICE/STUN for hole-punching. |
There was a problem hiding this comment.
This is nothing special for mobile devices. A NAT is the default nowadays for peer-to-peer. Also it's by design that all connections are relay-mediated. I would omit this paragraph.
| ## Polyfills | ||
|
|
||
| libp2p depends on Node built-ins (`crypto`, `stream`, `buffer`, `events`, | ||
| `process`) and on the Web Crypto / WebRTC globals. Hermes provides none of |
There was a problem hiding this comment.
Who's this Hermes guy and why does he keep peeking out of this implementation? It would be good to mention what Hermes is.
|
|
||
| ## Metro config | ||
|
|
||
| Metro must rewrite Node-style bare imports to their React Native shims. Add |
There was a problem hiding this comment.
I also don't know what Metro is. That and Hermes, are they native packages for iOS and Android?
| - No hole-punching via DCUtR (see | ||
| <https://github.com/libp2p/js-libp2p/discussions/2388>). All direct | ||
| peer-to-peer connections go over WebRTC; if WebRTC ICE fails, traffic | ||
| remains relayed. |
There was a problem hiding this comment.
Also by design, not a limitation IMO
| - No background operation. iOS suspends the JS runtime when the app is | ||
| backgrounded; Android Doze mode throttles. Foreground-only without | ||
| additional OS-specific work. |
There was a problem hiding this comment.
It would be good to figure out more about this limitation with time.
| - No background operation. iOS suspends the JS runtime when the app is | ||
| backgrounded; Android Doze mode throttles. Foreground-only without | ||
| additional OS-specific work. | ||
| - No local discovery (no mDNS). |
There was a problem hiding this comment.
Same for this for the future, Is that a permanent limitation, or just for the current implementation?
| - Mobile peer cannot listen for inbound direct connections. The transport | ||
| advertises `/p2p-circuit` and `/webrtc` listen addresses only. |
There was a problem hiding this comment.
Omit this, applies to all devices
What changed
This adds support for running Peerkit on mobile apps built with React Native.
Peerkit can now run inside a React Native app (iOS and Android). Because phones cannot accept direct incoming connections, mobile devices reach the network through a relay node that passes traffic on their behalf. Some pieces of the underlying networking library expect features that React Native does not provide out of the box, so small compatibility shims are included to fill those gaps.
loosely connected and easier to maintain.
Notes