Skip to main content

API Methods

Constructor

new OfflineProtocol(config: ProtocolConfig)
See Configuration for all config options.

Lifecycle Methods

MethodReturnsDescription
start()Promise<void>Start the protocol. Auto-initializes MLS if encryption is enabled, starts BLE scanning/advertising
stop()Promise<void>Stop the protocol and disconnect all peers
pause()Promise<void>Pause for background mode
resume()Promise<void>Resume from paused state
destroy()Promise<void>Clean up all resources
getState()Promise<ProtocolState>Get current state (Stopped, Running, Paused)
emitTestEvent()Promise<void>Emit a test event to verify event system is working

Messaging

MethodReturnsDescription
sendMessage(params)Promise<string>Send message, returns message ID
receiveMessage()Promise<MessageReceivedEvent | null>Poll for next received message
forwardMessage(params)Promise<string>Forward message with original sender attribution
interface SendMessageParams {
  recipient: string;
  content: string;
  priority?: MessagePriority;  // default: Medium
  replyToMsg?: string;         // ID of message being replied to
}

interface ForwardMessageParams {
  originalMessageJson: string; // Original message JSON as received
  newRecipient: string;
  priority?: MessagePriority;
}

enum MessagePriority {
  Low = 0,
  Medium = 1,
  High = 2,
  Critical = 3,
}

Connection Requests

MethodReturnsDescription
sendConnectionRequest(params)Promise<string>Send a connection request to a peer
acceptConnectionRequest(params)Promise<string>Accept a connection request
rejectConnectionRequest(params)Promise<string>Reject a connection request
cancelConnectionRequest(params)Promise<string>Cancel a previously sent request
interface SendConnectionRequestParams {
  recipient: string;
  senderName: string;
  keyPackage?: number[];  // Optional MLS key package bytes
}

interface AcceptConnectionRequestParams {
  recipient: string;
  accepterName: string;
  keyPackage?: number[];
}

interface RejectConnectionRequestParams {
  recipient: string;
}

interface CancelConnectionRequestParams {
  recipient: string;
}

Media & File Transfer

MethodReturnsDescription
sendMedia(params)Promise<string>Send media (image, video, audio, file) — returns file ID
sendFile(params)Promise<string>Send a generic file (convenience wrapper)
sendImage(recipient, fileData, fileName, metadata?)Promise<string>Send an image
sendVoiceNote(recipient, fileData, fileName, metadata?)Promise<string>Send a voice note
sendVideoNote(recipient, fileData, fileName, metadata?)Promise<string>Send a video note
sendVideo(recipient, fileData, fileName, metadata?)Promise<string>Send a video
getFileProgress(fileId)Promise<FileProgress | null>Get file transfer progress
cancelFileTransfer(fileId)Promise<boolean>Cancel an active transfer
processFileChunk(...)Promise<void>Process a file chunk (custom handling)
finalizeFile(fileId)Promise<void>Finalize a file transfer after all chunks
All media methods accept base64-encoded file data. The SDK handles chunking and reassembly automatically.
interface SendMediaParams {
  recipient: string;
  fileData: string;             // Base64-encoded file data
  fileName: string;
  contentType: ContentType;
  mediaMetadata?: MediaMetadata;
}

interface SendFileParams {
  recipient: string;
  fileData: string;             // Base64-encoded file data
  fileName: string;
}

enum ContentType {
  Text = 'text',
  Image = 'image',
  Video = 'video',
  Audio = 'audio',
  VoiceNote = 'voice_note',
  VideoNote = 'video_note',
  File = 'file',
  FileChunk = 'file_chunk',
}

interface MediaMetadata {
  mimeType: string;
  fileName: string;
  fileSize: number;
  durationMs?: number;         // Audio/video duration
  width?: number;              // Image/video width
  height?: number;             // Image/video height
  thumbnailBase64?: string;    // Small preview (< 2 KB)
}

Transport Management

MethodReturnsDescription
getActiveTransports()Promise<TransportType[]>Get list of active transports
enableTransport(type, config?)Promise<void>Enable a transport
disableTransport(type)Promise<void>Disable a transport
forceTransport(type)Promise<void>Force specific transport (override DORS)
releaseTransportLock()Promise<void>Release forced transport, let DORS decide
getTransportMetrics(type)Promise<TransportMetrics | null>Get transport statistics
type TransportType = 'ble' | 'internet' | 'wifiDirect';

