Skip to main content

Types & Internals

Naming conventions

The SDK uses two casing conventions, and the split is deliberate: The only camelCase fields on events are type and the optional seenAt. The clearest example of the pair is MediaMetadata (camelCase, input) versus MediaMetadataEvent (snake_case, output). ForwardInfo and ReplyContext are the exception: they are snake_case on both sides, as they are passed verbatim into params and re-emitted on events.

Core Enums

ProtocolState members hold strings. They were numeric (0/1/2) through v0.19.0, so state === ProtocolState.Running, which could never be true before, now works. If your app persisted a ProtocolState value itself, the old number now matches nothing; treat an unrecognized persisted value as Stopped.

Message Types

ForwardInfo

ReplyContext

Renders a reply preview without a local copy of the original message.
ForwardInfo and ReplyContext are display-level hints copied by the sending client, not cryptographic proofs. Do not rely on either for access-control or security decisions.

Media Types

MediaMetadata (input, camelCase)

MediaMetadataEvent (output, snake_case)

Same information as carried on received events. All fields are optional.
encryptionKey/encryption_key and iv are secret material. The SDK strips them from every cleartext wire frame and redacts them from telemetry, so they only ever arrive via the end-to-end-sealed media envelope.

FileProgress

Network Types

Routing Types

MLS Types

GroupRichReadiness is point-in-time and advisory: capability knowledge changes with key package exchanges and restarts, and the send path re-evaluates the gate itself. Use it to warn before sending (graying out an attachment button) rather than reacting to group_rich_extras_dropped after the fact.

Diagnostics & Telemetry Types

New telemetry variants land on { category: 'extension' } at older client builds, so handle that case rather than assuming exhaustiveness.
Several MetricsFrame counters are u64 in Rust but cross the bridge as JavaScript numbers (f64, 53-bit mantissa). Values above 2^53 silently lose precision, so treat any single value above roughly 9 PB as approximate.

Constants

DORS (Dynamic Offline Relay Switch)

DORS automatically selects the optimal transport across BLE, Wi-Fi Direct, Internet, Reticulum, and Nostr based on real-time conditions.

Scoring Factors

Transport Weights

BLE: optimized for energy efficiency and mesh scenarios Signal 30%, Energy 30%, Congestion 15%, Proximity 15% Wi-Fi Direct: optimized for high throughput Bandwidth 35%, Proximity 20%, Congestion 20%, Reliability 15% Internet: optimized for server connectivity Bandwidth 35%, Reliability 30%, Congestion 15%, Energy 10%

Switching Safeguards

Mesh Networking

Cluster Architecture

Devices organize into clusters of nearby connected peers. Connections between clusters are handled by bridge connections.
  • MEMBER: intra-cluster connection
  • BRIDGE: inter-cluster connection

How It Works

  1. Discovery: devices broadcast BLE advertisements with mesh metadata (degree, free slots, battery, uptime)
  2. Cluster detection: each device computes a cluster signature from connected peer hashes
  3. Connection decisions: the MeshController prioritizes bridging different clusters
  4. Rebalancing: lower-quality peers are periodically swapped for better candidates
  5. Delivery: messages are handed onward to a bounded set of neighbors until they arrive or run out of hops

Connection Budget

  • Default: 4 connections per device
  • Minimum: 1 connection maintained
  • Connections are scored and rebalanced roughly every 15 seconds
  • Bridge candidates get priority when clusters need unifying

Peer Scoring

Candidates from a different cluster get a score bonus to encourage network unification.

Message TTL

Default 8 hops. Messages are dropped when TTL reaches 0, which prevents infinite circulation. Every device also caps how much it forwards, per second overall and per neighbor, so a crowded room stays usable rather than filling with repeated copies.

Reliability Layer

Acknowledgments

Messages require an ACK for delivery confirmation, and message_delivered fires on receipt.

Retry Queue

Failed messages are retried with exponential backoff. Each attempt emits message_retrying with the scheduled time.

Message lifecycle states

Only two events are terminal. Everything else is a status update.

Deduplication

  • Bloom filter mode: space-efficient, ~1% false positive rate
  • HashMap mode: exact tracking, configurable capacity

Troubleshooting

Messages not delivering

  1. Verify both devices have the protocol started
  2. Check they are within BLE range (~10–30 m)
  3. Confirm recipient is a peer’s off1… address, not a username or profile string
  4. Ensure TTL is sufficient for the network size
  5. Watch message_retrying and message_undeliverable rather than treating either as failure
  6. Check getEstablishmentState(peerId) if encryption is required

No peers discovered

  1. Verify Bluetooth is enabled: await protocol.isBluetoothEnabled()
  2. Check runtime permissions were granted before start()
  3. Ensure background modes are declared (iOS)
  4. Verify devices are within range

Secure session not establishing

  1. Ensure encryption is enabled (it is by default)
  2. Check getEstablishmentState(peerId) for the current state
  3. Verify hasPendingKeyPackage(peerId) returns true
  4. Watch for secure_session_failed, but note it no longer implies the session is gone
  5. Watch for security_warning with SENDER_ADDRESS_MISMATCH, which indicates impersonation rather than a re-keyed peer

Sends fail with an encryption error

requireEncryption defaults to true, so a send that cannot be encrypted fails rather than falling back to plaintext. Confirm MLS initialized (isMlsInitialized()) and that a session exists with the recipient. Opt out explicitly only if you intend plaintext operation.

Degraded BLE behavior

Sample getBleDiagnostics() over time. Rising recipientNotAmongPeers or fragmentFallbacks counts indicate frames taking a degraded path. They still send, so they never surface as delivery failures.

Frequent disconnections

  1. Check signal strength via neighbor_discovered RSSI
  2. Increase stabilityWindowSecs in DORS config
  3. Check for BLE interference

High battery drain

  1. Verify DORS is selecting BLE over Wi-Fi Direct
  2. Check for excessive retry activity
  3. Use setBatteryLevel() to inform mesh decisions
  4. Consider relayPriority: 'never' on constrained devices

Transport not switching

  1. Verify the transport is enabled in config
  2. Check the hysteresis threshold is not too high
  3. Ensure the cooldown period has elapsed
  4. Use forceTransport() to test manually

Linking error

See Installation troubleshooting.