Repository navigation
v0.10.0
Mobile is here.
0.8 made a run survive the workspace you left. 0.9 made a space say what it was for. 0.10 removes the last thing tying you to the chair: a run waiting on an answer only reached you if you were sitting at the Mac. Mains is two apps now. The Mac still runs the agents, and an iPhone pairs with it over a QR code to drive them. You can approve a tool, send the next prompt, and read the transcript from wherever you are.
📖 Full write-up: Mains 0.10: Mobile is here
Pairing is a QR code and a token
- Settings → Relay → This machine shows a QR code. The phone scans it and is paired.
- The pairing code is short-lived. It lives only in memory, expires after five minutes, and works for a single exchange.
- The device token is what stays. It is stored securely on the phone, and only as a hash on the Mac. Revoke a device and its live connection is dropped immediately, not at the next reconnect.
- The phone reaches the Mac over the network, on your LAN or through Tailscale HTTPS. If the camera isn't available, you can copy the pairing link instead.
What a paired phone is allowed to do
- Everything is an allowlist. The phone can read runs, turns, tool calls, artifacts, context, workspaces, branches, diffs, projects, collections, spaces, providers, models, skills, and slash commands.
- It can change seven things. It can answer an approval, continue, start, fork, or stop a run, change a space's mode, and adjust the composer's run settings. Each command carries an id, so a retry on a flaky connection never runs twice.
- Files, git, the terminal, and settings stay on the Mac. Provider configuration reaches the phone without API keys or URLs, and only an allowlisted set of events is pushed to it.
The phone keeps its own copy
- A local database on the phone holds spaces, collections, workspaces, runs, turns, tool calls, artifacts, and pending approvals. It opens on cached state, even offline.
- Data flows one way, from the Mac to the phone, so there is nothing to merge. Reconnects fetch only what changed since the last sync.
- The connection is managed outside the UI. While offline it waits for the network instead of burning battery on retries. It rotates through the Mac's addresses when one fails, and tells you plainly when a token is refused or the versions don't match.
Answering the agent from the couch
- Approvals arrive as cards that mirror the desktop dialogs. You can allow or deny a tool, pick an option or type an answer to a question, or open and accept a URL. A request stays in the app until it's answered.
- The transcript renders like the desktop one. It uses the same tool-call renderers and Mains typography. Images the agent produces open in a full-screen viewer with zoom and a share sheet.
- Model, effort, permission mode, and fast, goal, and plan switches are all on the phone, wherever the provider supports them.
- Search covers everything on the Mac at once: chats by title or project, workspaces by name or branch.
Data sharing is asked for, not assumed
- Before a prompt goes to a provider, the phone asks. It names the service and who receives the data (OpenAI for Codex, Anthropic for Claude Code, GitHub for Copilot, Anysphere for Cursor) and links the privacy policy.
- Consent is tracked per Mac, per provider, and per disclosure version. A new Mac, a new provider, or a changed disclosure asks again. Grants live in Settings → AI Data Sharing on the phone.
What the phone can't do yet
- iPhone only for now. Android is planned.
- No push notifications yet. Approvals wait in the app until you open it.
- No terminal, file browsing, git actions, or diff viewer. Workspace rows show the branch and diff size only.
On the desktop
- Bash approvals show a readable command preview, with the shell, working directory, and timeout when the agent sets them.
- File edits waiting for approval show a real diff instead of raw tool input.
- Provider sign-in runs in its own terminal. It is scoped to the login command and doesn't need a workspace.
- Math renders. LaTeX in replies displays as proper equations on both the Mac and the phone.
- Rotate the backend token from Relay. Clients using the old token are disconnected.
- Every WebSocket host bind requires a pairing token, loopback included. The image proxy requires one too, serves only images, and sends security headers.
- Run attachments are validated. Unsafe filenames and paths outside the upload directory are rejected.
Smaller things
- Codex can resume runs that never picked a model, falling back to the provider's default.
- A live run refuses a second prompt, and Enter in the editor no longer slips past that.
- An emptied composer no longer sends a stray newline.
- Work mode now tells the agent that web search and web fetch are available and allowed.
- Space and collection icons live in one shared package, so both apps show the same set.
- The repository is now a monorepo:
apps/desktop,apps/mobile, and a sharedpackages/contractsfor the wire protocol both apps speak. - Building from source now requires Node.js 22.12 or newer. CI and releases run on Node 24.
Platform: macOS only (Apple Silicon and Intel). The iOS app ships through TestFlight while it is in App Store review.
Full changelog: v0.9.0...v0.10.0