Skip to content

iOS & iPadOS App

Download mycellm on the App Store

mycellm turns your iPhone or iPad into a node on the distributed inference network — serve inference, earn credits, and chat with on-device privacy protection. An M-series iPad Pro is a desktop-lite powerhouse: it runs 3B+ models at 30+ tokens/sec on Metal, serving the swarm rather than just using it. On iPhone, run smaller models locally — or route from your phone to a trusted device on your own network (your Mac mini, MacBook Air, or Studio) for far bigger models, no cloud involved.

Download: App Store · Source: github.com/mycellm/mycellm-ios (Apache 2.0)

Requires iOS / iPadOS 17.0+ on iPhone or iPad. M-series recommended for on-device inference.

The app is open source (Apache 2.0). The simplest way to get it is the App Store; to build it yourself:

Terminal window
# Clone and generate Xcode project
git clone https://github.com/mycellm/mycellm-ios
cd mycellm-ios
xcodegen generate
open Mycellm.xcodeproj

Set your DEVELOPMENT_TEAM in project.yml, then build and run (⌘R).

  1. Splash screen with spore particle animation
  2. The app generates your device identity (Ed25519 keypair, stored in Keychain)
  3. Auto-connects to the public mycellm network via bootstrap.mycellm.dev
  4. Auto-loads the last model you used (if any)

No account creation or sign-up required. Your identity is your cryptographic key.

Open the Models tab. Suggested starters:

ModelSizeGood for
Llama 3.2 1B Q40.8 GBFast responses, limited device
Llama 3.2 3B Q41.9 GBBalanced quality/speed
Phi-3 Mini Q42.3 GBReasoning tasks
Gemma 2 2B Q41.5 GBGeneral purpose

Tap a model to download from HuggingFace. Models persist across app launches but are removed if you delete the app.

Two inference modes, selectable at the top of the chat screen:

Prompts route through the public gateway to any available node on the network. Uses credits. Best for larger models you don’t have locally. Responses include node attribution (which peer served the request).

Runs inference locally using llama.cpp on Metal. No network required. Works in airplane mode. Conversation context is managed automatically — older messages are compacted into summaries when the context window fills.

Switch between modes mid-conversation. Each message bubble shows a colored border indicating which route was used (blue = network, green = on-device).

  • Persistent threads — conversations are saved with full metadata (model, node, tokens/sec, route, duration)
  • Thread management — switch between threads, delete threads or individual messages
  • Export/share — share any thread via the system share sheet (Notes, Mail, Messages, etc.)
  • Private sessions — ephemeral mode that doesn’t save to disk (eye-slash icon in thread list)
  • Copy messages — long-press any message for copy/delete options
  • Timestamps — visible on every message
  • Date dividers — visual grouping between different days
  • AI disclaimer — dismissable reminder above the input field

The privacy guard scans every outgoing prompt for API keys, passwords, credit card numbers, and PII patterns. When a match is detected:

  • High severity (API keys, passwords) — automatically redirects to your local model
  • Medium severity (email addresses, phone numbers) — warns with option to send anyway

This runs on-device before any data leaves your device.

SettingDescription
Remote EndpointPoint to a private bootstrap, OpenRouter, ollama, etc.
Privacy GuardToggle scanning, view built-in rules
ScreensaverAmbient spore animation, prevents OLED burn-in
Tip JarSupport development (StoreKit 2 IAP)
NetworkView joined networks, manage membership

Your device contributes compute to the network and earns credits automatically when a model is loaded and the node is running. The bootstrap relays inference requests to your device via QUIC.

Tip: iPad with M-series chips can serve 3B models at 30+ tokens/sec — competitive with many cloud instances.

Your device also runs a local HTTP server that exposes an OpenAI-compatible API on your LAN. Other tools on your network can use your iPad as an inference backend.