interface TransportMetrics {
  packetsSent: number;
  packetsReceived: number;
  bytesSent: number;
  bytesReceived: number;
  errorRate: number;
  avgLatencyMs: number;
}

Bluetooth

MethodReturnsDescription
isBluetoothEnabled()Promise<boolean>Check if Bluetooth is enabled
requestEnableBluetooth()Promise<boolean>Request to enable Bluetooth (Android only)
getBLePeerCount()Promise<number>Get number of discovered BLE peers

Network Topology

MethodReturnsDescription
getTopology()Promise<NetworkTopology>Get network topology snapshot
getMessageStats()Promise<MessageDeliveryStats[]>Get message delivery statistics
getDeliverySuccessRate()Promise<number>Get delivery success rate (0-1)
getMedianLatency()Promise<number | null>Get median latency in ms
getMedianHops()Promise<number | null>Get median hop count

Battery & Relay

MethodReturnsDescription
setBatteryLevel(level)Promise<void>Set battery level (0-100) for mesh decisions
getBatteryLevel()Promise<number | null>Get current battery level
setRelayPriority(priority)Promise<void>Set relay priority ('low', 'medium', 'high')
getRelayPriority()Promise<string>Get current relay priority
isRelay()Promise<boolean>Check if device is acting as a relay

DORS Configuration

MethodReturnsDescription
updateDorsConfig(config)Promise<void>Update DORS settings at runtime
getDorsConfig()Promise<DorsConfig>Get current DORS configuration
shouldEscalateToWifi()Promise<boolean>Check if DORS recommends WiFi escalation

Reliability Configuration

MethodReturnsDescription
updateAckConfig(config)Promise<void>Update ACK settings
updateRetryConfig(config)Promise<void>Update retry settings
updateDedupConfig(config)Promise<void>Update deduplication settings
getDedupStats()Promise<DedupStats>Get deduplication statistics
getPendingAckCount()Promise<number>Get pending ACK count
getRetryQueueSize()Promise<number>Get retry queue size

Gradient Routing

MethodReturnsDescription
learnRoute(destination, nextHop, hopCount, quality, sequenceNumber?)Promise<void>Learn a route from incoming message
getBestRoute(destination)Promise<RouteEntry | null>Get best route to destination
getAllRoutes(destination)Promise<RouteEntry[]>Get all routes to destination
hasRoute(destination)Promise<boolean>Check if route exists
removeNeighborRoutes(neighborId)Promise<void>Remove routes through neighbor
cleanupExpiredRoutes()Promise<void>Clean up expired routes
getRoutingStats()Promise<RoutingStats>Get routing table statistics
updateRoutingConfig(config)Promise<void>Update routing configuration

End-to-End Encryption (MLS)

MLS is automatically initialized when start() is called (if encryption.enabled is true). These methods provide manual control over encryption operations.

Session Management

MethodReturnsDescription
initializeMlsWithSecureStorage()Promise<void>Init MLS with platform secure storage (auto-called by start())
isMlsInitialized()Promise<boolean>Check if MLS is ready
establishSecureSession(peerId)Promise<MlsWelcome | null>High-level: establish session with peer (recommended)
mlsHasSession(otherUserId)Promise<boolean>Check if session exists
hasPendingKeyPackage(peerId)Promise<boolean>Check if peer’s key package is available
getEstablishmentState(peerId)Promise<EstablishmentState>Get session establishment state
mlsListSessions()Promise<string[]>List all active session user IDs
mlsDeleteSession(otherUserId)Promise<void>Delete a session
type EstablishmentState =
  | 'NoKeyPackage'      // No key package received from peer
  | 'HaveKeyPackage'    // Key package available, session can be created
  | 'SessionPending'    // Welcome sent, waiting for confirmation
  | 'SessionConfirmed'; // Session fully established

Key Package Management

MethodReturnsDescription
mlsGenerateKeyPackage()Promise<MlsKeyPackage>Generate a new key package
mlsGetOrCreateKeyPackage()Promise<MlsKeyPackage>Get existing or create new key package
mlsGetPendingKeyPackages()Promise<MlsKeyPackage[]>Get unsynced key packages
mlsMarkKeyPackageSynced(packageId)Promise<void>Mark key package as synced
mlsImportKeyPackage(userId, data)Promise<void>Import a peer’s key package

Low-Level Session Operations

