Skip to main content

Reticulum & Nostr

Beyond BLE, Wi-Fi Direct, and Internet, the SDK ships two further transports. Both are disabled by default because both need something the SDK cannot provide on its own: external infrastructure for Reticulum, a relay URL for Nostr. DORS treats them as ordinary transports: it scores them alongside the rest and switches when conditions justify it.

Reticulum

The Reticulum transport provides long-range, resilient mesh networking via the Reticulum network stack. It supports LoRa, TCP, UDP, serial, and I2P, which makes it suited to off-grid communication, disaster recovery, and infrastructure-sparse environments where BLE range is insufficient and the Internet is unavailable.

When to use it

It is not suited to:
  • High-bandwidth transfers such as media and files, since typical LoRa throughput is ~0.7 KB/s, peaking around 2.7 KB/s
  • Low-latency applications, since multi-hop LoRa paths can add seconds
  • Environments where every device is already in BLE range, where BLE is faster and simpler

Configuration

daemonAddress is the TCP address of a Reticulum instance in host:port form. Reticulum requires that external piece: a running Reticulum daemon, an RNode radio, or a network gateway.

Nostr

The Nostr transport routes messages over Nostr relays via WebSockets, providing a censorship-resistant, decentralized fallback when direct mesh and ordinary Internet endpoints are unreachable. Addressing uses a public routing tag deterministically derived from this device’s off1… address, so peers can compute where to send without exchanging keys. Relays simply rebroadcast the signed events to subscribers.

When to use it

It is not suited to:
  • Latency-sensitive workloads, since relay round-trips add tens of milliseconds at minimum
  • Pure offline scenarios, since relays are only reachable with Internet access
  • Hiding that an address is reachable (see below)

Configuration

Nostr requires an MLS identity, because its routing tag is derived from the address the SDK creates at MLS initialization. Enabling Nostr with encryption.enabled: false fails at startup.

Sealing

Outgoing frames are sealed into NIP-59 gift wraps (kind 1059, with NIP-44 v2 inner encryption), each signed by a fresh single-use key. Inside the event itself a relay sees an opaque routing tag, an unlinkable per-event pubkey, a jittered timestamp, and ciphertext: nothing identifying either party.
Setting sealingEnabled: false falls back to a legacy kind-4 event with the entire protocol envelope in cleartext: both addresses, app id, metadata map, content type, and millisecond timestamp, readable by every relay, permanently. It exists only to reach pre-sealing peers. Leave it on.

What a relay can see

Sealing hides the contents of an event, but a relay you actually subscribe on knows more, because your own subscription names your routing tag to it. Because the routing tag is derived from your address, anyone who knows an address can watch that inbox for traffic volume and timing. An address cannot be guessed, being a 160-bit hash of an identity key, so this is limited to people who could already send you traffic. That is a much smaller set than it used to be, but it is not nobody.

Cold contact

coldContactEnabled (default true) publishes MLS key packages to relays and resolves peers’ published packages, which buys cold first contact. The cost is that addressable records sit at this install’s routing tag and refresh unprompted. The contents are sealed, but their existence and refresh timing are visible to every relay. Disable it if that trade is wrong for your deployment.

Enabling at runtime

Both transports can also be enabled after startup:

Platform bridges

The Rust crates are I/O-free protocol engines: they queue, route, encrypt, and select transports, but never open a socket or touch a radio. A platform bridge performs the actual I/O: draining each transport’s outbound queue, performing the send, reporting the outcome, and injecting inbound bytes. The React Native binding ships bridges for all five transports on iOS and Android, so nothing extra is required. If you consume the Rust crates directly, you write the bridge yourself.

Back to Configuration

Full transport configuration reference.