As of app 1.2.0, an iOS node answers the same paths as the Python node, with the same payload shapes — so the dashboard, mycellm CLI and any fleet tooling manage an iPad exactly as they manage a Linux box. Everything under /v1/node/** requires the node API key (set it in Settings → API); /health, /metrics and the inference paths are open.

Inference — /v1/chat/completions (streaming, tools, vision), /v1/embeddings, /v1/models, /v1/models/{id}, /v1/models/capabilities.

Status & observability — /v1/node/status, /system, /version, /peers, /connections, /activity (+ /activity/stream SSE), /logs (+ /logs/stream SSE), and Prometheus /metrics.

Model management — /v1/node/models/ local, saved, suggested, search, search/{repo}/files, {name}/config, download, downloads, downloads/abort, load, unload, reload, update, scope, load-status, load-status/clear, remove-config, delete-file.

Credits & relays — /v1/node/credits (+ /tier, /history, /networks), /v1/node/relay (+ /add, /remove, /refresh).

App 1.2.0 reports core parity 0.7.1 — /health, /v1/node/status and the peer capability advertisement all carry that version, and it is the number fleet tooling should compare against. It moves independently of the App Store version above: a security fix shipped on both platforms bumps it even when no wire format changed, so a patched build and an unpatched one never report the same number.

The next release (1.3.0, core parity 0.8.0) adds the additive half of the execution fabric: embedding models declare execution_roles: ["embed"] so a planner stops routing chat to them instead of learning by refusal, and the device conditions below travel with the capability advertisement rather than being visible only on /v1/node/status. Every field is omitted when unset, so a 0.7.x or 0.6.3 peer sees a byte-identical payload.

POST /v1/node/models/download takes the same three forms as the Python node — a Hugging Face repo_id/filename pair, a single url + sha256, or a files manifest for an MLX directory. See the Models API for the request shapes and the verification rules, which are identical on both platforms.

A phone knows things a Linux box doesn’t, and a scheduler that routes on hardware specs alone will send work to a device that is throttling or nearly flat. /v1/node/status carries a device block:

  • thermal — state, throttled, unloading
  • power — battery_percent, charging, low_power_mode
  • network — interface, expensive, constrained, downloads_allowed_on_metered
  • runtime — app_state, foreground, serving_requires_foreground

expensive (costs money) and constrained (Low Data Mode) are reported separately rather than OR’d into one flag, because the right response differs.

/v1/node/system adds storage.models_free_bytes, so a caller planning a download gets an honest figure before starting one.

The node demotes itself. A device that is thermally critical, in Low Power Mode, or under 20% on battery advertises role: consumer instead of seeder — in /v1/node/status and in what peers see. It needs no agreement from any scheduler to protect itself.

Off by default. A download requested on cellular or in Low Data Mode is refused with 409 expensive_network, reporting network and estimated_bytes so the caller can decide with the real size in hand:

{"error": {"code": "expensive_network", "network": "cellular",
"estimated_bytes": 4128309248, "message": "…"}}

Send "allow_expensive": true to override per request, or grant standing permission in Settings → Downloads → Allow on Cellular. The policy is enforced per request rather than per session, and there is no confirmation dialog, because the caller is a dashboard or a script with nobody at the device to tap anything.

POST /v1/embeddings is served with the same request validation and OpenAI error envelope as the Python node. On-device embedding execution is disabled in app 1.2.0: the endpoint answers 400 with code embeddings_not_supported, which is what the Python node returns for a backend that cannot embed, and supports_embeddings on /v1/models/capabilities reports false. Check that field before sending, and route embeddings to a Python node meanwhile.

The backend path is written but produced numerically incorrect vectors in testing, so it is gated off rather than shipped — see the app CHANGELOG for the detail.

Some Python routes are deliberately absent, because they are coordinator or credential surfaces that don’t belong on a device:

SurfaceWhy not
/v1/admin/**Fleet approval and the node registry are a coordinator’s job
/v1/public/**The credit-tracker gateway runs on a bootstrap, not a leaf
/v1/node/federation/**Joining and inviting is driven from the app UI
/v1/node/settings/**Credentials live in the Keychain, not behind an HTTP write
/v1/node/proxyThere is no hosted dashboard on a device to proxy for
/api/** (Ollama compat)Not implemented

Fleet commands over the network protocol (node.status, node.config, model.list / load / unload / scope) are fully supported; train.status is not, since iOS nodes don’t train.

All network endpoints are centralized in Mycellm/Core/NetworkConfig.swift. To run your own network, edit one file:

enum NetworkConfig {
static let bootstrapHost = "bootstrap.yournetwork.dev"
static let apiBase = "https://api.yournetwork.dev"
// ...
}
  • Foreground only — iOS suspends network activity after ~30 seconds in the background. The app must stay in the foreground to serve inference.
  • No DHT — queries the bootstrap as a gateway (battery/NAT constraints on mobile).
  • Memory pressure — models >3B may be evicted on <8 GB RAM devices. The app recovers gracefully.
  • No background downloads — model downloads pause if backgrounded.