> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wolffi.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Mobile App

> The phone companion — pair once, then carry the same conversations, settings and usage over an end-to-end encrypted tunnel

# The Phone-Sized Companion

Wolffish runs on your machine — which has always meant being at your machine. The mobile app puts the desktop experience on a phone: the chat feed with its tool cards and delivered files, the conversation history, the usage ledger, and every settings page — Model, Channels, Services, MCP, Variables, Capabilities, Knowledge, Projects, Automations, Procedures, Customization, Usage, Data, Updates, Preferences, Appearance — laid out as the desktop's settings sidebar turned into a navigation list.

Since **v1.0.235** it does that against *your* desktop. A phone pairs with this machine and carries the same conversations, the same settings, the same usage with it — over a link only the two devices can read. Since **v1.0.236** it also carries everything that *shapes* those conversations: [projects, procedures, automations](#the-workspace-editable) and the three documents behind every reply are edited from either device, against the very same files. And since **v1.0.239** the machine on the other end doesn't need a screen at all — a phone pairs with a [headless server](#paired-to-a-server) exactly as it pairs with a desktop.

## Pairing

Everything lives in **Settings → Channels → Mobile** on the desktop. Pairing happens once, and there is no token to paste: the desktop *offers* a pairing and the phone claims it.

<CardGroup cols={1}>
  <Card title="Pairing QR" icon="qrcode">
    Generate a QR and scan it with your phone's camera. The secret travels **screen to camera** — it never crosses the network. The QR also carries the relay address, so a custom relay needs no extra typing.
  </Card>

  <Card title="Pairing code" icon="keyboard">
    Generate a short code — eight characters, printed as `K7M9-2QXR` — and type it on the phone when the camera can't see the screen. Case, spaces and dashes don't matter, and look-alike characters (`O`/`0`, `I`/`1`) are folded for you.
  </Card>
</CardGroup>

An offer expires after about **three minutes**; generate a new one whenever you need. Open Wolffish on the phone, tap **Connect to Desktop**, and scan or type.

<Note>
  The typed code carries only the secret. If you point the desktop at a **custom relay**, the phone must be told that address by hand — its pairing screen has a field for it, and the desktop's offer screen shows the exact URL to type. The QR does this for you.
</Note>

## The Encrypted Tunnel

Once paired, the two devices talk **end to end encrypted** — **X25519** key agreement, **ChaCha20-Poly1305** for the frames. Everything is sealed on your devices before anything is sent, and only this desktop and your phone hold the keys. Each device keeps its own key and **pins the other's**: no account, no password, nothing to sign into.

The paired panel shows the link's whole state — the tunnel status, the phone's name, model and OS, when it paired and when it was last seen, the relay and rendezvous, both key fingerprints, the session, and the frame counters. **Matching fingerprints on both devices mean nothing sits in between.**

Pairing keys are stored on the desktop through the OS key store — Keychain on macOS, DPAPI on Windows, the secret service on Linux — and the panel names the backend actually in use.

### The blind relay

The two devices are rarely on the same network, so something has to carry the bytes. That something is a **blind relay**, and its ignorance is the point:

* It helps the two find each other at a **rendezvous id** derived independently on both devices — the only fact about a pairing it ever learns.
* It passes **sealed frames** along. It cannot read, alter or replay them.
* It stores **nothing at all** — no database, no logs, no accounts, no message history.

The relay is [open source and self-hostable](https://github.com/thewolffish/wolffish-relay). Point the desktop at your own deployment in the same panel and the next pairing carries that address to your phone.

<Warning>
  Changing the relay **unpairs a paired phone** — both the offer payload and the paired device name the old address. The desktop asks you to confirm before applying, then you pair again on the new relay.
</Warning>

### Disconnect vs. unpair

Two exits sit at the foot of the panel and the difference between them is the whole point. **Disconnect** drops the live link; the pairing survives and the phone reconnects when it comes back to the foreground. **Unpair** forgets the device — its keys are cleared and the phone has to pair again from scratch.

Leaving from the phone's side leaves **nothing behind anywhere**: badges are cleared — buckets, tray, app icon — the relay is told to zero its copy and [forget the device's push registration](/integrations/notifications#registration-and-leaving-no-trace), and only then does the phone drop its keys and wipe everything it synced, back to a fresh install. Your desktop keeps the originals, so pairing again restores them.

<Info>
  The desktop is the host: it parks on the relay and waits. The phone dials in when it is in the foreground and disappears when iOS suspends it, so **every reconnect re-handshakes** and nothing assumes continuity. A phone that has been in your pocket for an hour comes back to a freshly secured link.
</Info>

## What Crosses the Link

The phone is a second view of the whole app, not a message stream — so the desktop answers for far more than chat, and **pushes changes as they happen** instead of leaving the phone to poll:

| The phone can                                                                       | Notes                                                                                         |
| ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| Browse conversations and read them                                                  | The index and each body on demand, kept live                                                  |
| **Send a message and run a turn**                                                   | The turn runs on the desktop, with your desktop's models and tools                            |
| Answer [`ask_user` cards](/capabilities/built-in-capabilities) and approval prompts | Parked requests live on the phone until answered — or until the turn is aborted               |
| Upload and download files                                                           | Chunked over the same sealed frames; uploads land in the workspace                            |
| Read and change settings                                                            | Config, variables, capability switches, [reflection](/memory/reflection) schedule and run-now |
| **Edit projects, procedures and automations**                                       | The same files the desktop writes — see [The Workspace, Editable](#the-workspace-editable)    |
| **Edit Soul, User and Agents**                                                      | The three documents behind every reply, saved straight into the workspace                     |
| **Score a finished turn**                                                           | The 0–10 bar, landing live on the desktop's own bar                                           |
| **Collect a diagnostic bundle**                                                     | The same [export](/debugging/diagnostic-export) the desktop's bug button runs                 |
| See usage and the changelog                                                         | The desktop's own ledger, mirrored                                                            |

Because a turn started on the phone is a real turn, it gets its own **channel badge**: conversations that began there wear a phone glyph in the desktop's History list and conversations rail, next to the Telegram, WhatsApp, [terminal](/getting-started/cli), automation and procedure badges.

<Note>
  Credential fields on the phone stay placeholders — **secrets are never entered on the phone**. Keys are pasted on the desktop, where they live.
</Note>

## The Workspace, Editable

Pairing brought your conversations across; until **v1.0.236** everything that *shapes* them stayed at the machine. [Projects](/configuration/projects), [procedures](/configuration/procedures) and [automations](/configuration/heartbeat) were things the phone could show and only the desktop could change. All three are now edited from either screen.

<CardGroup cols={1}>
  <Card title="Projects" icon="folder">
    A project gathers the standing instructions and files that fresh conversations start from. Create one, give it an emoji from the picker, edit its instructions, **add files from the phone itself**, and **file an existing conversation under it**. A chat opened inside a project carries its briefing from the very first turn rather than picking it up a moment later.
  </Card>

  <Card title="Procedures" icon="play">
    A saved prompt you run on demand — created, edited, deleted and **run** from the phone, with the run reporting back what the scheduler actually did.
  </Card>

  <Card title="Automations" icon="clock">
    A prompt that runs on a schedule, edited here as **the same `heartbeat.md` the desktop's own editor writes** — with the time of its next run and a play button beside it.
  </Card>

  <Card title="Customization" icon="file-pen">
    [**Soul**](/configuration/soul-md), [**User**](/configuration/user-md) and [**Agents**](/configuration/agents-md) — who Wolffish is, what it should always know about you, and the procedures of yours that outrank the built-in ones — open as editors and save straight into the desktop workspace. These are the files that shape every reply on every surface, chat and Telegram and WhatsApp alike.
  </Card>
</CardGroup>

Every one of these writes goes **through the desktop's own code**, so a change made on the phone and a change made at the machine are the same act, against the same files — never a second copy that quietly disagrees. Both screens show it at once: the desktop's markdown editor **adopts a save made on the phone live**, unless you are mid-draft, in which case your unsaved text stands and wins when you save it.

Projects, procedures and automations all carry **attached files and working folders**, editable from the phone since **v1.0.239**. An attached file is copied into the workspace, so a run can never break because the original moved; a working folder is the real folder the work happens in, so it is deliberately *not* copied. Folders are **typed, not browsed** — the desktop validates the path against its own filesystem and answers with its own refusal when it doesn't exist — and the chip wears the full desktop path, so there is never a doubt about *which machine's* folder it names.

### Turns you can score

Every finished answer carries a **0–10 [rating bar](/memory/reflection)**. A score cast on the phone lands on the desktop exactly as the desktop's own does, and a score cast anywhere else — the desktop's bar, a bare number replied to Telegram — appears here without waiting for anything to reload. It is the same signal the nightly reflection learns from; it simply stopped requiring the desktop.

### Every conversation, without leaving the one you're in

A sheet slides over the chat carrying the **whole conversation list**, grouped by recency, showing which project each one belongs to, which one is running right now, and where each one came from — the **origin glyph rides the very first live update**, so a conversation the phone has only ever seen running still wears its Telegram plane or terminal prompt from the first instant, instead of sitting blank until the metadata catches up. Switching is a tap, and the turn you were watching keeps streaming while you look. Its five destinations — Settings, Projects, Automations, Procedures and Customization — **stay pinned at the top** while the conversations scroll underneath them.

### What the desktop is busy with

Work that takes the desktop's whole screen shows on the phone as a **card in a stack over whatever screen you are on**, with the prompt behind each one a tap away: an [automation](/configuration/heartbeat) mid-run, a [compaction](/memory/consolidation), the [nightly reflection](/memory/reflection), a memory index being rebuilt. They appear the moment the phone connects — including for a run that started while it was asleep — and they go when the connection goes, because a card claiming something is running on a machine the phone can no longer see is a card that lies. The phone stays usable while they happen instead of looking broken.

### Diagnostics from the phone

Collecting a conversation's [diagnostic bundle](/debugging/diagnostic-export) — logs, tasks, memory, context, settings, attachments, and the model's own account of what went wrong — is something the phone can start, **watch step by step**, and hand to the system share sheet when it finishes. It runs behind the same single-flight guard as the desktop's own button, so a run started here and one started there can never fight over the same files.

<Note>
  A turn you are watching from the phone shows **the question above the answer**: the prompt travels with every live update, so a phone that pairs — or opens a conversation — mid-turn is no longer reading a reply to nothing. And a tunnel that comes up **mid-run knows the run exists**: on every connect the phone asks the desktop what is running right now, so a busy conversation shows its stop button and its streaming pulse instead of an idle composer, a parked approval card comes back to be answered, and the rating bar appears only once the desktop has actually said the turn ended — never over an answer still being written.
</Note>

## Phone Notifications

With **Phone notifications** on, the agent can reach you through its `notify_phone` tool when a run finishes, fails, or needs you. Nothing is ever sent automatically — the agent has to deliberately call the tool, and **Off makes the tool refuse outright** by removing it from the agent's hands entirely. A tap lands where the notification points — including **this run's own conversation**, via `wolffish://chat?id=current` — and if the phone is connected, the notification rides the live tunnel; if it isn't, it goes out as a platform push instead.

The whole system now has [**its own page**](/integrations/notifications): the phases and how long each stays relevant, what the desktop stamps on every send and what the model is never trusted with, the fixed list of screens a tap can open, how unread badges stay honest, and exactly what the relay can and cannot see along the way.

### Unread badges

Every notification that names a conversation feeds a **per-conversation unread count** — on the conversation rows here and in History, summed on the floating menu button, and as the number on the app icon, correct even before the app has woken. Clearing is reading: open the conversation and its count is gone, tray notifications included; there is no mark-as-read chore. The [notifications page](/integrations/notifications#unread-badges) has the full mechanics.

### Task results

One switch decides how much of a run the phone's feed relays. **Off** — the default — keeps it clean: assistant messages, file-bearing results, and errors. **On** relays every tool call and activity. Connection logging is separate and always on.

## The Chat Feed

The feed renders what the desktop renders — markdown replies, tool activity, delivered files — plus two cards worth knowing:

* **Reasoning** — when a reply carried model thinking, a collapsed card sits at the end of the turn; tap to unfold the raw thinking text. The desktop app shows the same card, so both surfaces tell the same story about how an answer came to be.
* **Status pills** — a turn that stopped at the response limit or hit a connection error says so in place.

Text is yours to take: **long-press any bubble or tool card** to select from the *rendered* reply — in place on Android, and on iPhone in a dedicated sheet where you drag across the styled text and copy exactly the part you need, instead of fighting a screenshot.

The composer's menu sheet carries the desktop's chat controls: cloud/local model switch, model picker, single vs. [workflow](/configuration/workflow-mode) mode, the thinking-level dial, a [project](/configuration/projects) picker with the project's instructions in preview, and a context meter. Its button wears **vertical faders** so it doesn't read as a second copy of the conversations navigator beside it, and the Model screen leads with the two knobs touched every session — chat mode and thinking — keeping the picker below them. Voice notes record from the mic and ride as audio attachments.

## File Viewers

Tap any attachment to expand it full screen (sharing hands off to the system share sheet): images with pinch-to-zoom and pan, SVG with a lightbox, video and audio players, PDF, Word documents, spreadsheets — with CSV/TSV rendered as tables — Markdown, plain text, HTML with a preview/source toggle, and code files as line-numbered cards.

**PDFs render in the card on both platforms.** Android's WebView has never shipped a PDF engine, so the card used to hand the document off to whatever viewer the phone happened to have. The reader is now vendored into the app — a self-contained pdf.js page composed around the file's own bytes — so the preview and the expanded document are the same on Android as on iPhone: a real first page, scrollable and pinch-zoomable. Nothing is handed to another app, the sandbox stays shut, and the page drawing the document can reach no file but the one you opened. Very large PDFs still go to the system viewer, which is where they belong.

## Usage On the Go

The Usage screen is the desktop's Usage panel on one column: an **Activity** month grid, a **day card** that's always visible (it opens on today and moves when you tap a day — tokens, cost, messages, per-model rows, Brave Search queries), six ranges from Today to All Time, overview stats (conversations, messages, total tokens, active days, longest streak, favourite model), a **Costs** section (total spend, top day, daily average), and per-provider cards.

Each range **reports its own window**: every total is closed at today as well as opened at its start, so a figure labelled Today can never quietly carry tomorrow, and stepping from 3 Months to 6 Months to Year to Date moves the numbers instead of repeating them. The closing bound applies to the range totals only — the activity calendar and its day card still draw every row the ledger holds, since a month rendering blank because the clock hasn't reached it is indistinguishable from a month with no usage. The range switch fills its row as one control, splitting the width evenly between the six.

## Browser Extension Rows

Settings → Services mirrors the desktop's [multi-browser extension](/integrations/browser-extension) roster: **one row per connected browser profile** — browser name with the profile email, version, and OS — with the screenshot settings editable from the phone. The pairing port stays desktop-managed: moving it restarts the desktop's pairing server, which is the desktop's own act. A snapshot from an older desktop that predates multi-browser support falls back to a single "Chrome extension" row with its port. Installing and pairing the extension itself always happens on the desktop.

## Updates

Two cards in **Settings → Updates** keep the surfaces distinct: **This app** (the mobile version and build, whether you're on a store build or an over-the-air update, and an automatic-updates switch that genuinely governs update checks) and **Desktop app** (the mirrored desktop version, platform, and its own auto-update setting).

## Paired to a Server

The phone doesn't care what answers the tunnel. Pair it with the desktop app on your laptop, or with a **headless Wolffish on a [VPS](/getting-started/server-deployment)** — the same feed, the same workspace, the same settings pages, over the same sealed link. For a server deployment this is **the recommended interface**: the [terminal CLI](/getting-started/cli) administers the box, and the phone is the surface you actually live in — conversations streaming live, automations watchable as they run, [notifications](/integrations/notifications) when something finishes or needs you, and projects, procedures, automations and the customization documents all editable against the server's own files.

Pairing works from the terminal — `wolffish pair phone` draws a scanner-friendly QR when the window fits it, and falls back to the **typed code** by itself when it doesn't. And the phone reports the server's terminal state right back: the **Settings → Channels → CLI** card says whether the `wolffish` command resolves on the desktop's own PATH, whether autostart is registered, and which mechanism holds it — `systemd`, `launchd`, or `schtasks`. Registering with the operating system is the desktop's own act, so this device reports it rather than changing it.

<Note>
  On a self-hosted relay, the typed code carries only the secret — enter the relay's address in the **Relay** card on the phone's pairing screen. The QR carries it for you.
</Note>

## Demo Mode

The app still ships a **self-contained Demo Mode** — a complete, offline tour of the product filled with three months of real (anonymized) usage, for trying it before pairing anything. The first tap downloads a curated dataset — about 19 MB over the wire in eleven shards, unpacking to **167 conversations** plus a full desktop settings snapshot — under a progress bar; later taps open straight into chat. The download is versioned, so a republished dataset refreshes itself on devices that already imported an older one, and an interrupted download leaves the previous dataset intact.

After the import, the demo is fully offline. Sending a message returns a friendly card explaining that new messages aren't processed in the demo — browsing, editing, and configuring all work; real turns need a paired desktop. Attachment bytes load lazily: each file type resolves to one published sample fetched on first view and cached, so the file viewers have something genuine to open for over a hundred formats.

The dataset covers the parts of Wolffish a transcript alone can't show: an agent **asking permission** before something dangerous — and being refused once — a **twenty-question [ask card](/capabilities/built-in-capabilities)** answered one chip at a time, and a whole session **held by voice**, spoken in both directions. The workspace behind the demo is populated too, so Projects, Procedures, Automations and the customization documents carry real content from the first screen rather than filling in a moment later.

### Leaving the demo

The only exit is deliberate: **Settings → Data → Factory reset this device**, confirmed by typing the phrase **FACTORY RESET**. It wipes the conversations, cached media, and mirrored settings from the phone, keeps your language and theme, and never touches a desktop.

The app runs on iPhone and Android, portrait, and fully bilingual — English and Arabic with complete RTL mirroring; switching language restarts the app.
