SDKs and platforms

How do I use Offline Protocol from Python?

Install offline-protocol-sdk from PyPI with pip and import it as offline_protocol_sdk. Prebuilt wheels cover macOS 14+ on Apple silicon, Linux x86_64 and aarch64 with glibc 2.34 or later, and Windows x86_64. A ProtocolManager runs a full mesh peer with MLS encryption over Bluetooth LE, TCP peer streams on a local network, an internet relay or a gateway daemon, and the package's offline-protocol-service command shares one engine with other local programs over JSON-RPC or plain HTTP.

TL;DR

pip install offline-protocol-sdk installs prebuilt wheels for Apple silicon macOS, glibc 2.34+ Linux and Windows x86_64. A ProtocolManager runs a full encrypted peer over Bluetooth LE, LAN peer streams, a relay or a gateway daemon. The offline-protocol-service command shares one engine with other local programs, and its HTTP front lets them call services on other devices.

Learning objectives

After reading this article you will be able to:

  • Install offline-protocol-sdk on a supported host, with the lan and http extras when needed
  • Choose the Python transports and storage for a desktop, gateway or headless server
  • Run offline-protocol-service to share one engine over the local API or the HTTP front

offline-protocol-sdk on PyPI

The Python binding is offline-protocol-sdk on PyPI, imported as offline_protocol_sdk. Its package metadata marks it beta, it requires Python 3.10 or later, and it lists Python 3.10 to 3.13. It runs the same Rust protocol engine as the phone SDKs, so a Python process is a full peer: it has its own address, encrypts with MLS (RFC 9420), relays for others and keeps delivery state across restarts.

The usual uses are a desktop tool, a gateway or a site computer that exchanges messages and shared state with phones. If you are still choosing a Python library for peer-to-peer networking in general, Which Python libraries do peer-to-peer and mesh networking? compares the options by layer. The package metadata names AGPL-3.0-only, and a commercial licence is available.

Python wheels and supported hosts

Python wheels on PyPI carry the native library, so on a supported host there is nothing to compile:

HostWheel tagRequires
macOS on Apple silicon (arm64)macosx_14_0_arm64macOS 14 or later
Linux x86_64manylinux_2_34_x86_64glibc 2.34 or later
Linux aarch64manylinux_2_34_aarch64glibc 2.34 or later
Windows x86_64win_amd64

The docs translate glibc 2.34 as Debian 12, Ubuntu 22.04, RHEL 9 or newer. There is no wheel for Intel macOS, musl-based Linux such as Alpine, or an older glibc, and no source distribution, so pip reports no matching distribution there. On those hosts, clone the SDK at a release tag and build the native library and the bindings together with the Rust toolchain (1.91 or newer), and never mix bindings from one revision with a library from another.

lan and http extras and the installed commands

The base install is enough to run a peer from your own Python code:

pip install offline-protocol-sdk

A host that runs the service, such as a gateway or a site computer, usually also wants the two extras. lan adds DNS-SD discovery for peer streams through python-zeroconf, and http adds the HTTP front through aiohttp. Both are imported only when used.

pip install 'offline-protocol-sdk[lan,http]'

Every install puts three commands on the path:

CommandWhat it does
offline-protocol-serviceServes one engine to local applications over the local API; --lan needs the lan extra and --http the http extra
offline-protocol-http-frontRuns the HTTP front on its own, against a service already running
offline-protocol-verifyDrives the local service and waits for the events that show a message was held, carried and delivered

Python transports and phone reach

Python transports are built by ProtocolManager from flags in ProtocolConfig, and each one has a different reach:

TransportConfig flagWhat it does in Python
Internet relayinternet_enabledWebSocket client over websockets
Bluetooth LEble_enabledCentral role over Bleak on every platform; peripheral role over Bless on macOS and Linux, not Windows
Peer streamwifi_direct_enabledTCP streams over an existing IP network, to listed host:port peers or hosts found over DNS-SD
Gatewayreticulum_enabledA client for a separately deployed gateway daemon, on local IP
Nostrnostr_enabledA stub callback; apps that drive Nostr themselves replace it

The peer stream takes the wifi_direct slot’s name, but it does not create a Wi-Fi Direct network. It is how phones reach a Python host on a local network: an iPhone on 0.28.0 or later and an Android phone on 0.29.0 or later, with their Wi-Fi transport enabled, find the host on the same Wi-Fi network when it allows multicast. Over Bluetooth LE, the Python peripheral lets a phone start a session with the host, and the docs record it running between an Android phone and Macs and Raspberry Pis.

This is the SDK README’s minimal relay example:

import asyncio
from pathlib import Path

from offline_protocol_sdk import ProtocolManager
from offline_protocol_sdk.offline_protocol import ProtocolConfig, OverflowPolicy

