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

# Types & Internals

> Type definitions, DORS transport scoring, mesh networking architecture, reliability layer, and troubleshooting.

# Types & Internals

## Naming conventions

The SDK uses two casing conventions, and the split is deliberate:

| Direction                     | Casing       | Examples                                                |
| ----------------------------- | ------------ | ------------------------------------------------------- |
| **Event payloads** (output)   | `snake_case` | `message_id`, `peer_id`, `reason_code`, `last_seen_ms`  |
| **Params and config** (input) | `camelCase`  | `SendMessageParams`, `MediaMetadata`, `TelemetryConfig` |

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

```typescript theme={null}
enum MessagePriority {
  Low = 0,
  Medium = 1,
  High = 2,
  Critical = 3,
}

enum ProtocolState {
  Stopped = 'Stopped',
  Running = 'Running',
  Paused = 'Paused',
}

enum ContentType {
  Text = 'text',
  Image = 'image',
  Video = 'video',
  Audio = 'audio',
  VoiceNote = 'voice_note',
  VideoNote = 'video_note',
  File = 'file',
  FileChunk = 'file_chunk',   // internal transport type
  Poll = 'poll',
}

enum NodeRole {
  Normal = 'normal',
  Relay = 'relay',
}

type TransportType = 'ble' | 'internet' | 'wifiDirect' | 'reticulum' | 'nostr';
type RelayPriority = 'never' | 'auto' | 'always';
type OverflowPolicy = 'drop_oldest' | 'drop_newest';
type PresenceStatus = 'online' | 'away' | 'offline';
type PresenceSource = 'internet' | 'peer';
type RelaySyncState = 'synced' | 'pending' | 'unsynced';
type EstablishmentState =
  | 'NoKeyPackage' | 'HaveKeyPackage' | 'SessionPending' | 'SessionConfirmed';
```

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

## Message Types

### ForwardInfo

```typescript theme={null}
interface ForwardInfo {
  original_sender: string;
  original_message_id: string;
  original_timestamp: number;
  forward_count: number;
}
```

### ReplyContext

Renders a reply preview without a local copy of the original message.

```typescript theme={null}
interface ReplyContext {
  sender: string;
  text: string;
  timestamp?: number;
  reply_media_label?: string;
  reply_content_type?: string;
}
```

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

## Media Types

### MediaMetadata (input, camelCase)

```typescript theme={null}
interface MediaMetadata {
  mimeType: string;            // required
  fileName: string;            // required
  fileSize: number;            // required
  durationMs?: number;
  width?: number;
  height?: number;
  thumbnailBase64?: string;    // < 2 KB
  mediaId?: string;
  downloadUrl?: string;
  thumbnailUrl?: string;
  encryptionKey?: string;      // secret
  iv?: string;                 // secret
  ciphertextHash?: string;
  stickerProvider?: string;
  stickerRemoteId?: string;
  stickerKind?: string;
}
```

### MediaMetadataEvent (output, snake\_case)

Same information as carried on received events. All fields are optional.

```typescript theme={null}
interface MediaMetadataEvent {
  mime_type?: string;
  file_name?: string;
  file_size?: number;
  duration_ms?: number;
  width?: number;
  height?: number;
  thumbnail_base64?: string;
  media_id?: string;
  download_url?: string;
  thumbnail_url?: string;
  encryption_key?: string;     // secret
  iv?: string;                 // secret
  ciphertext_hash?: string;
  sticker_provider?: string;
  sticker_remote_id?: string;
  sticker_kind?: string;
}
```

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

### FileProgress

```typescript theme={null}
interface FileProgress {
  file_id: string;
  chunks_sent: number;
  total_chunks: number;
  percentage: number;
}
```

## Network Types

```typescript theme={null}
interface NetworkTopology {
  timestamp: number;
  local_user_id: string;
  nodes: NetworkNode[];
  links: NetworkLink[];
  stats: NetworkStats;
}

interface NetworkNode {
  user_id: string;
  role: NodeRole;
  connection_count: number;
  battery_level?: number;
  last_seen: number;
  transports: TransportType[];
}

interface NetworkLink {
  from: string;
  to: string;
  quality: number;          // 0.0 - 1.0
  transport: TransportType;
  rssi?: number;
}

interface NetworkStats {
  total_nodes: number;
  relay_nodes: number;
  total_connections: number;
  avg_link_quality: number;
  network_diameter?: number;
}

interface MessageDeliveryStats {
  message_id: string;
  sender: string;
  recipient: string;
  sent_at: number;
  delivered_at?: number;
  hop_count: number;
  transport?: TransportType;
  retry_count: number;
  latency_ms?: number;
}

interface DedupStats {
  totalTracked: number;
  recentTracked: number;
  capacityUsedPercent: number;
  mode: string;             // 'HashMap' or 'BloomFilter'
}
```

## Routing Types

