Skip to content

feat: React Native transport over js-libp2p + authored-data-sync module#83

Open
veeso wants to merge 3 commits into
mainfrom
feat/20-implement-react-native-transport-via-js-libp2p-with-polyfills
Open

feat: React Native transport over js-libp2p + authored-data-sync module#83
veeso wants to merge 3 commits into
mainfrom
feat/20-implement-react-native-transport-via-js-libp2p-with-polyfills

Conversation

@veeso

@veeso veeso commented Jun 8, 2026

Copy link
Copy Markdown
Member

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

  • Mobile devices always connect through a relay. They never accept direct incoming connections.
  • A React Native app must load the compatibility shims at startup, before anything else. The package README explains the required setup.
  • Unit tests cover the new code.

veeso added 2 commits June 8, 2026 15:59
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`.
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@veeso, we couldn't start this review because you've reached your PR review rate limit.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 690174b1-3037-48e4-911a-310ebe3e9efb

📥 Commits

Reviewing files that changed from the base of the PR and between 393cc54 and 582aa6c.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (20)
  • knip.json
  • packages/authored-data-sync/src/authored-data-sync.ts
  • packages/transport-libp2p-core/src/transport.ts
  • packages/transport-libp2p-react-native/README.md
  • packages/transport-libp2p-react-native/package.json
  • packages/transport-libp2p-react-native/src/datachannel-early-message-polyfill.ts
  • packages/transport-libp2p-react-native/src/event-target-polyfill.ts
  • packages/transport-libp2p-react-native/src/factories.ts
  • packages/transport-libp2p-react-native/src/index.ts
  • packages/transport-libp2p-react-native/src/polyfills.ts
  • packages/transport-libp2p-react-native/src/quick-crypto-noise.ts
  • packages/transport-libp2p-react-native/src/rn-modules.d.ts
  • packages/transport-libp2p-react-native/src/text-codec-polyfill.ts
  • packages/transport-libp2p-react-native/src/websocket-buffered-amount-polyfill.ts
  • packages/transport-libp2p-react-native/tests/event-target-polyfill.test.ts
  • packages/transport-libp2p-react-native/tests/factories.test.ts
  • packages/transport-libp2p-react-native/tests/polyfills.test.ts
  • packages/transport-libp2p-react-native/tests/quick-crypto-noise.test.ts
  • packages/transport-libp2p-react-native/tests/text-codec-polyfill.test.ts
  • packages/transport-libp2p-react-native/tests/websocket-buffered-amount-polyfill.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/20-implement-react-native-transport-via-js-libp2p-with-polyfills

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@veeso veeso force-pushed the feat/20-implement-react-native-transport-via-js-libp2p-with-polyfills branch from 571e01c to 8e93bf2 Compare June 8, 2026 14:14
@veeso veeso requested a review from a team June 8, 2026 14:15
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

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.
@veeso veeso force-pushed the feat/20-implement-react-native-transport-via-js-libp2p-with-polyfills branch from 8e93bf2 to 582aa6c Compare June 8, 2026 14:32
@cocogitto-bot

cocogitto-bot Bot commented Jun 8, 2026

Copy link
Copy Markdown

✔️ 0696ba0...582aa6c - Conventional commits check succeeded.

@jost-s jost-s left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice one!

Comment on lines +2 to +11
* 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.
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Comment on lines +7 to +10
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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also don't know what Metro is. That and Hermes, are they native packages for iOS and Android?

Comment on lines +156 to +159
- 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also by design, not a limitation IMO

Comment on lines +160 to +162
- No background operation. iOS suspends the JS runtime when the app is
backgrounded; Android Doze mode throttles. Foreground-only without
additional OS-specific work.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for this for the future, Is that a permanent limitation, or just for the current implementation?

Comment on lines +164 to +165
- Mobile peer cannot listen for inbound direct connections. The transport
advertises `/p2p-circuit` and `/webrtc` listen addresses only.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Omit this, applies to all devices

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants