Skip to main content
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:
Or install it globally:
Node.js 18 or later is required. Keep npm’s optional dependencies enabled: they carry the binary for your platform. See install the CLI.

2. Sign in

Account management uses the developer session from an email code. In an interactive terminal, the CLI asks for the code:
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:
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:
Or create each part yourself:
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 explains the check. Find the values in your project:
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:
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 lists where each one comes from. To attest from Xcode development builds as well, turn them 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:
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 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 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:
--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

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.

6. Allow your web origins

A browser client is accepted from the application’s allowed origins:
Entries are stored as scheme://host[:port]. See 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:
Add the App Attest entitlement to app.json and check the installed versions:
Pass the attestation providers to the provider, so every code request and sign-in carries an attestation:
Rebuild the native app (npx expo run:ios, npx expo run:android or eas build --profile development); App attestation has the details. For a web app, follow OfflineID SDK for React. For local device communication, install the Mesh SDK as described in React Native installation:
The Mesh SDK needs no account for local operation; the App ID and features above apply to its hosted relay and telemetry.

8. Verify

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:

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.

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. A sign-in that answers 402 means the organization’s hosted services are paused for billing; see usage limits. A 503 means a check could not be made; retry shortly.