# Offline Protocol CLI (`offline`)
[](https://github.com/Offline-Protocol/CLI/actions/workflows/ci.yml)
[](LICENSE)
[](https://github.com/Offline-Protocol/CLI/releases)
One CLI to build offline-first apps on the Offline Protocol ecosystem — and
the MCP server that makes the whole ecosystem **AI-native**.
**New here?** → [docs/QUICKSTART.md](docs/QUICKSTART.md) (install, first app, MCP with AI agents)
```bash
offline create --prompt "Durable equipment handoff between field engineers, with backend reconciliation" --name handoff --yes
offline create backend-delivery --name telemetry --yes # retained events to a backend
offline create nearby-service --name status --yes # discover and invoke a nearby service
offline login # only for hosted services (Offline ID starters)
offline doctor # validate your environment
offline mcp serve # MCP server for AI agents (stdio)
```
## What's inside
| Crate | Purpose |
|---|---|
| `crates/offline-cli` | The `offline` binary: login, create, init, add, plan, doctor, update, registry, skills, mcp |
| `crates/offline-registry` | Knowledge core: packages, skills, workflows, templates — embedded, fully offline |
| `crates/offline-generator` | Tera-based project scaffolding (used by `create` and MCP `scaffold_project`) |
| `crates/offline-mcp` | Model Context Protocol server: plan, scaffold, and integrate Offline Protocol components |
| `crates/offline-registry/registry/` | Content: packages, skills, workflows, starter templates |
### Workflows & templates
The three entrances for operational apps come first; the planner selects
them from prompts about handoffs, retained events for a backend, or calling a
service on a nearby device. Each starter ships the application logic the SDK
leaves to you (durable records, stable IDs, explicit acceptance, retries,
recovery) with tests, and the first two include a reference backend.
| Workflow | Templates |
|---|---|
| `local-handoff` (share a local workflow) | `react-native-mesh-handoff` |
| `backend-delivery` (deliver retained events to a backend) | `react-native-mesh-event-delivery` |
| `nearby-service` (discover and invoke a nearby service) | `react-native-mesh-service` |
| `custom-p2p-app` | `react-native-mesh-custom-app` |
| `offline-social-network` | `react-native-social`, `nextjs-social`, `react-vite-social` |
| `offline-id-mesh-chat` | `react-native-social` (+ mesh via `enableMesh`) |
| `offline-id-mobile` | `react-native-id-only` |
| `nearby-chat` | `react-native-mesh-app` |
| `group-messaging` | `react-native-mesh-app` |
### Example app scaffolds
Full React Native sample apps from [offline-app-examples](https://github.com/Offline-Protocol/offline-app-examples)
(vendored `packages/mesh`, `packages/ui`, and native android/ios):
| Template | Demonstrates |
|---|---|
| `orderup` | Host-authoritative kitchen POS: waiters send orders over Bluetooth mesh to a kitchen display |
| `stocksync` | Shared inventory counter with host-authoritative sync and optimistic taps |
| `cowrite` | Collaborative plain-text editing with MLS group sync and live cursors |
```bash
offline create orderup --name my-kitchen --dir ./my-kitchen
offline create --template stocksync --name stock-demo -y
offline registry list templates # includes exampleApp metadata
```
Regenerate embedded copies after changing the upstream examples:
```bash
node scripts/sync-example-app-templates.mjs
```
## Install
The installed command is always `offline`.
**npm (recommended):**
```bash
npm install -g @offline-protocol/cli
offline --version
```
**crates.io:** not available yet. The `offline-cli` crate on crates.io is a
placeholder until the first real publish; use npm or build from source.
**From source (developers):**
```bash
git clone https://github.com/Offline-Protocol/CLI.git
cd CLI
cargo install --path crates/offline-cli
offline --version
```
**From GitHub Releases** (archives + `SHA256SUMS`; members of the
Offline-Protocol organization only, because the repository is internal):
1. Download the archive for your platform from
[Offline-Protocol/CLI releases](https://github.com/Offline-Protocol/CLI/releases)
2. Verify with `SHA256SUMS` or the matching `.sha256` file
3. Extract and put `offline` on your `PATH`
4. Run `offline doctor` and `offline update`
Maintainer release process: [docs/RELEASING.md](docs/RELEASING.md).
Homebrew distribution is still planned.
## Command reference
Quick map of top-level commands. Full descriptions, flags, and examples:
**[docs/COMMANDS.md](docs/COMMANDS.md)**.
| Command | Purpose |
|---|---|
| `offline login` / `logout` / `whoami` | Interactive auth (localhost callback + poll); logout revokes the key server-side when possible |
| `offline login --email` | The same login without a browser (SSH, agents). Both ways store the developer session that `setup`, `orgs`, `apps` and `keys` need; on SSH or a machine with no display, `offline login` prints the URL instead of opening a browser |
| `offline login --email <address> --code <code>` | Finish the email sign-in without a prompt (agents, CI). Without a terminal, `offline login --email <address>` sends the code and exits |
| `offline setup` / `orgs` / `apps` / `keys` | Provision and manage organizations, applications and API keys (need a developer session). `setup` reuses the API key this machine already made for the app |
| `offline org use <org>` / `--org <id>` | Set the default organization (asked once at login if you belong to several), or override it for one command |
| `offline apps status` | What the application has set up and what is missing for sign-in on iOS, Android, web and server, with the command that fixes each gap |
| `offline apps native add/remove/list/detect/dev-builds` | Native apps for app attestation: iOS bundle ID + Team ID, Android package + signing-certificate SHA-256; `detect` reads them from app.json, Xcode, Gradle and assetlinks.json |
| `offline apps native dev-mode on --hours N` / `off` | Development mode (1-72 hours, ends by itself): simulators, emulators and Expo Go sign in without attestation, email codes only |
| `offline apps native production on/off`, `degraded allow/refuse`, `history` | Mark the app production (no development mode), the policy for Android phones that cannot fully attest, and the audit history |
| `offline apps native setup-project [--write]` | Expo: add the App Attest entitlement to app.json and print the packages to install |
| `offline apps origins add/remove/list` | Web origins allowed to sign in from a browser |
| `offline apps features enable/disable/list` | Product features: `telemetry`, `relay`, `proof-of-location` |
| `offline apply [--detect] [--dry-run]` | Converge the application to the `app` block of `offline.config.json` (native apps, origins, features); additive and idempotent |
| `offline create` | Scaffold from workflow, template, or `--prompt`; runs `npm install` and starts the dev server (Metro for React Native) unless `--skip-setup` |
| `offline plan` | Plan only (workflow + packages + optional architecture docs) |
| `offline init` | Wire `offline.config.json`, `.mcp.json`, `AGENTS.md` into an existing app |
| `offline add <package>` | Add package (+ transitive requires) to `package.json` |
| `offline doctor` | Toolchain + auth + API reachability checks |
| `offline update` | Check npm for a newer CLI and print the upgrade command |
| `offline registry …` | List / search / show packages, workflows, skills, templates, capabilities; export `llms.txt` |
| `offline skills list` / `show` | List or read an integration skill |
| `offline mcp serve` | Run the MCP server (stdio) |
| `offline mcp install <client>` | Print client config (`cursor`, `claude-desktop`, `vscode`, `claude-code`, `codex`) |
| `offline mcp tools` | List MCP tools |
| `offline completions <shell>` | Shell completions |
`offline login` (browser) stores an application API key, which `create` and
`whoami --verify` use. Local BLE mesh starters (`nearby-chat`,
`group-messaging`, `custom-p2p-app`) scaffold without a login, as
does the local MCP server. A template opts out of the login step with
`"requiresLogin": false` in its `template.json`. `setup`, `orgs`,
`apps` and `keys` call the developer management API and need the developer
session that `offline login` stores; after `offline login --api-key` (CI) only,
they stop with an error that says so. When the API key is shared with the application,
`offline logout` clears this machine but leaves the key active; revoke it in the
developer portal if you no longer need it.
Every informational command supports `--json`. Config: `~/.offline/config.json` (0600).
Overrides: `OFFLINE_API_KEY`, `OFFLINE_API_BASE`, `OFFLINE_ENV` (`prod` or `dev`/`local`),
`OFFLINE_PORTAL_BASE`, `OFFLINE_CONFIG_DIR`, `OFFLINE_REGISTRY_DIR`,
`OFFLINE_CALLBACK_PORT` (pin the localhost login callback port; default: an ephemeral port).
The npm launcher forwards SIGINT, SIGTERM and SIGHUP to the native binary and
exits with its status; `OFFLINE_CLI_BINARY` points the launcher at a locally
built binary.
## What the CLI stores and sends
**Stored on your computer.** `offline login` and `offline login --email` write
`~/.offline/config.json` (or `$OFFLINE_CONFIG_DIR/config.json`): the
application API key, the developer session token from email sign-in, your
email address, developer, organization and application IDs, and any API or
portal base URL you set. The file is plain JSON, not encrypted and not in the
system keychain. On macOS and Linux the CLI creates it owner read and write
only (0600), replaces it atomically, keeps `~/.offline` owner-only (0700) and
refuses a symlinked file or a directory other users can write to; on Windows
it keeps the default permissions of your user profile folder. Anyone who can
read the file can use the key.
**Removed by `offline logout`.** It deletes the file. A key the CLI created
for itself is also revoked on the server; a key shared with the application
stays active until you revoke it in the developer portal. After email
sign-in, logout clears the session token from this computer.
**Sent to Offline Protocol.** Sign-in requests (including this computer's
hostname as the device name, shown when you approve the sign-in), and the
calls each command makes to the developer API at `api.offlineprotocol.com`
with your key or session. The hosted MCP server
(`mcp.offlineprotocol.com`) receives the tool arguments your agent sends and
returns results; it keeps rate-limit counters, keyed by a hash of the
credential or by client address, in memory for about 90 seconds.
**Sent elsewhere.** `offline update` asks the public npm registry
(`registry.npmjs.org`, or `OFFLINE_NPM_REGISTRY`) for the latest
`@offline-protocol/cli` version, only when you run it. Scaffolding and the
local MCP server's `scaffold_project` and `integrate_packages` tools run
`npm install` and similar package-manager commands, which contact the package
registries you have configured. The AI agent or client you connect to the MCP
server receives tool inputs and results, and handles them under its own
terms.
**No telemetry.** The CLI and the local MCP server send no usage analytics
and no crash reports.
**Local MCP tools act with your permissions.** `offline mcp serve` can write
files in your project and run package-manager commands, subject to your
agent client's approval settings. It is not sandboxed. The hosted MCP server
refuses those tools.
The [privacy policy](https://www.offlineprotocol.com/privacy) covers the same
points for every Offline Protocol service. Report security issues to
security@offlineprotocol.com ([SECURITY.md](SECURITY.md)).
## For AI agents (MCP)
The MCP server is **local stdio** inside the CLI — no separate cloud deploy.
Agents plan and integrate Offline Protocol components via tools (not from memory).
### Set up Cursor / Claude
```bash
# Pick ONE client (do not use | — the shell treats it as a pipe):
offline mcp install cursor
offline mcp install claude-desktop
offline mcp install vscode
offline mcp install claude-code
offline mcp install codex
# Claude Code shortcut:
claude mcp add offline-protocol -- offline mcp serve
# Codex shortcut:
codex mcp add offline-protocol -- offline mcp serve
```
Paste the printed JSON into the client’s MCP config. Ensure `offline` is on `PATH`,
then reload the agent / MCP session.
### Ask the agent to create a project
Use **Agent mode** and a prompt like:
```text
Using the offline-protocol MCP only (do not invent package names or SDK APIs):
1. Call generate_architecture with my idea.
2. Follow nextActions.
3. Call scaffold_project with name="festival-pulse" and platform="react-native".
4. Call get_skill for every skill listed before writing Offline Protocol code.
Idea: nearby festival chat with Offline ID login and BLE mesh messaging
```
Or use the MCP prompt **`plan-app`** with `description`, optional `platform`, and `name`.
**Flow:** `generate_architecture` → `scaffold_project` (new) or `integrate_packages` + `init_project` (existing) → `get_skill` → app code.
Full tool list and rules: [docs/MCP.md](docs/MCP.md).
## Development
```bash
cargo build --workspace
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --all -- --check
# live registry content (not the embedded copy):
OFFLINE_REGISTRY_DIR=./crates/offline-registry/registry cargo run -p offline-cli -- registry list
# local API gateway (typically :3010):
OFFLINE_ENV=dev offline login
```
### CI & releases
| Workflow | Trigger | What it does |
|---|---|---|
| `.github/workflows/ci.yml` | PR + push to `main` | fmt, clippy, tests; release-build smoke on `main` |
| `.github/workflows/release.yml` | tag `v*` or manual bump | Bumps version, updates `CHANGELOG.md`, builds binaries, publishes GitHub Release |
Manual release: Actions → **Release** → bump `patch`/`minor`/`major` (needs `RELEASE_PAT`).
Or push a tag: `git tag v0.1.2 && git push origin v0.1.2` (add a `CHANGELOG.md` section for that version first if you skip the bump job).
Release notes on GitHub come from the matching `## [x.y.z]` section in [`CHANGELOG.md`](CHANGELOG.md), which `git-cliff` refreshes during the bump job (`cliff.toml`).
## Documentation
- **[docs/QUICKSTART.md](docs/QUICKSTART.md)** — install, portal login, first scaffold, MCP with AI agents
- **[docs/README.md](docs/README.md)** — documentation index (coverage map for every command)
- [docs/COMMANDS.md](docs/COMMANDS.md) — full command reference
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — crates, planner, MCP integrate loop
- [docs/REGISTRY.md](docs/REGISTRY.md) — registry schema and authoring
- [docs/MCP.md](docs/MCP.md) — MCP tools, agent prompts, local vs hosted
- [docs/MCP-MESH-APP.md](docs/MCP-MESH-APP.md) — React Native mesh scaffold flow
- [docs/AUTH.md](docs/AUTH.md) — device-flow login / logout
- [CHANGELOG.md](CHANGELOG.md) — release history
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, PR expectations, and registry
guidelines. Please follow the [Code of Conduct](CODE_OF_CONDUCT.md).
Security reports: [SECURITY.md](SECURITY.md) (do not file public issues for
vulnerabilities).
## License
Licensed under the [Apache License, Version 2.0](LICENSE).
See [NOTICE](NOTICE) for copyright attribution.
## Terms and privacy
The CLI's source code is licensed under Apache-2.0 (above). Signing in with
`offline login`, and using the Offline Protocol developer portal, the hosted
MCP server at `mcp.offlineprotocol.com`, and hosted services, are governed by
the [Developer Terms of Service](https://www.offlineprotocol.com/legal/developer-terms)
and the [Acceptable Use Policy](https://www.offlineprotocol.com/legal/acceptable-use).
The [Privacy Policy](https://www.offlineprotocol.com/privacy) explains what the
CLI stores on your computer and what it sends to Offline Protocol. All legal
documents: [offlineprotocol.com/legal](https://www.offlineprotocol.com/legal).
## Status
Shipped: device-flow auth, deterministic planning, Tera scaffolds, MCP **plan + scaffold + integrate**,
CI, multi-platform GitHub Releases, `offline update`.
Also shipped: hosted MCP (Streamable HTTP) at `https://mcp.offlineprotocol.com/mcp`
(read-only tools; see [docs/MCP-HOSTED.md](docs/MCP-HOSTED.md)).
Not yet: hosted registry refresh, Homebrew tap, official crates.io publish
(install with npm until then).
## Agent plugins and distribution
The [agent plugin package](plugins/offline-protocol/README.md) provides a shared integration skill and local MCP configuration for Codex, Claude Code, Cursor and Gemini CLI. These packages are review candidates; public listings and client verification are tracked in the [distribution plan](distribution/PLAN.md).
Validate packaging with `node scripts/check-agent-distribution.mjs`; add `--smoke` to test the pinned published CLI over MCP.
`node scripts/check-guidance.mjs` fails on SDK guidance that is known to be wrong (web `appId` on id-react, camelCase event fields, string priorities, Wi-Fi Direct enabled in a cross-platform starter or listed next to BLE as an equal transport, the removed telemetry sink, a Maven Central dependency line for the Android preview before 0.29.0 or for Java before 0.30.0, a pub.dev line, a "no package" claim or a refused Nostr config for the Flutter plugin, a Ruby gem line before 0.31.0 or a "not on RubyGems" claim, an npm line for the browser binding's unpublished JavaScript client, a NuGet line for the C# binding, a silent React Native fallback); CI runs it on every pull request.
`node scripts/scaffold-matrix.mjs` generates every starter template variant with a locally built `offline`, installs the exact package versions the registry pins, and runs `tsc --noEmit`; the Scaffolds workflow runs it for every variant on pull requests that touch the registry, generator or CLI. `OFFLINE_BIN=target/debug/offline node scripts/agent-readiness-e2e.mjs` runs the MCP readiness scenarios against a local build instead of the published npm package.