The bitcoin-tribe project successfully runs Hyperswarm in React Native using react-native-bare-kit, which provides a native worklet runtime that runs Node.js-compatible code with full UDP socket access on iOS and Android. This is a game-changing solution that bypasses the limitations outlined in WHY_HYPERDHT_NOT_IN_REACT_NATIVE.md.
Bare Kit is a native runtime developed by Holepunch (the creators of Hyperswarm) that allows you to run Node.js-like JavaScript code with full system-level access on mobile platforms.
A worklet is an isolated JavaScript runtime that:
- Runs native code (not in the React Native JavaScript bridge)
- Has access to UDP sockets, TCP sockets, and all Node.js APIs
- Communicates with React Native via IPC (Inter-Process Communication)
- Runs in a separate thread with its own memory space
┌──────────────────────────────────────────────────────────┐
│ React Native App │
│ (JavaScript Bridge) │
│ - UI Components │
│ - Business Logic │
└────────────────────┬─────────────────────────────────────┘
│
│ IPC (Inter-Process Communication)
│ via bare-rpc
│
┌────────────────────▼─────────────────────────────────────┐
│ Bare Worklet │
│ (Native Runtime) │
│ │
│ ✅ Hyperswarm with UDP sockets │
│ ✅ Full Node.js APIs (dgram, net, crypto, etc.) │
│ ✅ Direct OS-level network access │
│ ✅ NAT traversal & DHT │
└───────────────────────────────────────────────────────────┘
IPC stands for Inter-Process Communication - a mechanism that allows different processes to communicate and exchange data.
Why IPC is needed here:
- React Native and Bare worklet run as separate processes
- Each has its own memory space (can't directly access each other's variables)
- They need a way to send messages and data back and forth
How it works in Bare Kit:
-
React Native → Worklet (sending commands):
// React Native sends a command const request = rpc.request(SEND_MESSAGE); request.send(JSON.stringify({ message: "Hello!" })); const reply = await request.reply();
-
IPC Channel transmits the message between processes
-
Worklet → React Native (sending events):
// Worklet notifies React Native of new message const req = rpc.request(ON_MESSAGE); req.send(JSON.stringify({ from: peerKey, text: "Hi!" }));
The tool: bare-rpc
- RPC = Remote Procedure Call (a type of IPC)
- Lets you call functions in another process as if they were local
- Handles serialization, message passing, and replies automatically
Think of it like: Two people in different rooms passing notes under the door - that's IPC! 📝
In practice:
- React Native sends: "Join room X"
- Worklet receives message via IPC
- Worklet executes:
swarm.join(roomTopic) - Worklet sends back via IPC: "Joined successfully"
- React Native receives confirmation
{
"dependencies": {
"react-native-bare-kit": "0.5.6", // Native worklet runtime
"bare-rpc": "0.2.5", // RPC for IPC communication
"hyperswarm": "4.11.7", // P2P networking (runs in worklet)
"b4a": "1.6.7", // Buffer utilities
"buffer": "6.0.3" // Buffer polyfill
}
}This is the Node.js-compatible code that runs in the native worklet with full system access:
import Hyperswarm from 'hyperswarm';
import RPC from 'bare-rpc';
import b4a from 'b4a';
import { Buffer } from 'buffer';
const { IPC } = BareKit; // Native IPC interface
// ✅ This works because we're in a native worklet!
const swarm = new Hyperswarm({
seed: Buffer.from(Bare.argv[0], 'hex'), // Seed from React Native
});
// Set up RPC to communicate with React Native
const rpc = new RPC(IPC, (req, error) => {
const data = b4a.toString(req.data);
if (req.command === GET_KEYS) {
req.reply(JSON.stringify({
publicKey: swarm.keyPair.publicKey.toString('hex'),
secretKey: swarm.keyPair.secretKey.toString('hex'),
}));
} else if (req.command === SEND_MESSAGE) {
// Send message through Hyperswarm connection
const { pubKey, message } = JSON.parse(data);
const conn = connections.get(pubKey);
if (conn) conn.write(message);
}
});
// Handle incoming connections
swarm.on('connection', (conn, info) => {
connections.set(info.publicKey.toString('hex'), conn);
// Notify React Native of new connection
const req = rpc.request(ON_CONNECTION);
req.send(JSON.stringify({
publicKey: info.publicKey.toString('hex'),
}));
// Forward incoming data to React Native
conn.on('data', data => {
const req = rpc.request(ON_MESSAGE);
req.send(JSON.stringify({
data: data.toString(),
publicKey: info.publicKey.toString('hex'),
}));
});
});The worklet code is compiled into a native bundle using bare-pack:
npx bare-pack \
--target ios \
--target android \
--linked \
--out src/services/p2p/app.bundle.mjs \
src/services/p2p/worklet.mjsThis creates app.bundle.mjs which contains:
- All dependencies (Hyperswarm, hyperdht, etc.) bundled
- Platform-specific native bindings
- Ready to run in the Bare worklet runtime
This is the React Native side that manages the worklet. Bitcoin-tribe uses a basic implementation:
import { Worklet } from 'react-native-bare-kit';
import RPC from 'bare-rpc';
import b4a from 'b4a';
import bundle from './app.bundle.mjs';
export default class ChatPeerManager {
worklet: Worklet;
rpc: any;
async init(seed: string): Promise<boolean> {
await this.worklet.start('/app.bundle', bundle, [seed]);
// Set up RPC with numeric command IDs
this.rpc = new RPC(this.worklet.IPC, async req => {
const data = b4a.toString(req.data);
if (req.command === 4) { // ON_MESSAGE (magic number)
const message = JSON.parse(data);
if (this.onMessageCallback) {
this.onMessageCallback(message);
}
}
});
return true;
}
async sendMessage(pubKey: string, message: string) {
const request = this.rpc.request(3); // SEND_MESSAGE (magic number)
request.send(JSON.stringify({ pubKey, message }));
return await request.reply();
}
}For a cleaner, production-ready version with:
- Type-safe RPC commands (no magic numbers)
- Proper event listener patterns
- Better error handling
- Full TypeScript types
- Clean architecture
👉 See IMPLEMENTATION_GUIDE.md
┌─────────────────────────────────────────────────────────────────┐
│ React Native UI │
│ │
│ chatManager.sendMessage(peerPubKey, "Hello!") │
└──────────────────┬──────────────────────────────────────────────┘
│
│ 1. RPC Request (SEND_MESSAGE)
▼
┌─────────────────────────────────────────────────────────────────┐
│ React Native Manager (HyperswarmManager) │
│ │
│ this.rpc.request(SEND_MESSAGE).send(data) │
└──────────────────┬──────────────────────────────────────────────┘
│
│ 2. IPC (via react-native-bare-kit)
▼
┌─────────────────────────────────────────────────────────────────┐
│ Bare Worklet (worklet.mjs) │
│ │
│ if (req.command === SEND_MESSAGE) { │
│ const conn = connections.get(pubKey); │
│ conn.write(message); // ✅ Sends via Hyperswarm UDP! │
│ } │
└──────────────────┬──────────────────────────────────────────────┘
│
│ 3. UDP packets via Hyperswarm/hyperdht
▼
┌───────────────────┐
│ DHT Network │
│ (Internet) │
└───────────────────┘
│
│ 4. Peer receives message
▼
┌─────────────────────────────────────────────────────────────────┐
│ Bare Worklet (worklet.mjs) │
│ │
│ conn.on('data', data => { │
│ const req = rpc.request(ON_MESSAGE); │
│ req.send(data); // Send to React Native │
│ }); │
└──────────────────┬──────────────────────────────────────────────┘
│
│ 5. IPC back to React Native
▼
┌─────────────────────────────────────────────────────────────────┐
│ React Native Manager (HyperswarmManager) │
│ │
│ this.onMessageCallback(message) │
└──────────────────┬──────────────────────────────────────────────┘
│
│ 6. Update UI
▼
┌─────────────────────────────────────────────────────────────────┐
│ React Native UI │
│ │
│ Display message in chat │
└─────────────────────────────────────────────────────────────────┘
From WHY_HYPERDHT_NOT_IN_REACT_NATIVE.md:
- React Native doesn't have UDP socket access
- No
dgrammodule - Sandboxed JavaScript runtime
- Only HTTP/WebSocket/WebRTC available
Bare Kit provides a NATIVE runtime, not a JavaScript polyfill:
-
Native Module Architecture
react-native-bare-kitis a native module (Objective-C/Java)- Embeds the Bare runtime (like embedding Node.js)
- Bare runtime has full OS access (UDP, TCP, filesystem, etc.)
-
Not Running in React Native's JS Engine
❌ React Native JavaScript (Hermes/JSC) ↓ No UDP access ✅ Bare Worklet (Native Runtime) ↓ Full UDP access via native bindings -
Bare Runtime vs React Native Runtime
Feature React Native JS Bare Worklet Engine Hermes/JavaScriptCore Bare (Node.js-like) UDP Sockets ❌ No ✅ Yes TCP Sockets ❌ No ✅ Yes File System Limited ✅ Full access Crypto Polyfills ✅ Native crypto Threading Limited ✅ Native threads Node.js APIs ❌ No ✅ Yes (dgram, net, etc.) Hyperswarm ❌ Won't work ✅ Works perfectly -
How IPC Works Under the Hood
iOS: React Native ←→ Objective-C Bridge ←→ Bare Worklet (Native) (BareKit.framework) Android: React Native ←→ Java/Kotlin Bridge ←→ Bare Worklet (Native) (BareKit library)
"Hyperdht fundamentally cannot work in React Native because it requires UDP socket access, which is not available in mobile JavaScript environments."
This was correct for standard React Native, but...
"Hyperswarm CAN work in React Native if you run it in a Bare worklet, which is a native runtime with full Node.js compatibility and OS-level network access."
Key Insight: Bare Kit doesn't polyfill or bridge Node.js APIs to React Native. Instead, it runs a separate native runtime alongside React Native.
-
True P2P Networking
- Full Hyperswarm/hyperdht functionality
- UDP hole-punching works
- NAT traversal works
- DHT discovery works
-
Performance
- Native code execution (no JS bridge overhead for networking)
- Efficient binary data handling
- Direct socket access
-
Compatibility
- Use existing Node.js packages (Hyperswarm, Hypercore, etc.)
- No need to rewrite code for React Native
- Works on both iOS and Android
-
Separation of Concerns
- UI in React Native
- Networking in Bare worklet
- Clean RPC interface between them
-
Offline-First
- No dependency on backend servers
- True peer-to-peer mobile apps
- Works without internet (local network discovery)
-
Additional Complexity
- Need to manage two runtimes (React Native + Bare)
- RPC communication adds cognitive overhead
- Bundle generation step required
-
Bundle Size
app.bundle.mjsis large (includes all Node.js dependencies)react-native-bare-kitis 238 MB unpacked
-
Learning Curve
- Need to understand Bare runtime
- RPC patterns for communication
- Debugging across two runtimes
-
Maturity
- Relatively new technology (Bare Kit)
- Smaller community compared to WebRTC solutions
- Less documentation and examples
To implement this solution in the HolepunchP2PChat project:
-
Install dependencies
yarn add react-native-bare-kit@0.5.6 bare-rpc hyperswarm b4a buffer
-
Create worklet structure (recommended clean architecture)
src/p2p/ ├── constants/ │ └── rpc-commands.ts # Type-safe RPC command definitions ├── types/ │ └── p2p.types.ts # Shared TypeScript types ├── worklet/ │ ├── hyperswarm-worklet.mjs # Bare worklet with Hyperswarm │ └── app.bundle.mjs # Generated bundle (gitignore this) └── managers/ └── HyperswarmManager.ts # React Native managerSee IMPLEMENTATION_GUIDE.md for complete file contents
-
Add bundle generation script to
package.json{ "scripts": { "bundle:worklet": "bare-pack --target ios --target android --linked --out src/p2p/worklet/app.bundle.mjs src/p2p/worklet/hyperswarm-worklet.mjs", "prebuild": "yarn bundle:worklet" } }
-
Port existing Hyperswarm code to
hyperswarm-worklet.mjs- Move from
src/network/NetworkManager.tsto worklet - Use clean architecture with
WorkletStateandRPCManagerclasses - Add type-safe RPC handlers
- See IMPLEMENTATION_GUIDE.md for complete code
- Move from
-
Create
HyperswarmManagerclass- Improved version of bitcoin-tribe's
ChatPeerManager - Handle worklet lifecycle
- Provide clean, type-safe API for React Native components
- See IMPLEMENTATION_GUIDE.md for production-ready code
- Improved version of bitcoin-tribe's
-
Update React Native components
- Replace direct Hyperswarm calls with
HyperswarmManagermethods - Use event listeners with proper cleanup
- See usage examples in IMPLEMENTATION_GUIDE.md
- Replace direct Hyperswarm calls with
-
Test on iOS
- Build and run
- Verify Hyperswarm connectivity
- Check message delivery
-
Test on Android
- Build and run
- Verify Hyperswarm connectivity
- Check message delivery
-
Test P2P scenarios
- Peer discovery
- Message exchange
- Room joining/leaving
- Offline/online transitions
- Update README with Bare Kit setup instructions
- Document RPC commands
- Add troubleshooting guide
- Update architecture diagrams
Note: The code examples below show the basic approach used by bitcoin-tribe. For production-ready, clean architecture with full type safety and best practices, see IMPLEMENTATION_GUIDE.md.
The bitcoin-tribe project uses this basic structure. While it works, the code could be cleaner and more maintainable.
Key files:
worklet.mjs- Hyperswarm code in native workletChatPeerManager.ts- React Native managerapp.bundle.mjs- Generated bundle
Example structure:
// Basic worklet example from bitcoin-tribe
import Hyperswarm from 'hyperswarm';
import RPC from 'bare-rpc';
const swarm = new Hyperswarm({ seed });
const rpc = new RPC(IPC, (req) => {
// Handle commands with numeric IDs
if (req.command === 1) { /* GET_KEYS */ }
if (req.command === 2) { /* JOIN_ROOM */ }
});For a complete, production-ready implementation with:
- ✅ Clean architecture with proper separation of concerns
- ✅ Full TypeScript type safety
- ✅ Type-safe RPC commands (enums instead of magic numbers)
- ✅ Better state management (class-based, no global state)
- ✅ Proper error handling
- ✅ Event listener patterns with cleanup
- ✅ Comprehensive documentation
The implementation guide includes:
-
Type-Safe RPC Commands (
src/p2p/constants/rpc-commands.ts)export enum WorkletCommand { GET_KEYS = 'GET_KEYS', JOIN_ROOM = 'JOIN_ROOM', SEND_MESSAGE = 'SEND_MESSAGE', }
-
Shared TypeScript Types (
src/p2p/types/p2p.types.ts)export interface P2PMessage { id: string; roomTopic: string; text: string; sender: string; timestamp: number; }
-
Clean Worklet (
src/p2p/worklet/hyperswarm-worklet.mjs)- State management with
WorkletStateclass - RPC communication with
RPCManagerclass - Proper error handling and logging
- State management with
-
Clean Manager (
src/p2p/managers/HyperswarmManager.ts)- Singleton pattern with proper lifecycle
- Event listeners with cleanup functions
- Type-safe API
-
Usage Example with React hooks and proper cleanup
| Aspect | Bitcoin-Tribe | Our Improved Version |
|---|---|---|
| File naming | Generic (worklet.mjs) |
Descriptive (hyperswarm-worklet.mjs) |
| Class naming | ChatPeerManager |
HyperswarmManager |
| RPC commands | Magic numbers (1, 2, 3) | Type-safe enums |
| State management | Global variables | Encapsulated classes |
| Type safety | Minimal | Full TypeScript |
| Error handling | Basic | Comprehensive |
| Event system | Callbacks | Proper listeners with cleanup |
👉 For complete code examples, see IMPLEMENTATION_GUIDE.md
| Solution | Pros | Cons | Verdict |
|---|---|---|---|
| Bare Kit | ✅ True P2P ✅ Full Hyperswarm support ✅ No server needed ✅ Native performance |
Best for true P2P | |
| WebSocket Bridge | ✅ Simple ✅ Well-understood ✅ Easy debugging |
❌ Requires server ❌ Not true P2P ❌ Server costs |
Good for hybrid |
| WebRTC | ✅ True P2P ✅ Industry standard ✅ Well-tested |
❌ Different from Hyperswarm |
Alternative P2P |
| Native UDP Module | ✅ Custom solution | ❌ Massive work ❌ Maintain iOS/Android separately ❌ Reinvent hyperdht |
Don't do this |
Bare Kit is a revolutionary solution that enables true peer-to-peer networking in React Native by providing a native runtime with full Node.js compatibility. While it adds complexity, it's the only way to run Hyperswarm natively on mobile without rewriting the entire stack.
For the HolepunchP2PChat project, this means:
- ✅ We CAN use Hyperswarm in React Native
- ✅ We CAN have true P2P mobile apps
- ✅ We CAN avoid backend server dependencies
- ✅ We CAN leverage the existing Hyperswarm ecosystem
The bitcoin-tribe implementation proves this works in production. Now we can adapt it for HolepunchP2PChat! 🚀