> ## 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.31.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 or later: the telemetry uploader trusts only the telemetry endpoint's issuing roots from 0.30.0.
> Call the company and product Offline Protocol, never Offline alone. Current packages: @offline-protocol/mesh-sdk 0.31.0 (React Native), offline-protocol-sdk 0.31.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.31.0 on crates.io, the OfflineProtocolSDK Swift package 0.31.0 (preview, iOS 13+ only), the Android library com.offlineprotocol:offline-protocol-sdk 0.31.0 on Maven Central (preview, minSdk 24), the Java binding com.offlineprotocol:offline-protocol-java 0.31.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 Ruby binding offline_protocol 0.31.0 on RubyGems (Ruby 3.3+, native library from the GitHub release's desktop archives), the browser binding (experimental; the offline-protocol-web crate 0.31.0 on crates.io, with the JavaScript in bindings/web built from the SDK repository, not on npm), the offline_protocol Flutter plugin (preview, not on pub.dev; a path dependency on the SDK repository at tag v0.31.0; MeshSdk carries the React Native module's native surface), the service container image ghcr.io/offline-protocol/offline-protocol-service 0.31.0 (linux/amd64 and linux/arm64), @offline-protocol/id-react 0.2.0, @offline-protocol/id-react-native 0.4.0, @offline-protocol/pol 0.1.2 and @offline-protocol/cli 0.2.9 (account setup commands such as offline apps status, offline apps native and offline apply from 0.3.0). Canonical docs URLs start with https://www.offlineprotocol.com/docs.
> The Mesh SDK runs in a native app or gateway, and from 0.31.0 in a browser through the experimental browser binding (a full node in a Web Worker; see /mesh-sdk/web). The OfflineID SDK for React 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.31.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.9 is on npm (@offline-protocol/cli, command offline). Its local MCP server, named offline-protocol, runs with npx -y @offline-protocol/cli@0.2.9 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.31.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; 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.
> OfflineID App IDs are public. A request naming an application needs a credential: an allowed browser origin, an API key in X-Api-Key from a server, or, from an iOS or Android app, an attestation from a build registered to the application (iOS bundle ID and Team ID, Android package and signing certificate SHA-256). Register native apps with offline apps native add or offline apply, pass attestation providers (createExpoAppIntegrityProviders with @expo/app-integrity) to OfflineAppProvider, sign in on a physical device, and check readiness with offline apps status. Follow /getting-started/agent-quickstart and /id-sdk/app-attestation; error responses list the fixing commands in data.next_steps.
> 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.

# Agent quickstart: set up an app from the terminal

> Set up an Offline Protocol app end to end from a terminal or coding agent: sign in, create the app, register iOS and Android apps, install SDKs and verify.

Every step of Offline Protocol setup runs from the `offline` CLI, and the local MCP server exposes the same steps as tools, so a coding agent can take an app from nothing to a working native sign-in without opening the developer portal. Each command is idempotent and accepts `--json`.

The account commands on this page (`apps status`, `apps native`, `apps origins`, `apps features` and `apply`) and the local MCP account tools need CLI 0.3.0 or later.

## 1. Install the CLI

Run it through npx:

```bash theme={null}
npx -y @offline-protocol/cli@latest --version
```

Or install it globally:

```bash theme={null}
npm install -g @offline-protocol/cli
offline --version
```

Node.js 18 or later is required. Keep npm's optional dependencies enabled: they carry the binary for your platform. See [install the CLI](https://www.offlineprotocol.com/docs/tools/overview.md#install).

## 2. Sign in

Account management uses the developer session from an email code. In an interactive terminal, the CLI asks for the code:

```bash theme={null}
offline login --email you@example.com
```

An agent or another non-interactive terminal signs in in two calls. The first sends the code and exits; the second completes sign-in without a prompt:

```bash theme={null}
offline login --email you@example.com
offline login --email you@example.com --code 123456
```

`offline whoami --json` shows the signed-in developer. In CI, set `OFFLINE_ACCESS_TOKEN` from your secret manager instead.

`offline login` without `--email` opens the portal's approval page and stores an app API key for that terminal. That key serves CI and hosted MCP; it does not manage organizations, applications or keys.

## 3. Create the organization, application and key

One command creates whatever is missing (organization, application, default API key) and saves the IDs for later commands:

```bash theme={null}
offline setup --app-name "Field App" --json
```

Or create each part yourself:

```bash theme={null}
offline orgs create "Example Inc"
offline apps create "Field App"
offline keys create "backend" --product offline_id --app-id app_your_application_id
```

The App ID (`app_...`) is public and goes in your app. An API key is a secret for your servers, telemetry and CI; the CLI prints it once. Native and web clients sign users in without an API key.

