> ## Documentation Index
> Fetch the complete documentation index at: https://www.offlineprotocol.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Reticulum & Nostr

> Long-range LoRa mesh via Reticulum and censorship-resistant relay routing via Nostr, the two opt-in transports.

# 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](https://reticulum.network/) 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

| Scenario                    | Why Reticulum                                                |
| --------------------------- | ------------------------------------------------------------ |
| Off-grid or wilderness      | LoRa reaches 2–15+ km line-of-sight, far beyond BLE's \~50 m |
| Disaster response           | Works without cell towers, Internet, or power infrastructure |
| Rural or sparse networks    | Bridges gaps where devices are too far apart for BLE mesh    |
| Censorship resistance       | I2P transport option for anonymized routing                  |
| Hardware-constrained setups | RNode devices are inexpensive and self-contained             |

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

```typescript theme={null}
const protocol = new OfflineProtocol({
  appId: 'my-app',
  profile: 'default',
  transports: {
    reticulum: {
      enabled: true,
      daemonAddress: 'localhost:4242',   // default
      autoReconnect: true,               // default
      maxReconnectAttempts: 0,           // default: infinite
    },
  },
});
```

`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](https://nostr.com/) 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

| Scenario                   | Why Nostr                                                                         |
| -------------------------- | --------------------------------------------------------------------------------- |
| Censorship circumvention   | Many independent relays, so blocking one does not take the network down           |
| Cross-network reach        | Works wherever WebSockets work, including over hostile NAT or transparent proxies |
| Lightweight infrastructure | No daemon, no LoRa hardware, no custom server, just a relay URL                   |

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

```typescript theme={null}
const protocol = new OfflineProtocol({
  appId: 'my-app',
  profile: 'default',
  transports: {
    nostr: {
      enabled: true,
      relayUrls: ['wss://relay.damus.io'],
      connectionTimeout: 30,       // seconds
      autoReconnect: true,
      reconnectDelay: 1000,        // ms
      maxReconnectAttempts: 0,     // infinite
      sealingEnabled: true,        // default
      coldContactEnabled: true,    // default
    },
  },
});
```

<Warning>
  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.
</Warning>

### Sealing

Outgoing frames are sealed into [NIP-59](https://github.com/nostr-protocol/nips/blob/master/59.md)
gift wraps (kind `1059`, with [NIP-44 v2](https://github.com/nostr-protocol/nips/blob/master/44.md)
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.

<Warning>
  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.
</Warning>

### 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:

```typescript theme={null}
await protocol.enableTransport('reticulum', { enabled: true, daemonAddress: 'localhost:4242' });
await protocol.enableTransport('nostr', { enabled: true, relayUrls: ['wss://relay.damus.io'] });

console.log(await protocol.getActiveTransports());
// ['ble', 'internet', 'nostr']
```

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

<Card title="Back to Configuration" icon="arrow-left" href="/docs/mesh-sdk/configuration">
  Full transport configuration reference.
</Card>
