Skip to content

Latest commit

 

History

History
627 lines (494 loc) · 23.9 KB

File metadata and controls

627 lines (494 loc) · 23.9 KB

How Bare Kit Enables Hyperswarm in React Native

Executive Summary

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.


The Breakthrough: What is Bare Kit?

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.

Key Concept: Worklets

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                                  │
└───────────────────────────────────────────────────────────┘

Understanding IPC (Inter-Process Communication)

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:

  1. 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();
  2. IPC Channel transmits the message between processes

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

How Bitcoin-Tribe Implements It

1. Dependencies

{
  "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
  }
}

2. Architecture Components

A. Worklet Script (worklet.mjs)

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'),
    }));
  });
});

B. Bundle Generation

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

This creates app.bundle.mjs which contains:

  • All dependencies (Hyperswarm, hyperdht, etc.) bundled
  • Platform-specific native bindings
  • Ready to run in the Bare worklet runtime

C. React Native Manager (ChatPeerManager.ts in bitcoin-tribe)

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

3. Communication Flow

┌─────────────────────────────────────────────────────────────────┐
│  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                                         │
└─────────────────────────────────────────────────────────────────┘

Why This Works (Technical Deep Dive)

The Problem Recap

From WHY_HYPERDHT_NOT_IN_REACT_NATIVE.md:

  • React Native doesn't have UDP socket access
  • No dgram module
  • Sandboxed JavaScript runtime
  • Only HTTP/WebSocket/WebRTC available

The Bare Kit Solution

Bare Kit provides a NATIVE runtime, not a JavaScript polyfill:

  1. Native Module Architecture

    • react-native-bare-kit is a native module (Objective-C/Java)
    • Embeds the Bare runtime (like embedding Node.js)
    • Bare runtime has full OS access (UDP, TCP, filesystem, etc.)
  2. 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
    
  3. 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
  4. 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)
    

Comparison with Previous Understanding

What We Thought (From WHY_HYPERDHT_NOT_IN_REACT_NATIVE.md)

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

What's Actually Possible (With Bare Kit)

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


Advantages of This Approach

✅ Pros

  1. True P2P Networking

    • Full Hyperswarm/hyperdht functionality
    • UDP hole-punching works
    • NAT traversal works
    • DHT discovery works
  2. Performance

    • Native code execution (no JS bridge overhead for networking)
    • Efficient binary data handling
    • Direct socket access
  3. Compatibility

    • Use existing Node.js packages (Hyperswarm, Hypercore, etc.)
    • No need to rewrite code for React Native
    • Works on both iOS and Android
  4. Separation of Concerns

    • UI in React Native
    • Networking in Bare worklet
    • Clean RPC interface between them
  5. Offline-First

    • No dependency on backend servers
    • True peer-to-peer mobile apps
    • Works without internet (local network discovery)

⚠️ Considerations

  1. Additional Complexity

    • Need to manage two runtimes (React Native + Bare)
    • RPC communication adds cognitive overhead
    • Bundle generation step required
  2. Bundle Size

    • app.bundle.mjs is large (includes all Node.js dependencies)
    • react-native-bare-kit is 238 MB unpacked
  3. Learning Curve

    • Need to understand Bare runtime
    • RPC patterns for communication
    • Debugging across two runtimes
  4. Maturity

    • Relatively new technology (Bare Kit)
    • Smaller community compared to WebRTC solutions
    • Less documentation and examples

Implementation Checklist for HolepunchP2PChat

To implement this solution in the HolepunchP2PChat project:

Phase 1: Setup (1-2 days)

  • 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 manager
    

    See 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"
      }
    }

Phase 2: Migration (2-3 days)

  • Port existing Hyperswarm code to hyperswarm-worklet.mjs

    • Move from src/network/NetworkManager.ts to worklet
    • Use clean architecture with WorkletState and RPCManager classes
    • Add type-safe RPC handlers
    • See IMPLEMENTATION_GUIDE.md for complete code
  • Create HyperswarmManager class

    • 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
  • Update React Native components

    • Replace direct Hyperswarm calls with HyperswarmManager methods
    • Use event listeners with proper cleanup
    • See usage examples in IMPLEMENTATION_GUIDE.md

Phase 3: Testing (1-2 days)

  • 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

Phase 4: Documentation (1 day)

  • Update README with Bare Kit setup instructions
  • Document RPC commands
  • Add troubleshooting guide
  • Update architecture diagrams

Code Examples for HolepunchP2PChat

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.

Bitcoin-Tribe's Approach (Basic)

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 worklet
  • ChatPeerManager.ts - React Native manager
  • app.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 */ }
});

Our Improved Approach (Production-Ready)

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

See: IMPLEMENTATION_GUIDE.md

The implementation guide includes:

  1. 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',
    }
  2. Shared TypeScript Types (src/p2p/types/p2p.types.ts)

    export interface P2PMessage {
      id: string;
      roomTopic: string;
      text: string;
      sender: string;
      timestamp: number;
    }
  3. Clean Worklet (src/p2p/worklet/hyperswarm-worklet.mjs)

    • State management with WorkletState class
    • RPC communication with RPCManager class
    • Proper error handling and logging
  4. Clean Manager (src/p2p/managers/HyperswarmManager.ts)

    • Singleton pattern with proper lifecycle
    • Event listeners with cleanup functions
    • Type-safe API
  5. Usage Example with React hooks and proper cleanup

Quick Comparison

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


Comparison: Bare Kit vs Other Solutions

Solution Pros Cons Verdict
Bare Kit ✅ True P2P
✅ Full Hyperswarm support
✅ No server needed
✅ Native performance
⚠️ Large bundle
⚠️ New technology
⚠️ Extra complexity
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
⚠️ Complex setup
⚠️ Needs STUN/TURN
❌ Different from Hyperswarm
Alternative P2P
Native UDP Module ✅ Custom solution ❌ Massive work
❌ Maintain iOS/Android separately
❌ Reinvent hyperdht
Don't do this

Resources

Official Documentation

Examples

Community


Conclusion

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:

  1. ✅ We CAN use Hyperswarm in React Native
  2. ✅ We CAN have true P2P mobile apps
  3. ✅ We CAN avoid backend server dependencies
  4. ✅ We CAN leverage the existing Hyperswarm ecosystem

The bitcoin-tribe implementation proves this works in production. Now we can adapt it for HolepunchP2PChat! 🚀