config = ProtocolConfig(
    app_id="my-app",
    profile="alice",
    ble_enabled=False,
    wifi_direct_enabled=False,
    internet_enabled=True,
    reticulum_enabled=False,
    nostr_enabled=False,
    prefer_online=True,
    initial_ttl=3,
    encryption_enabled=True,
    auto_key_exchange=True,
    store_pending=True,
    require_encryption=False,
    max_pending_per_peer=100,
    max_pending_global=1000,
    pending_ttl_ms=86_400_000,  # 24 h (the SDK default)
    overflow_policy=OverflowPolicy.DROP_OLDEST,
)

# The peer's address: its pm.local_address. A username reaches nobody.
peer_address = "off1..."

async def main():
    # The installer must remove this application-owned directory on uninstall.
    state_root = Path("/app/install-owned-data/offline-protocol")
    async with ProtocolManager(
        config,
        event_handler=print,
        state_root=state_root,
    ) as pm:
        pm.internet.configure(server_url="ws://relay.example.com")
        await pm.internet.start()

        msg_id = pm.send_message(peer_address, "Hello from Python!")
        print(f"Sent: {msg_id}")

        # Keep running to receive messages
        await asyncio.sleep(30)

asyncio.run(main())

Python key storage on desktops and servers

Python key storage has two options. On a desktop with a secret service (macOS Keychain, GNOME Keyring or KWallet, Windows Credential Locker), the MLS identity goes into the OS keyring through keyring, and you pass a state_root for the protocol state.

A server, container or headless Linux host usually has no secret service, and there keyring falls back to a backend that cannot hold keys. Pass a store key instead, with store_key_env or store_key, plus separate mls_root and state_root directories, and ProtocolManager uses the SDK’s built-in file stores, sealing every MLS record under that key. The key is 32 random bytes generated once and kept. Losing mls_root gives the device a new address. Can I run Offline Protocol on a server or in Docker? goes further into headless hosts.

Local API service for several applications

The local API lets one process own the engine and serve several applications on the same host over JSON-RPC 2.0 on a WebSocket, so they share one identity, one set of peers and one document store. The offline-protocol-service command is the reference server:

export OFFLINE_PROTOCOL_STORE_KEY="$(openssl rand -hex 32)"   # once; keep it
offline-protocol-service --config config.json \
    --mls-root /var/lib/example/keys --state-root /var/lib/example/state \
    --socket /run/example/api.sock --listen 0.0.0.0:7878 --peer 10.0.0.2:7878

config.json holds the ProtocolConfig fields by name. The service starts every transport the configuration enables: --lan finds other hosts with no peer list, ble_enabled starts both Bluetooth LE roles so phones can find the host, and --relay connects the internet transport. A client opens the Unix socket (owner-only by default), sends hello with its application ID, then calls the engine’s methods and receives its events by name. The local API never binds a non-loopback address, and whoever can open the socket counts as one of the operator’s applications.

HTTP front for programs without the SDK

The HTTP front lets a program that does not import the SDK call a service on another device with an ordinary HTTP request. Start the service with --http 127.0.0.1:8080, register a provider’s local endpoint with a PUT /services/<service>, and a requester on another host sends its request to its own front with a host name of the form <service>.<device address>.offline.protocol.internal. The fronts carry the request and response inside end-to-end encrypted messages, over whichever carrier reaches the other host.

Two numbers shape the design. Bodies are capped at 128 KiB in either direction, and the deadline defaults to 30 seconds. The front does not authorize callers, so the provider decides who it serves, for example by checking the X-Offline-Protocol-Sender header against an allowlist. How can a program call an HTTP service on another device with no internet? covers the pattern in more depth.

Frequently asked questions

What is the import name of the Offline Protocol Python package?

The package is offline-protocol-sdk on PyPI and the import name is offline_protocol_sdk. ProtocolManager is imported from the top level, and ProtocolConfig from offline_protocol_sdk.offline_protocol.

Why does pip find no matching distribution on my machine?

There is no wheel for Intel macOS, musl-based Linux such as Alpine, or glibc older than 2.34, and no source distribution. On those hosts build the native library and bindings from a clone of the SDK repository with the Rust toolchain.

Can a Python host create a Wi-Fi Direct group with Android phones?

No. The Python peer-stream transport uses TCP over an existing IP network. iPhones on 0.28.0 or later and Android phones on 0.29.0 or later reach the host on the same Wi-Fi network when it allows multicast, but Android Wi-Fi Direct groups do not include the host.

Does the Python package work on Windows?

Yes, there is a Windows x86_64 wheel. The Bluetooth LE peripheral role is not available there, because its library is not installed on Windows; the central role, peer streams and the relay client are.

Do programs that call the HTTP front need the SDK installed?

No. They send an ordinary HTTP request to the front on their own host, and the front carries it to the other device inside end-to-end encrypted messages. Only the host running offline-protocol-service needs the package.

Sources

Build it with Offline Protocol

The Python page covers installing from PyPI, choosing between the OS keyring and the built-in file stores, and starting a local peer on a Linux or desktop host.

Read the Python docs