```typescript theme={null}
interface RouteEntry {
  nextHop: string;
  hopCount: number;
  quality: number;      // 0.0 - 1.0
  lastSeenMs: number;
}

interface RoutingStats {
  destinationCount: number;
  routeCount: number;
}

interface GradientRoutingConfig {
  maxRoutesPerDestination?: number;
  routeTtlSecs?: number;
  maxRoutingTableSize?: number;
}
```

## MLS Types

```typescript theme={null}
interface MlsKeyPackage {
  packageId: string;
  userId: string;
  keyPackageData: number[];
  createdAt: number;
  isSynced: boolean;
}

interface MlsEncryptedMessage {
  groupId: string;          // for 1:1 this is "session:userId"
  messageType: string;      // 'Application' | 'Proposal' | 'Commit'
  epoch: number;
  ciphertext: number[];
  senderId: string;
  timestampMs: number;
}

interface MlsWelcome {
  groupId: string;
  welcomeData: number[];
  inviterId: string;
  timestampMs: number;
}

interface MlsSessionInfo {
  otherUserId: string;
  groupId: string;
  epoch: number;
  createdAt: number;
}

interface MlsGroupInfo {
  groupId: string;
  groupName: string;
  memberIds: string[];
  epoch: number;
  createdAt: number;
}

interface MlsCommit {
  groupId: string;
  commitData: number[];
  newEpoch: number;
}

interface GroupRichReadiness {
  ready: boolean;
  unknownMembers: string[];
}
```

`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

```typescript theme={null}
interface BleDiagnostics {
  fragmentFallbacks: number;
  recipientNotAmongPeers: number;
  undersizedMtuReports: number;
}

interface TransportMetrics {
  packetsSent: number;
  packetsReceived: number;
  bytesSent: number;
  bytesReceived: number;
  errorRate: number;
  avgLatencyMs: number;
  rssi?: number;
  bandwidthBps?: number;
  congestion?: number;
  queueDepth?: number;
  batteryLevel?: number;
  isCharging?: boolean;
  relayConnectionCount?: number;
  isActiveRelay?: boolean;
  deliveryRatio?: number;
  dropRate?: number;
  averageHopCount?: number;
  energyCost?: number;
}

type TelemetryRecord =
  | { category: 'protocol'; eventJson: string }
  | { category: 'mls'; eventJson: string }
  | { category: 'metricsFrame'; frame: MetricsFrame }
  | { category: 'transportState'; event: TransportStateTelemetryEvent }
  | { category: 'routingDecision'; decision: RoutingDecision }
  | { category: 'deviceCapability'; snapshot: DeviceCapabilitySnapshot }
  | { category: 'extension'; name: string; payloadJson: string };
```

New telemetry variants land on `{ category: 'extension' }` at older client builds, so handle
that case rather than assuming exhaustiveness.

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

## Constants

```typescript theme={null}
const PROTOCOL_START_DELAY_MS = 100;
const MAX_EVENT_HISTORY = 200;
const ONE_SHOT_EVENT_TYPES = ['internet_session_superseded', 'mesh_stopped_by_user'];
const MESH_WAKE_TASK_KEY = 'OfflineProtocolMeshWake';
const LINKING_ERROR: string;
```

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

| Factor          | Description                          |
| --------------- | ------------------------------------ |
| Signal Strength | RSSI for BLE/Wi-Fi (-50 to -100 dBm) |
| Proximity       | Hop count to destination             |
| Bandwidth       | Transport throughput capability      |
| Congestion      | Queue depth and backlog              |
| Energy          | Battery impact of the transport      |
| Reliability     | Historical delivery success rate     |
| Load            | Current processing capacity          |

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

| Safeguard        | Default    | Description                                  |
| ---------------- | ---------- | -------------------------------------------- |
| Hysteresis       | 15 points  | Minimum score improvement required to switch |
| Cooldown         | 20 seconds | Wait time between switches                   |
| Stability Window | 8 seconds  | Transport must be stable before switching    |

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

| Factor       | Weight | Description                       |
| ------------ | ------ | --------------------------------- |
| RSSI         | 35%    | Signal strength to peer           |
| Availability | 20%    | Free connection slots             |
| Uptime       | 15%    | How long the peer has been active |
| Battery      | 15%    | The peer's battery level          |
| Stability    | 10%    | Connection reliability history    |
| Load         | 5%     | Current processing load           |

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.

| Event                   | Terminal? | Meaning                                         |
| ----------------------- | --------- | ----------------------------------------------- |
| `message_delivered`     | **Yes**   | Recipient ACKed                                 |
| `message_failed`        | **Yes**   | Gave up                                         |
| `message_sent`          | No        | Queued and handed to a transport                |
| `message_deferred`      | No        | No transport available; persisted to the outbox |
| `message_retrying`      | No        | Retry scheduled                                 |
| `message_undeliverable` | No        | Recipient offline; parked, and this **repeats** |

### 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](/docs/mesh-sdk/installation-rn#troubleshooting).
