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

> ## Agent Instructions
> These docs target Mesh SDK v0.30.0. Match the installed package and binding before generating code. Start at /getting-started/agents for task-specific reading paths. Apps that call enableTelemetry on 0.26.0 to 0.29.0 should update to 0.30.0: the telemetry uploader now trusts only the telemetry endpoint's issuing roots.
> Call the company and product Offline Protocol, never Offline alone. Current packages: @offline-protocol/mesh-sdk 0.30.0 (React Native), offline-protocol-sdk 0.30.0 on PyPI (Python 3.10 to 3.13; wheels for macOS 14+ arm64, Linux x86_64 and aarch64 with glibc 2.34+, Windows x86_64; other hosts build from source; extras lan and http add LAN discovery, the HTTP front and offline-protocol-verify), the offline-protocol crates 0.30.0 on crates.io, the OfflineProtocolSDK Swift package 0.30.0 (preview, iOS 13+ only), the Android library com.offlineprotocol:offline-protocol-sdk 0.30.0 on Maven Central (preview, minSdk 24), the Java binding com.offlineprotocol:offline-protocol-java 0.30.0 on Maven Central (JDK 22+, native library from the GitHub release's desktop archives), the C header offline_protocol.h with the native library in the GitHub release's desktop archives, the offline_protocol Flutter plugin (preview, not on pub.dev; a path dependency on the SDK repository at tag v0.30.0), @offline-protocol/id-react 0.2.0, @offline-protocol/id-react-native 0.3.3, @offline-protocol/pol 0.1.2 and @offline-protocol/cli 0.2.8. Canonical docs URLs start with https://www.offlineprotocol.com/docs.
> The Mesh SDK runs in a native app or gateway. A browser OfflineID SDK integration does not provide browser mesh transport. Local mesh operation does not require a portal account or API key. The Mesh SDK appId is a mesh app identifier the developer chooses, not the portal App ID (app_...) that OfflineID and Proof of Location require.
> In v0.30.0 service RPC bodies are sealed in the 1:1 MLS session once the recipient runs 0.29 or later and the session is confirmed; discovery, the first request to a provider never met, and requests to older peers are signed plaintext, so keep secrets out of service bodies unless the event says encrypted: true. The HTTP front (offline-protocol-service --http) always carries calls inside encrypted direct messages but does not authorize callers. Message delivery, durable local acceptance and backend commit are distinct outcomes. Use the workflow guide for the required application logic.
> Offline Protocol CLI 0.2.8 is on npm (@offline-protocol/cli, command offline). Its local MCP server, named offline-protocol, runs with npx -y @offline-protocol/cli@0.2.8 mcp serve and requires no login or key. Hosted MCP is at https://mcp.offlineprotocol.com/mcp with an app API key in Authorization: Bearer and the matching App ID in x-app-id; organization keys are rejected, and Claude Desktop and Claude.ai cannot send these headers; there, use the public read-only endpoint https://mcp.offlineprotocol.com/public/mcp as a custom connector, or local MCP to scaffold or edit projects. Follow /tools/overview for setup and do not invent commands beyond it. MCP provides integration context and planning, not mesh execution; file-writing tools are local only.
> In v0.30.0 phone Wi-Fi peer streams carry data: Android over Wi-Fi Direct (the SDK forms the group with autoAccept on Android 10+) and over the Wi-Fi network it is on (DNS-SD), iOS over Network framework (LAN or AWDL). Android and iOS meet over that slot only on a shared Wi-Fi network that allows multicast; iOS 0.28 and later does not see iOS 0.27 or earlier over it. Otherwise use BLE or a provisioned relay across platforms. The Swift and Android packages are previews whose public names may change; the Android library is on Maven Central from 0.29.0 only, so never give a Gradle line for an earlier version. The receiver core ACKs before application persistence; use application acceptance for durable workflows.
> Proof of Location is Sepolia testnet witness evidence, not zero-knowledge proof or proof of presence. The geohash is public onchain. Read /proof-of-location/security before integration.

# C binding

> Call the Mesh SDK v0.30.0 from C, C++, Go, C# or Zig: a C header over the native library, with JSON parameters, results and events.

From v0.30.0 the SDK has a C API over the same native library the other bindings load. It is the [local API](https://www.offlineprotocol.com/docs/mesh-sdk/local-api.md) called in process: you create an engine from a JSON configuration, call any method of the interface definition by name with JSON parameters, and receive events as JSON. Any language that calls C uses it the same way, including C++, Go through cgo, C# through P/Invoke and Zig.

The header declares about a dozen functions, and it does not change when a method is added to the interface definition, so a wrapper in another language keeps working across releases.

## What you need

| File | Where it comes from |
| - | - |
| `offline_protocol.h` | Every desktop archive on the [v0.30.0 GitHub release](https://github.com/Offline-Protocol/offline-protocol-sdk/releases/tag/v0.30.0), and [`bindings/c/include`](https://github.com/Offline-Protocol/offline-protocol-sdk/tree/v0.30.0/bindings/c/include) in the SDK repository |
| The shared library | The release's desktop archives: `liboffline_protocol_uniffi.so` (Linux x86\_64 and aarch64), `liboffline_protocol_uniffi.dylib` (macOS arm64), `offline_protocol_uniffi.dll` with its import library `offline_protocol_uniffi.dll.lib` (Windows x86\_64) |
| A JSON library | Your own. Every result and event is JSON text |

The release archives are `offline-protocol-0.30.0-linux-x86_64.tar.gz`, `offline-protocol-0.30.0-linux-aarch64.tar.gz`, `offline-protocol-0.30.0-macos-arm64.tar.gz` and `offline-protocol-0.30.0-windows-x86_64.zip`. The iOS XCFramework in the same release carries the same C functions, and the same header applies. The Android libraries do not: the C binding is off in the Android builds.

To build the library yourself, including the static archive the releases do not ship, use Rust 1.91 or newer:

```bash theme={null}
git clone --branch v0.30.0 --depth 1 https://github.com/Offline-Protocol/offline-protocol-sdk.git
cd offline-protocol-sdk
cargo build --release -p offline-protocol-uniffi
# target/release/liboffline_protocol_uniffi.{so,dylib,a}
```

## Quick start

This program creates an engine, sets up storage, starts it and sends a message. It is the SDK's own quick start, which the release compiles and runs against each desktop library before publishing.

```c theme={null}
#include <stdio.h>
#include "offline_protocol.h"

int main(void) {
  OfflineProtocolHandle *engine = NULL;
  OfflineProtocolError err = {0};
  char *result = NULL;

  /* Every ProtocolConfig field without a default in the interface
   * definition; the rest take the definition's defaults. */
  const char *config =
      "{\"app_id\":\"com.example.app\",\"profile\":\"default\","
      "\"ble_enabled\":false,\"wifi_direct_enabled\":false,\"internet_enabled\":true,"
      "\"reticulum_enabled\":false,\"nostr_enabled\":false,\"prefer_online\":false,"
      "\"initial_ttl\":8,\"encryption_enabled\":true,\"auto_key_exchange\":true,"
      "\"store_pending\":true,\"max_pending_per_peer\":64,\"max_pending_global\":4096,"
      "\"pending_ttl_ms\":60000,\"overflow_policy\":\"DropOldest\"}";

  if (offline_protocol_create(config, &engine, &err) != OfflineProtocolStatus_Ok) {
    fprintf(stderr, "create: %s\n", err.message);
    offline_protocol_error_clear(&err);
    return 1;
  }

  /* Storage before start(): the built-in file stores need no callbacks.
   * The store key seals key material at rest; keep it out of the source. */
  if (offline_protocol_call(engine, "initialize_mls_with_file_stores",
          "{\"mls_root\":\"/var/lib/app/mls\",\"state_root\":\"/var/lib/app/state\","
          "\"store_key\":\"<32 bytes, base64>\"}", &result, &err) != OfflineProtocolStatus_Ok) {
    fprintf(stderr, "storage: %s\n", err.message);
    offline_protocol_error_clear(&err);
    offline_protocol_free(engine);
    return 1;
  }
  offline_protocol_string_free(result);

  /* A failed call leaves `result` as it was, so free it only on success. */
  if (offline_protocol_call(engine, "start", NULL, &result, &err) != OfflineProtocolStatus_Ok) {
    fprintf(stderr, "start: %s\n", err.message);
    offline_protocol_error_clear(&err);
    offline_protocol_free(engine);
    return 1;
  }
  offline_protocol_string_free(result);

  /* A failed call writes `err` over whatever it held, so clear it after
   * every failure, including one you otherwise ignore. */
  if (offline_protocol_call(engine, "local_address", NULL, &result, &err) ==
      OfflineProtocolStatus_Ok) {
    printf("this device is %s\n", result); /* "off1..." as a JSON string */
    offline_protocol_string_free(result);
  } else {
    offline_protocol_error_clear(&err);
  }

  /* An engine refusal names the definition's variant and its position;
   * the other statuses are the binding's own (a bad parameter, say). */
  if (offline_protocol_call(engine, "send_message",
          "{\"recipient\":\"off1...\",\"content\":\"hi\",\"priority\":\"Medium\"}",
          &result, &err) == OfflineProtocolStatus_Ok) {
    printf("queued %s\n", result); /* the message id */
    offline_protocol_string_free(result);
  } else {
    fprintf(stderr, "send: %s (%d) %s\n", err.variant ? err.variant : "-", err.code, err.message);
    offline_protocol_error_clear(&err);
  }

  if (offline_protocol_call(engine, "stop", NULL, &result, &err) == OfflineProtocolStatus_Ok) {
    offline_protocol_string_free(result);
  } else {
    fprintf(stderr, "stop: %s\n", err.message);
    offline_protocol_error_clear(&err);
  }
  offline_protocol_free(engine);
  return 0;
}
```

```bash theme={null}
cc -I include app.c -L <libdir> -loffline_protocol_uniffi -Wl,-rpath,<libdir>
```

## Call methods

Method names, parameter names and the JSON form of every type are the local API's, so its [method table](https://github.com/Offline-Protocol/offline-protocol-sdk/blob/v0.30.0/docs/spec/local-api.md#method-table) is the reference for what you can call.

* Engine methods are bare (`send_message`). `MeshServices` methods carry `services.` (`services.register_service`) and `DataStore` methods carry `data.` (`data.create_doc`).
* Parameters are a JSON object keyed by the definition's parameter names. `NULL` means no parameters.
* Bytes are padded standard base64. Enums are spelled as the definition spells them (`"WiFiDirect"`, `"Medium"`). An omitted dictionary field takes the definition's default. An unknown key is refused.
* A method that returns nothing returns `null`.
* An engine refusal sets `err.variant` to the `ProtocolError` variant name and `err.code` to its position, the same number as the local API's `-32000 - n`. See [error codes](https://www.offlineprotocol.com/docs/mesh-sdk/errors.md).

Unlike the local API service, the C binding reaches the transport methods (`ble_`, `internet_`, `wifi_direct_`, `reticulum_`, `nostr_`) and the platform operations such as `send_file_from_path`, because your process is the platform here and owns the radios, sockets and files.

The declarations that take a callback have their own functions: events, the four transport notifications, `initialize_mls` with your own storage providers, and the storage conformance suite. The header documents each.

## Ownership and threads

* Free every `char *` the library returns with `offline_protocol_string_free`, and nothing else with it. Clear an `OfflineProtocolError` with `offline_protocol_error_clear` after every failure.
* The library writes a result only when a call succeeds. Free a result only after `OfflineProtocolStatus_Ok`.
* A callback runs synchronously on whichever thread is inside the engine. It must be safe from any thread, must not call back into the engine and must not unwind. Copy the event, queue it and return.
* Every function that takes a callback also takes a `drop` function and a `ctx`. The library calls `drop(ctx)` exactly once, including when it refuses the registration, so never free `ctx` yourself.
* Every entry point catches a panic and answers `OfflineProtocolStatus_Internal` instead of aborting your process.

## Events

Install a callback with `offline_protocol_set_event_callback`, or poll with `offline_protocol_poll_event`. Every event is queued either way; the queue keeps the newest 1,000. Event payloads are the engine's JSON, the same as the [event reference](https://www.offlineprotocol.com/docs/mesh-sdk/events.md) describes.

## What your host provides

The C binding provides nothing beyond the engine:

* **Transports.** Drain each transport's outbound queue, perform the I/O, report the outcome and inject what you receive. The transport callbacks say when a queue has frames; the Internet transport has none and is polled.
* **Storage.** Two storage providers written in C, or the built-in file stores through `initialize_mls_with_file_stores`. Prove a provider with `offline_protocol_run_storage_conformance`.
* **Lifecycle.** `start`, `stop` and `process` on a timer. For telemetry, the binding fills `os`; pass `os_major` yourself and report backgrounding with `notify_app_state`.
* **One engine per handle, freed once,** with no call in flight when it is.

## Reference

* [C binding README](https://github.com/Offline-Protocol/offline-protocol-sdk/blob/v0.30.0/bindings/c/README.md) and its [consumer check](https://github.com/Offline-Protocol/offline-protocol-sdk/tree/v0.30.0/bindings/c/consumer-check), two complete C programs CI compiles and runs.
* [C binding contract](https://github.com/Offline-Protocol/offline-protocol-sdk/blob/v0.30.0/docs/bridges/c.md): ownership, threading, storage providers and which builds carry the binding.
* [Local API specification](https://github.com/Offline-Protocol/offline-protocol-sdk/blob/v0.30.0/docs/spec/local-api.md): method names, encodings and errors.

The SDK is AGPL-3.0-only with a commercial license available; see [licensing](https://www.offlineprotocol.com/docs/operations/licensing.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.