From the local MCP server, `create_api_key` with `{ "name": "backend", "product": "offline_id" }` creates the key and writes it as `OFFLINE_API_KEY` to `.env.local` (`envFile` sets another path in the project). The file must be ignored by git, or the tool refuses. It never returns the secret, so the key stays out of the agent's conversation.

## 4. Register your iOS and Android apps

An App ID ships inside every copy of your app, so on its own it proves nothing. Offline Protocol accepts a code request or sign-in from a native app when it carries an attestation from a build registered to your application: an iOS bundle ID with its Apple Team ID, or an Android package with its signing certificate SHA-256. [App attestation](https://www.offlineprotocol.com/docs/id-sdk/app-attestation.md) explains the check.

Find the values in your project:

```bash theme={null}
offline apps native detect --dir .
```

`detect` reads `app.json` or `app.config.json`, `ios/*.xcodeproj`, `android/app/build.gradle` and the project's keystores, and prints the bundle ID, Team ID, package name and certificate fingerprints it finds. Add `--home-debug-keystore` to read `~/.android/debug.keystore`, or `--keystore <path>` for a release keystore (its password in `OFFLINE_KEYSTORE_PASSWORD`). Nothing is changed. Register what it found:

```bash theme={null}
offline apps native add ios --bundle-id com.example.fieldapp --team-id ABCDE12345
offline apps native add android --package com.example.fieldapp \
  --sha256 AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23:45:67:89
```

Repeat `--sha256` for each certificate that signs builds you ship: the Play App Signing key, the upload key and the debug keystore. [Find your signing certificate](https://www.offlineprotocol.com/docs/id-sdk/app-attestation.md#android-package-and-signing-certificate) lists where each one comes from. To attest from Xcode development builds as well, turn them on:

```bash theme={null}
offline apps native dev-builds on
```

`offline apps native list` shows what is registered, and `offline apps native remove ios --bundle-id <id>` or `offline apps native remove android --package <name> [--sha256 <fp>]` removes an entry.

### Simulators, emulators and Expo Go

The iOS Simulator, Android emulators and Expo Go cannot attest. To sign in from them while you develop, turn on development mode for 1 to 72 hours:

```bash theme={null}
offline apps native dev-mode on --hours 24
```

Email sign-ins then work without an attestation, up to 200 an hour. Development mode ends on its own, and the sessions it issued stop working when it does; `offline apps native dev-mode off` ends it earlier. An application marked production (`offline apps native production on`) cannot use it, so test against a separate development application. [Development mode](https://www.offlineprotocol.com/docs/id-sdk/app-attestation.md#development-mode-for-simulators-emulators-and-expo-go) covers the details and the change history (`offline apps native history`).

Android phones without full hardware attestation, such as phones with an unlocked bootloader, sign in with `degraded` trust by default. `offline apps native degraded refuse` limits Android sign-ins to `hardware` trust; [Android devices without full attestation](https://www.offlineprotocol.com/docs/id-sdk/app-attestation.md#android-devices-without-full-attestation) explains the trade-off.

### Keep the registration in the repository

To keep the setup in the repository, describe it in the `app` block of `offline.config.json` and converge the application to it:

```json theme={null}
{
  "$schema": "https://dev.offlineprotocol.com/schemas/offline.config.json",
  "appId": "app_your_application_id",
  "app": {
    "native": {
      "ios": [{ "bundleId": "com.example.fieldapp", "teamId": "ABCDE12345" }],
      "android": [{ "package": "com.example.fieldapp", "sha256": ["AB:CD:EF:..."] }],
      "allowDevelopmentBuilds": false
    },
    "origins": ["https://fieldapp.example.com"],
    "features": { "telemetry": true, "relay": false, "proofOfLocation": false }
  }
}
```

```bash theme={null}
offline apply --detect --dry-run
offline apply --detect
```

`--detect` fills in native apps found in the project, `--dry-run` prints the changes without making them, and `--file` reads another path. `apply` only adds: it never removes an identity, origin or feature that the file leaves out.

In the portal, the same settings are on the application's **Native apps**, **Domains** and product tabs.

## 5. Turn on hosted features

```bash theme={null}
offline apps features list
offline apps features enable telemetry relay
```

The features are `telemetry` (Mesh SDK telemetry upload), `relay` (the hosted Mesh SDK relay) and `proof-of-location`. Local mesh communication needs none of them. Telemetry also needs a Mesh SDK key; see [enable SDK telemetry](https://www.offlineprotocol.com/docs/getting-started/get-api-key.md#enable-sdk-telemetry).

## 6. Allow your web origins

A browser client is accepted from the application's allowed origins:

```bash theme={null}
offline apps origins add https://fieldapp.example.com http://localhost:3000
offline apps origins list
```

Entries are stored as `scheme://host[:port]`. See [allowed web origins](https://www.offlineprotocol.com/docs/getting-started/sdk-setup.md#allowed-web-origins).

## 7. Install the SDKs

For OfflineID sign-in in an Expo or React Native app, install the SDK with `@expo/app-integrity`, which provides App Attest on iOS and hardware key attestation on Android:

```bash theme={null}
npx expo install @offline-protocol/id-react-native@0.4.0 @expo/app-integrity @offline-protocol/core @react-native-async-storage/async-storage@^2.2.0 react-native-svg axios
```

Add the App Attest entitlement to `app.json` and check the installed versions:

```bash theme={null}
offline apps native setup-project --write
```

Pass the attestation providers to the provider, so every code request and sign-in carries an attestation:

```tsx theme={null}
import type { ReactNode } from "react";
import { OfflineAppProvider, createExpoAppIntegrityProviders } from "@offline-protocol/id-react-native";

// Loads @expo/app-integrity on first use; in Expo Go the providers report that the device cannot attest.
const attestation = createExpoAppIntegrityProviders(() => require("@expo/app-integrity"));

export default function Root({ children }: { children: ReactNode }) {
  return (
    <OfflineAppProvider appId="app_your_application_id" attestation={attestation}>
      {children}
    </OfflineAppProvider>
  );
}
```

Rebuild the native app (`npx expo run:ios`, `npx expo run:android` or `eas build --profile development`); [App attestation](https://www.offlineprotocol.com/docs/id-sdk/app-attestation.md#expo-and-react-native) has the details. For a web app, follow [OfflineID SDK for React](https://www.offlineprotocol.com/docs/id-sdk/setup-react.md). For local device communication, install the Mesh SDK as described in [React Native installation](https://www.offlineprotocol.com/docs/mesh-sdk/installation-rn.md):

```bash theme={null}
npm install @offline-protocol/mesh-sdk@0.31.0
```

The Mesh SDK needs no account for local operation; the App ID and features above apply to its hosted relay and telemetry.

## 8. Verify

```bash theme={null}
offline apps status
offline apps status --json
```

For each platform, `status` shows what is registered, what is missing, what the SDK must send and the next commands to run. It also shows development mode and when it ends, the production mark and the Android policy for devices without full attestation (in `--json`: `development` with `active`, `until`, `available` and `command`, `production` and `androidDegraded`). `--detect` also compares the registration with the native apps the project ships. The application is ready when:

* every iOS and Android app you ship is listed with its bundle ID and Team ID, or package and certificate fingerprints, and nothing is listed as missing;
* each web origin your site uses is listed;
* the features you use are on.

Settings reach the sign-in service within about a minute. Then sign in from a signed build on a physical device. With `onStatus` on the attestation providers, the request reports `verified`, and the sign-in response carries `data.attestation.status: "verified"` with `trust: "hardware"`. A simulator, emulator or Expo Go sign-in in development mode carries `trust: "development"`.

## Local MCP tools

`offline mcp serve` (or `npx -y @offline-protocol/cli@latest mcp serve`) runs the local MCP server over stdio. Account tools use the developer session from `offline login --email`, so sign in first. Each tool mirrors a command above:

| Step | MCP tool | CLI command |
| - | - | - |
| List or create applications | `list_applications`, `create_application` | `offline apps list`, `offline apps create` |
| Create an API key, with the secret written to a gitignored `.env.local` | `create_api_key` | `offline keys create` |
| Check what is registered and missing | `app_status` | `offline apps status` |
| Find bundle IDs, Team IDs, packages and fingerprints in the project | `detect_native_apps` | `offline apps native detect` |
| Register an iOS or Android app | `register_native_app` | `offline apps native add` |
| Remove a registered app or fingerprint | `remove_native_app` | `offline apps native remove` |
| Accept Xcode development builds | `set_development_builds` | `offline apps native dev-builds` |
| Add the App Attest entitlement and check SDK versions in an Expo project | `configure_native_project` | `offline apps native setup-project` |
| Turn development mode on or off | `set_development_mode` | `offline apps native dev-mode` |
| Mark the application as production | `set_production` | `offline apps native production` |
| Accept or refuse Android devices without full attestation | `set_android_degraded` | `offline apps native degraded` |
| List development mode, production and Android policy changes | `attestation_history` | `offline apps native history` |
| Set allowed web origins | `set_allowed_origins` | `offline apps origins add` / `remove` |
| Turn hosted features on or off | `set_features` | `offline apps features enable` / `disable` |
| Converge the application to `offline.config.json` | `apply_project_setup` | `offline apply` |

## Hosted MCP

The hosted server at `mcp.offlineprotocol.com` reads an application's setup; it does not change it.

* On the keyed endpoint (`https://mcp.offlineprotocol.com/mcp`, with an app API key and `x-app-id`), `app_status` reports what is registered and missing for the key's application.
* On the public endpoint (`https://mcp.offlineprotocol.com/public/mcp`), when the setup tools are enabled on the server, an agent without an API key signs in through the portal. `start_setup_login` returns a `verificationUrl`, a `userCode`, a `loginHandle` and `expiresIn`; you confirm the code in the portal, on the same page as `offline login`. The agent then calls `finish_setup_login` with the `loginHandle` until `status` is `approved` (or `expired`), and receives a `credential` and the `appId`. `app_status` with that `credential` reports the same status. The credential is read-only, lasts 24 hours and works only with the hosted MCP server.

Registering native apps, origins, features, development mode and every other change needs the CLI or the local MCP server (`npx -y @offline-protocol/cli mcp serve`). To connect a client, see [coding agents](https://www.offlineprotocol.com/docs/tools/coding-agents.md).

## Troubleshooting

Error responses name what is missing in `data.missing`, list the commands that fix it in `data.next_steps` and link a guide in `data.docs`. The gateway puts the code in the top-level `error` string; the sign-in service puts it in `error.code`. Read both.

| Code | HTTP | Meaning | Run |
| - | - | - | - |
| `app_credentials_required`, `missing: "native_registration"` | 401 | The request names your App ID without a credential, and the application has no native app registered. | `offline apps native detect`, then `offline apps native add ios --bundle-id <bundle id> --team-id <Team ID>` and `offline apps native add android --package <package> --sha256 <fingerprint>`, or `offline apply --detect`. |
| `app_credentials_required`, `missing: "attestation"` | 401 | Native apps are registered, but this request carries no attestation. | Pass attestation providers to `OfflineAppProvider` (id-react-native 0.4.0 or later), then `offline apps status`. |
| `app_credentials_required`, `missing: "credential"` | 401 | The request has no allowed origin, attested token or API key. | `offline apps origins add <origin>` for a website; send an API key in `X-Api-Key` from a server (`offline keys create`). |
| `invalid_app` | 401 | The App ID does not exist or the application is disabled. | `offline apps list`, or `offline apps create "My App"`. |
| `origin_not_allowed` | 403 | The browser origin is not on the application's list. | `offline apps origins add <origin>` |
| `attestation_required` | 403 | The sign-in or code request had no attestation, or its attestation did not verify. | `offline apps status`; pass attestation providers and sign in from a real device. |
| `attestation_failed` | 403 | The attestation did not verify. `data.reason` names the check, for example `signing_certificate_mismatch`, `app_not_registered` or `development_not_allowed`. | `offline apps native detect` and compare with `offline apps native list`; add the missing fingerprint or bundle; `offline apps native dev-builds on` for Xcode builds. |
| `attestation_key_unknown` | 403 | The server does not know this install's App Attest key. | Nothing: the SDK generates and registers a new key, then retries once. |
| `attestation_not_configured` | 403 | The request carried an attestation, but the application has no native app to check it against. | `offline apps native add ...` or `offline apply --detect` |
| `development_mode_required` | 403 | The device cannot attest (simulator, emulator or Expo Go) and development mode is off. | `offline apps native dev-mode on --hours 24`, or a development build on a physical device. For an application marked production, use a separate development application. |
| `app_credentials_required`, `missing: "development_mode"` | 401 | A session from development mode, used after development mode ended. | Sign in again from an attested build, or `offline apps native dev-mode on --hours 24`. |
| `development_build_required` | none (SDK) | The SDK's providers found no `@expo/app-integrity` in the build (Expo Go, or a build made before installing it), and the server refused the request. | `npx expo install @expo/app-integrity`, then `npx expo run:ios` or `npx expo run:android` (or `eas build --profile development`); or `offline apps native dev-mode on --hours 24`. |
| `code_requests_limited` | 429 | The application reached its hourly cap of email codes requested without an attestation. | Upgrade to id-react-native 0.4.0 and pass attestation providers, so code requests carry an attestation. |
| `development_sign_ins_limited` | 429 | The application reached 200 development-mode sign-ins this hour. | Sign in from an attested build, or wait for the next hour. |
| `degraded_sign_ins_limited` | 429 | Android sign-ins with `degraded` trust reached the hourly cap for the application (500) or for this client (30). | Wait for the next hour. |

A sign-in that answers 402 means the organization's hosted services are paused for billing; see [usage limits](https://www.offlineprotocol.com/docs/operations/licensing.md#usage-warnings-billing-limits-and-pauses). A 503 means a check could not be made; retry shortly.


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