MethodReturnsDescription
mlsCreateSession(otherUserId)Promise<MlsWelcome>Create session (requires imported key package)
mlsJoinSession(welcome)Promise<MlsSessionInfo>Join session from Welcome message
mlsEncryptForUser(otherUserId, plaintext)Promise<MlsEncryptedMessage>Encrypt message for user
mlsDecryptFromUser(encrypted)Promise<number[] | null>Decrypt message from user
mlsDecrypt(encrypted)Promise<number[] | null>Decrypt any MLS message (1:1 or group)
mlsProcessWelcome(welcome)Promise<MlsSessionInfo | MlsGroupInfo>Process Welcome (auto-detects type)

Identity & Signing

MethodReturnsDescription
getIdentityPublicKey()Promise<number[]>Get Ed25519 public key (32 bytes)
deriveUserIdFromPublicKey(publicKey)Promise<string>Derive user ID from public key (base58)
signData(data)Promise<number[]>Sign data with identity key (64 byte signature)
verifySignature(publicKey, data, signature)Promise<boolean>Verify an Ed25519 signature

Trust

MethodReturnsDescription
resetTofuForPeer(peerId)Promise<boolean>Reset TOFU trust pin for a peer

Group Messaging

Groups use MLS for end-to-end encryption with mesh transport for delivery.
MethodReturnsDescription
meshCreateGroup(groupName)Promise<MlsGroupInfo>Create a new encrypted group
meshInviteToGroup(groupId, inviteeUserId)Promise<void>Invite a user to a group
meshSendGroupMessage(groupId, content, priority?, replyToMsg?)Promise<string[]>Send message to all members (returns per-member IDs)
meshForwardMessageToGroup(params)Promise<string[]>Forward message to group with attribution
meshRemoveFromGroup(groupId, memberId)Promise<void>Remove a member from a group
meshLeaveGroup(groupId)Promise<void>Leave a group
meshListGroups()Promise<string[]>List all group IDs
meshGetGroupInfo(groupId)Promise<MlsGroupInfo | null>Get group info
meshSetMemberRole(groupId, userId, role)Promise<void>Set member role (admin only)
meshGetMemberRole(groupId, userId)Promise<string>Get a member’s role
meshGetGroupRoles(groupId)Promise<Record<string, string>>Get all member roles
meshRenameGroup(groupId, newName)Promise<void>Rename a group (admin only)
interface ForwardMessageToGroupParams {
  originalMessageJson: string;
  groupId: string;
  priority?: MessagePriority;
}
Example:
// Create a group
const group = await protocol.meshCreateGroup('Team Chat');

// Invite members
await protocol.meshInviteToGroup(group.groupId, 'user456');
await protocol.meshInviteToGroup(group.groupId, 'user789');

// Send a group message
const messageIds = await protocol.meshSendGroupMessage(
  group.groupId,
  'Hello team!',
  'high'
);

// Listen for group messages
protocol.on('group_message_received', (event) => {
  console.log(`${event.sender}: ${event.content}`);
});

// Manage roles
await protocol.meshSetMemberRole(group.groupId, 'user456', 'admin');

Presence, Typing & Read Receipts

MethodReturnsDescription
sendPresenceUpdate(recipient, status)Promise<string>Send presence status ('online', 'away', 'offline')
sendTypingIndicator(recipient, conversationId, isTyping)Promise<string>Send typing indicator
sendReadReceipt(recipient, messageIds)Promise<string>Send read receipt for message IDs
// Send presence
await protocol.sendPresenceUpdate('user456', 'online');

// Send typing indicator
await protocol.sendTypingIndicator('user456', 'user456', true);

// Send read receipt
await protocol.sendReadReceipt('user456', ['msg-id-1', 'msg-id-2']);

// Listen for updates
protocol.on('presence_updated', (event) => {
  console.log(`${event.peer_id} is ${event.status}`);
});

protocol.on('typing_indicator_received', (event) => {
  console.log(`${event.sender} is ${event.is_typing ? 'typing...' : 'idle'}`);
});

protocol.on('read_receipt_received', (event) => {
  console.log(`${event.sender} read messages: ${event.message_ids}`);
});

User Blocking

MethodReturnsDescription
blockUser(userId)Promise<void>Block a user (messages silently dropped)
unblockUser(userId)Promise<void>Unblock a user
getBlockedUsers()Promise<string[]>Get list of blocked user IDs
isUserBlocked(userId)Promise<boolean>Check if a user is blocked

Service Discovery & RPC (MeshServices)

