Quickstart: join the mesh and ship your first feature

Last updated

The fastest path from an empty React Native project to a device on the mesh: install, initialize, join, send and receive a message, and invoke a service on a peer. One install exposes the whole offline-first SDK, and the same TypeScript API runs on iOS and Android. Every snippet below is real SDK surface you can copy into your app.

What you will build

A React Native app that initializes an OfflineProtocol with your identity, joins the mesh over BLE and WiFi Direct, sends and receives an MLS-encrypted message relayed across up to 8 hops, and both advertises and invokes a service over the mesh, all with no server in the path.

Prerequisites

1Install the SDK

Add the package with npm or yarn. The Rust core ships inside the React Native binding, so there is no separate native library to compile. You do link the native module once per platform, then rebuild.

Shell
# with npm
npm install @offline-protocol/mesh-sdk

# or with yarn
yarn add @offline-protocol/mesh-sdk

Then link the native module. On iOS, add the pod and run pod install. On Android, register the package in MainApplication.kt.

iOS · Podfile
target 'YourAppName' do
  use_expo_modules!
  # add the Mesh SDK pod
  pod 'MeshSdk', :path => '../node_modules/@offline-protocol/mesh-sdk/ios'
end
Android · MainApplication.kt
import com.offlineprotocol.OfflineProtocolPackage

// inside getPackages()
override fun getPackages(): List<ReactPackage> =
  PackageList(this).packages.apply {
    add(OfflineProtocolPackage())
  }

Both platforms also need Bluetooth and location permissions in Info.plist and AndroidManifest.xml. The full permission blocks are in the installation guide.

2Initialize the protocol

Create an OfflineProtocol with your appId and userId. This is where your OfflineID lives, the Ed25519 self-sovereign identity that verifies you to peers. Encryption is enabled by default with automatic key exchange, so there is nothing to configure to get an MLS-encrypted session.

TypeScript
import { OfflineProtocol, MessagePriority } from '@offline-protocol/mesh-sdk';

const protocol = new OfflineProtocol({
  appId: 'my-app',
  userId: 'user123',
  // encryption is on by default with auto key exchange
});
3Join the mesh

Register your event listeners first, then call start(). The device begins scanning and advertising over BLE and WiFi Direct at once, and DORS handles transport selection and failover. Peers surface as neighbor_discovered events with a transport and signal reading.

TypeScript
// register listeners before you start, so no early event is missed
protocol.on('neighbor_discovered', (event) =>
  console.log(`peer ${event.peer_id} via ${event.transport}, rssi ${event.rssi}`));

protocol.on('secure_session_established', (event) =>
  console.log(`secure session with ${event.peer_id}`));

// start scanning and advertising; DORS picks the transport
await protocol.start();
4Send and receive a message

Subscribe to message_received, then call sendMessage with a recipient, content, and priority. DORS handles acknowledgment, retry, deduplication, and relaying across up to 8 hops, so a peer that is not in direct radio range still receives it. sendMessage returns a message ID you can correlate with message_delivered.

TypeScript
// receive
protocol.on('message_received', (event) => {
  console.log(`from ${event.sender}: ${event.content}`);
  console.log(`encrypted: ${event.encrypted}, hops: ${event.hop_count}`);
});

// send
const messageId = await protocol.sendMessage({
  recipient: 'user456',
  content: 'Hello!',
  priority: MessagePriority.High,
});

// confirm delivery across the mesh
protocol.on('message_delivered', (event) =>
  console.log(`delivered ${event.message_id} in ${event.latency_ms}ms, ${event.hop_count} hops`));
5Advertise and invoke a service

Messaging moves data between known peers. Service discovery lets a device find and call unknown peers by what they can do, like an API endpoint on the mesh with no server or DNS. Create a MeshServices instance, then register, discover, and invoke.

TypeScript · provider
import { MeshServices } from '@offline-protocol/mesh-sdk';

const services = new MeshServices();

// advertise a capability with optional metadata
await services.registerService('translate.v1', '1.0', {
  languages: 'en,es,fr,de',
});

// answer incoming requests
protocol.on('service_request_received', async (event) => {
  const { text, targetLang } = JSON.parse(event.body);
  const translated = await translateLocally(text, targetLang);
  await services.respondToServiceRequest(
    event.request_id, event.sender, event.service_id,
    'ok', JSON.stringify({ translated }),
  );
});
TypeScript · consumer
// discover providers across the mesh (pass null for all services)
await services.discoverServices('translate.v1');

protocol.on('service_discovered', (event) => {
  console.log(`found ${event.service_id} v${event.version}, ${event.hop_count} hops away`);
  // invoke it request / response
  services.sendServiceRequest(
    event.provider_peer_id, event.service_id, 'translate',
    JSON.stringify({ text: 'Hello world', targetLang: 'es' }),
  );
});

protocol.on('service_response_received', (event) => {
  if (event.status === 'ok') {
    const { translated } = JSON.parse(event.body);
    console.log(`result: ${translated}`);
  }
});

For every method, event, and option, see the API methods reference and the events reference.

What you just built

Recap

In five steps your app installs the SDK, initializes an identity, joins the mesh, exchanges MLS-encrypted messages relayed across up to 8 hops, and both offers and calls a service over the network, with no server, no DNS, and no registry in the path. Discovery, acknowledgment, retry, deduplication, and transport failover are the default behavior, not extra code. Every app you ship this way also relays for every other app on the mesh, so the network grows with you.

Next steps

Quickstart FAQ

Do I need a server or backend to run this?

No. The quickstart runs entirely device-to-device over the mesh. There is no server in the message path, no DNS, and no registry. Everything works with the internet fully offline. The internet transport is optional and off this path.

Why can I not see peers on a simulator?

Peer discovery uses BLE and WiFi Direct radios that simulators and emulators do not expose. Use two physical devices in range of each other to see neighbor_discovered fire and messaging work.

Which platforms does the same code run on?

The same TypeScript API runs on iOS and Android through one React Native binding over the Rust core. One npm install exposes the full SDK surface on both, once you link the native module per platform.

Is encryption something I have to turn on?

No. MLS (RFC 9420) end-to-end encryption is on by default with automatic key exchange on peer discovery. You get an encrypted session per conversation without writing any crypto code.

You are on the mesh. Keep building. One install, iOS and Android.

Read the docs All guides