> ## 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.28.0. Match the installed package and binding before generating code. Start at /getting-started/agents for task-specific reading paths.
> Call the company and product Offline Protocol, never Offline alone. Current packages: @offline-protocol/mesh-sdk 0.28.0 (React Native), offline-protocol-sdk 0.28.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), the offline-protocol crates 0.28.0 on crates.io, the OfflineProtocolSDK Swift package 0.28.0 (preview, iOS 13+ only) and Android bindings from the GitHub release zip (preview, not on Maven Central), @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.6. 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.
> Service RPC is signed plaintext in v0.28.0. 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.6 is on npm (@offline-protocol/cli, command offline). Its local MCP server, named offline-protocol, runs with npx -y @offline-protocol/cli@0.2.6 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.28.0 phone Wi-Fi peer streams carry data: Android over Wi-Fi Direct (the SDK forms the group with autoAccept on Android 10+), iOS over Network framework (LAN or AWDL). Android and iOS do not interoperate over that slot, and iOS 0.28 does not see iOS 0.27 or earlier over it; use BLE or a provisioned relay across platforms. The Swift and Android packages are previews whose public names may change; never present a Gradle Maven Central dependency for the Android library. 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.

# Custody for replicated documents

> Custody in Mesh SDK v0.28.0 lets a device hold a neighbour's replicated-document frames for hours while the recipient is away, under quotas you set.

A device that forwards a frame it did not originate holds it for about five seconds. If no neighbour can take it in that time, the frame is dropped. Custody, new in v0.28.0, lets a device hold one class of frame for hours instead and deliver it when the recipient appears. It is off by default, and a device that never enables it, or never meets a custodian, behaves as before.

## What custody carries

Custody v1 carries only sealed document replication frames: deltas, snapshots, version offers and blob-removal reports from the [DataStore](/docs/mesh-sdk/data). Direct messages, media chunks and requests for a snapshot or a blob are never deposited.

1. **The depositor asks.** When a device offers its own replication frame to neighbours because the recipient is out of reach, it adds a one-hop custody request. It can do this only while it retains the plaintext, so a frame offered after a restart carries no request.
2. **Forwarders strip the request.** Every device removes the request from a third-party frame it transmits, so a deposit is always one hop, from the frame's own sender over the link that proved it.
3. **The custodian holds what it could not forward.** A frame with a request is forwarded normally first. Custody starts only when that forward would be abandoned.
4. **The custodian redelivers.** When the recipient appears, the held frame goes straight to it, at most once per neighbour per hold. Meanwhile it can be re-originated toward other neighbours.
5. **A receipt settles nothing.** The custodian may send the depositor a signed receipt. Only the recipient's acknowledgement settles a message, so the depositor keeps its own outbox entry and retries unchanged.

Records are sealed on the custodian's disk and restored at launch. They expire in wall time at the end of the hold.

## Enable it

Custody is applied when the protocol is constructed; there is no runtime update. In React Native:

```typescript theme={null}
import { OfflineProtocol } from '@offline-protocol/mesh-sdk';

export const protocol = new OfflineProtocol({
  appId: 'field-handoff',
  profile: 'default',
  custody: { enabled: true, holdMs: 6 * 60 * 60 * 1000 },
});
```

In Python, pass a `CustodyConfig` as `ProtocolConfig(custody=...)`:

```python theme={null}
from offline_protocol_sdk.offline_protocol import CustodyConfig

custody = CustodyConfig(enabled=True, hold_ms=6 * 60 * 60 * 1000)
```

| Option (React Native) | Default | Meaning |
| - | - | - |
| `enabled` | `false` | Whether this device accepts deposits |
| `holdMs` | 21,600,000 (6 hours) | How long an accepted frame is held. Must be strictly shorter than the outbox lifetime |
| `maxEntriesPerDepositor` | 64 | Held frames one depositor with an established session may have |
| `maxBytesPerDepositor` | 2 MiB | Bytes one such depositor may have |
| `maxEntries` | 512 | Held frames across every depositor |
| `maxBytes` | 16 MiB | Bytes across every depositor |
| `strangerMaxEntries` | 0 | Held frames for a proven peer without a session. Zero refuses such peers; set it together with `strangerMaxBytes` |
| `strangerMaxBytes` | 0 | Bytes for a proven peer without a session |
| `overflowPolicy` | `drop_oldest` | Evict the oldest held frame to admit a new one, or refuse the new one with `drop_newest` |

Omit a field to keep the core default. Python and Rust use the same fields in snake case. The core validates the bounds at construction and rejects, for example, a hold that is not shorter than the outbox lifetime.

## Inspect and erase

`getCustodyStats()` returns what this device is holding and what it has done as a custodian and as a depositor. `held` and `heldBytes` are gauges; the rest are cumulative since start or the last erase, with one counter per refusal reason, so "custody is off" can be told from "nobody asked". `eraseCustody()` drops every held frame and resets the counters. The DataStore `wipeAll()` erases custody as well. In Python the methods are `get_custody_stats()` and `erase_custody()` on `pm.protocol`.

See the [networking API reference](/docs/mesh-sdk/api/networking#getcustodystats) for the exact signatures.

## Risks to accept

A custodian stores other people's ciphertext and the routing metadata around it, on its own disk and battery. The [release threat model](https://github.com/Offline-Protocol/offline-protocol-sdk/blob/v0.28.0/docs/security/threat-model.md) records three residual risks: custody-borne re-key pressure (R19), a custodian retaining routing metadata about third parties (R20) and deposit spam (R21). Keep the stranger tier closed unless you need it, size the quotas for the device's storage, and treat custody as a latency improvement, not a delivery guarantee.

The full contract is the [custody chapter](https://github.com/Offline-Protocol/offline-protocol-sdk/blob/v0.28.0/docs/spec/custody.md).


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