Service Discovery turns the mesh into an offline internet — a network where every device can be both a client and a server. Devices register capabilities, others discover them through multi-hop routing, and invoke them with a request/response pattern. No server, no DNS, no infrastructure. On the traditional internet, you resolve a hostname, connect to a server, and make a request. Service Discovery follows the same pattern, but everything runs over the mesh. The devices around you are the infrastructure. While messaging moves data between known peers, Service Discovery lets devices find and interact with unknown peers based on what they can do.

How It Works

  1. A device registers a service with an ID, version, and capability metadata
  2. Other devices broadcast discovery queries that propagate across the mesh
  3. Providers respond with service_discovered events including their peer ID, version, and capabilities
  4. The consumer sends a request to a specific provider, which receives it as a service_request_received event
  5. The provider processes the request and sends a response back
Discovery queries propagate through the mesh via multi-hop routing, so services don’t need to be directly connected — a device 5 hops away can still be discovered and invoked, just like a server on the other side of the internet.

Setup

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

const protocol = new OfflineProtocol(config);
await protocol.start();

const services = new MeshServices();

API

MethodReturnsDescription
registerService(serviceId, version, capabilities?)Promise<void>Register a local service with optional key-value capabilities
unregisterService(serviceId)Promise<boolean>Unregister a service (returns true if found)
discoverServices(serviceId?)Promise<string>Broadcast discovery query — pass null to discover all services (returns query ID)
sendServiceRequest(provider, serviceId, method, body)Promise<string>Send a request to a specific provider (returns request ID for correlating responses)
respondToServiceRequest(requestId, requester, serviceId, status, body)Promise<string>Respond to an incoming request with a status and body

Provider Example

// Register a translation service
await services.registerService('translate.v1', '1.0', {
  languages: 'en,es,fr,de',
  format: 'json',
  maxLength: '5000',
});

// Handle incoming requests
protocol.on('service_request_received', async (event) => {
  if (event.service_id === 'translate.v1') {
    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 })
    );
  }
});

Consumer Example

// Discover translation services on the mesh
const queryId = await services.discoverServices('translate.v1');

// Listen for providers
protocol.on('service_discovered', (event) => {
  console.log(`Found ${event.service_id} v${event.version} at ${event.provider_peer_id}`);
  console.log(`Capabilities:`, event.capabilities); // { languages: 'en,es,fr,de', ... }
  console.log(`${event.hop_count} hops away`);

  // Send a request to the provider
  services.sendServiceRequest(
    event.provider_peer_id,
    event.service_id,
    'translate',
    JSON.stringify({ text: 'Hello world', targetLang: 'es' })
  );
});

// Handle responses
protocol.on('service_response_received', (event) => {
  if (event.status === 'ok') {
    const { translated } = JSON.parse(event.body);
    console.log(`Translation: ${translated}`);
  } else {
    console.error(`Service error: ${event.body}`);
  }
});

Use Cases

Local AI Inference — A device with a loaded ML model registers an inference service. Nearby devices discover it and send classification or generation requests without needing cloud access. Sensor Data Feeds — IoT sensors register as data providers (temperature, air quality, GPS). Nearby devices discover and query them in real time, creating ad-hoc sensor networks with zero infrastructure. Decentralized Content Delivery — Devices that have cached content (maps, articles, firmware updates) register as content providers. Others discover and fetch what they need from the nearest available peer — a CDN that runs on the devices around you. Emergency Services — When infrastructure is down, devices register capabilities (medical supplies, shelter availability, communication relay). Rescue teams discover what’s available in their vicinity. The mesh becomes the network that disaster couldn’t take down. Collaborative Computing — Split a computation across multiple devices. One device registers a “compute” service, others discover it, send work units, and collect results — a mesh-native MapReduce. Event & Venue Services — Devices at a conference, stadium, or festival register services they offer (live polling, file drops, local chat rooms, schedule lookup). Attendees discover and use them without any centralized app server or internet connection.

Discovery Across the Mesh

Discovery queries are not limited to directly connected peers. They propagate through the mesh using the same multi-hop routing as regular messages. This means:
  • A service registered on a device 5 hops away can still be discovered
  • The hop_count field in service_discovered tells you how far away the provider is
  • You can use hop count to prefer closer providers for latency-sensitive operations
  • Multiple providers for the same service ID may respond — your app decides which to use

Event Listeners

MethodReturnsDescription
on(eventType, listener)thisRegister event listener
off(eventType, listener)thisRemove event listener
once(eventType, listener)thisRegister one-time listener
removeAllListeners(eventType?)thisRemove all listeners
You can also listen for all events with protocol.on('all', listener). See the Events Reference for all available event types and their payloads.

Next: Events Reference

See all event types and their payloads.