---
title: "The phone client — Robion user manual"
description: "The phone client: pairing, installing, push notifications, chat, photos, questions, permissions, new task or project, changes and merge."
url: "https://robion.app/docs/phone/"
lang: en
---

# The phone client

The phone is a small web app the Robion server serves at `/m/`, reached over your Tailscale
network. This file covers pairing, installing it, push, its screens, the chat and its limits.

## What it is

- A web app (PWA) served by the Robion server itself — the Mac's server or a Linux server. It
  shows the state of every task, lets you answer permissions and questions, sends prompts and
  photos, reviews and merges changes, and reads the Inbox and the board. It is not a terminal:
  the **terminal** tab shows the text of the screen, with a key bar for dialogs.
  Dividers the agent drew across the wide desktop terminal are shortened to the phone's width
  there (since 0.20.28), so a rule is one line, not five.
  Since 0.20.29 the screen is read cell by cell, so a line that wrapped in a narrow pane keeps
  the space at the wrap (words ran together before).
- The server stays on loopback; Tailscale Serve exposes it as HTTPS to your tailnet only. Every
  phone authenticates with its own token (a cookie set at pairing); each pairing can be revoked
  separately (**paired devices** under **⚙ → Servers**).
- Language and theme follow the server (**⚙ → Appearance**); the page reloads when they change.
- Swipe from the left edge to go back. An **offline** strip at the top says "no connection to
  the server — retrying"; when a new Robion ships, a banner offers **reload**.

## Pairing

- On the Mac: **⚙ → Remote access** → **enable**. Prerequisites: Tailscale.app signed in and
  HTTPS certificates enabled for the tailnet (admin console → DNS → HTTPS Certificates). The
  section reports the problem otherwise: "Tailscale not found", "Tailscale is not running",
  "the tailnet has HTTPS certificates disabled", or "Tailscale Serve does not point at the
  server — try disabling and enabling again".
- Then **pair phone**: a QR code and a **link** (`/pair?code=…`), valid 10 minutes and single
  use; **new QR code** mints another. Scan it on the phone: the browser sets the cookie and
  opens the app. **Allowed tailnet accounts** restricts who may pair (empty = anyone in the
  tailnet who has the link).
- On a Linux server: `robion-server pair` prints the same one-time link.
- The same pairing serves the desktop in a browser: a link ending in `&to=d` (**⚙ → Remote
  access** → **desktop in a browser**, or `robion-server pair --desktop`) lands on `/d/`, the
  whole window, instead of the phone client — [workspace.md](https://robion.app/docs/workspace/), "The desktop in a browser".
- A phone that is not paired shows "This phone is not paired" with the steps. **unpair this
  phone** in the phone's **⚙** deletes the cookie; pairing again needs a new QR code.
- **New token** (**⚙ → Remote access** → **generate a new token**) signs out every device and
  the hooks (they are reinstalled); the phone must be paired again.

## Installing it as an app

- Push notifications work only in the installed app, so the phone guides you to install it:
  on iOS, Safari Share → **Add to Home Screen**, then open Robion from its icon; on Android a
  real **install the app** button; elsewhere the browser menu → "Install app". The hint can be
  hidden; the same steps sit in the phone's **⚙**.
- Without the tailnet HTTPS certificate the PWA cannot be installed on iOS.
- The status bar is opaque in the app’s background colour (it follows the theme) since
  0.20.12. Before, the app drew under it and iOS 26 blurred the top of every screen (the
  task name and **back** looked washed out). iOS reads that setting only when the app is
  added to the home screen: after updating, remove the Robion icon and add it again from
  Safari, otherwise the blur stays.

## Push notifications

- Web Push straight from the server to the installed app, end-to-end encrypted, no third-party
  service. Enable it in the phone's **⚙ → notifications** → **enable notifications**; **send a
  test notification** there, or from the Mac (**⚙ → Notifications** → **send a test
  notification**, "Push to phones").
- A push arrives when an agent starts waiting for you (a permission: "agent asks for
  permission"; a question or a prompt in the terminal: "agent waits for a reply") or finishes
  a turn nobody has seen yet ("done"). The title is the task's name (for a relayed server's
  task followed by " · " and the server's name), the detail follows. No push while a focused
  client already shows that pane (for a relayed server the Mac's own view of it counts); the
  same news for the same pane repeats at most once a minute.
- A permission push carries **Allow** / **Deny** buttons — answer from the lock screen. A tap
  on any push lands in the task, even when the app was closed.
- Since 0.20.28 the terminal of the task shows a toast when the phone decided ("the phone
  allowed Bash", "the phone answered: Tea"), so whoever sits at the Mac knows why the dialog
  went.
- Automations can push too (the "push to the phone" action) and the Steward pushes its plan.
- States shown: "enabled on this phone", "blocked by the system — allow notifications for
  Robion in Settings", "this browser cannot push", "notifications work only in the installed
  app".
- So the phone has time to react, the Mac holds a permission request for the phone: **⚙ →
  Remote access → Hold permissions for remote access** (max. 55 s: Claude Code draws the dialog
  after a few seconds anyway and stops listening to the hook at 60 s). While held, the
  terminal's status line reads "robion: the permission is on the phone" (since 0.20.28). The
  hold ends when a client on the Mac opens the pane or when the time is up — then the answer is
  typed into the terminal instead.

## On a tablet

The client splits the display by its width (CSS px; a rotation changes it):

- **Under 768 px** (a phone): one screen at a time; **‹ back** and a swipe from the left edge
  go back.
- **768 to 1023 px** (a tablet held upright; since 0.20.37): a task fills the display and its
  header has **show the overview** (the panel icon) in place of **‹ back**: it slides the
  overview, with the bottom bar under it, over the task from the left edge; a swipe from the
  left edge does the same. Picking a task or a tab of the bottom bar closes it; so does a tap
  on the dimmed task. The overview, inbox, board and settings fill the display as on a phone;
  a diff and the controls keep **‹ back**. Before 0.20.37 the overview stayed docked at this
  width and left the task barely half of it.
- **From 1024 px** (a tablet on its side, a browser window): the overview with the bottom bar
  stays docked on the left (340 px) and the task opens next to it; **‹ back** closes the task
  and leaves the overview alone.

## Screens

- **The bottom bar** (since 0.20.16): **overview**, **inbox** (with the count of items as a
  badge), **board**, **settings** — on every top-level screen; a task or a diff fills the
  display without it. Before, the board and the inbox hid behind ⚙ and the inbox pill.
- **Overview** (the first screen): every project as a large row — icon, name and one line
  summing up its tasks ("1 waiting for you · 3 waiting for a prompt", amber when something waits
  for you, green for a finished turn nobody has seen, blue while an agent is working, "no
  running task" otherwise); a tap on
  the row folds the project to that one line (▸) and a tap unfolds it (▾); the phone remembers
  the folded projects per device. **fold all projects** / **unfold all projects** sits at the
  bottom next to **new project**. The project row is a raised band; under an unfolded project
  its open tasks hang indented on a guide line below the project's icon (since 0.20.27 — a
  task used to sit further left than the project's name and read as one), the ones needing you
  first, with a state dot and label; **allow** / **deny** straight on a waiting row. Projects
  nobody touched for the Mac's **Inactive projects** days (⚙ → Application on the Mac, default
  14; no prompt, no new task, no agent at work or waiting) sit under one **inactive · n** header
  at the end of their server's list, folded until tapped — each server's group on its own,
  remembered per device (since 0.20.27; per server since 0.20.31). On the
  right of each project row the icon buttons **controls** (▦, when the project has a panel)
  and **new task** (+): the form opens under the row. **N waiting for you** in the header and
  the subscription limits ("5 h x % · 7 d y %"). "no project — add one on the Mac": projects
  are added on the desktop. Coming back from a task, the list stands where you left it (since
  0.20.32; it used to jump to the top).
- **Servers on the phone** (since 0.20.17): the phone pairs with one server — the one that
  served it (the Mac's, or a Linux server paired with `robion-server pair`) — and that
  server relays every other server the Mac's **⚙ → Servers** knows and has paired (the hub).
  Once the Mac knows another server, the overview groups projects under a strip per server:
  **this server** (with its host) first, then each relayed server by its name and state —
  **connected**, **connecting…**, **unreachable — retrying**, **not paired on the Mac** (the
  box is in ⚙ → Servers but was never paired there), **another version — waiting for its
  update** (a protocol mismatch; the Mac updates the box by itself). A relayed server's tasks
  open, answer, chat, send photos, show changes and controls like the home server's; their
  pushes arrive from the home server with the server's name after the task ("build · lab"),
  and no push comes while the Mac shows that pane. What stays the home server's: the Inbox,
  the board, automations, the Steward and the limits. A Linux server relays nothing (it has no
  desktop pairings), so its own phone app sees only itself. Pairing a box on the Mac is enough:
  nothing to set up on the phone.
- **Task**: the header shows the state, the task's branch (a worktree task), the permission
  mode chip and the agent's step list ("3/7", **steps** / **hide**); tabs **chat**,
  **terminal**, **changes**, **aside** (side questions, since 0.20.35; a Claude Code task),
  and **design** for a task with a design record (workshop projects). **seen** marks a finished turn reviewed; **close task** ends the session.
- **changes**: "since the agent's last turn" or "uncommitted"; each file with its status
  (new, modified, deleted, renamed, conflict) opens a red/green diff; the branch line and
  **Merge into main** for a worktree task (below).
- **INBOX** (`#/inbox`): what the automations left for you — "an agent proposes a rule",
  "proposed — waiting for your go", "blocked by your limits", "failed", "started by the
  Steward", "finished — the agent's last message"; buttons **enable**, **open**, **accept**,
  **run anyway**, **dismiss**, **snooze** (until the next working day 08:00). Since 0.20.4:
  the header pill **N need you**; each item shows its age ("5 min ago") and "updated 5 min ago"
  when a later turn refreshed it; **dismiss** on a proposal, a proposed rule, a blocked or a
  failed item asks **why? — the Steward reads it tomorrow** with one-tap chips **already done** /
  **not now** / **not needed** / **wrong premise** plus **or in your own words…**, then
  **dismiss** / **back** (a finished or started item dismisses at once); a group of two or more
  finished, failed or blocked items offers **dismiss all** ("Dismiss all N items of this
  group?"). The Steward line at the top: **Steward: planning…**, **Steward: last plan 3 h ago ·
  7 min · 46 turns · $1.23**, **Steward: no plan yet** or the error in red; **plan now** when
  idle, **cancel** while it runs. See [automations.md](https://robion.app/docs/automations/).
- **BOARD** (`#/board`, the **board** tab): your cards as three sections **todo** / **doing** /
  **done** (done shows the last week, the rest folds into **+ N older**); each card with its
  project's colour, the first line of its note, the project, **from the Steward** / **from an
  agent**, and the state of the task that came out of it (**task running**, **task waits for
  you**, **task finished — review**, **task idle**, **task closed**). **new card…** at the top:
  type a title and a **note (optional)** and the project picker (**no project** allowed) appear,
  then **add**. A tap on a card opens it as a sheet: title, note, "project · column · origin ·
  created N ago", the **ticket** link when it mirrors one; buttons **start a task** (Claude Code
  in the card's project with the card as its first prompt; the card follows the task to doing;
  **a task needs a project — edit the card** without one), **open task** once it has one,
  **→ doing** / **→ done** / **→ todo** (no drag on a phone), **edit** (title, note, project;
  **save** / **cancel**), **delete** ("Delete this card?" → **delete** / **cancel**), **close**.
- **AUTOMATIONS**: every rule with its switch ("N rules, M on"); rules are written by an agent
  on the Mac. Since 0.20.4 each rule also has **run now** (**running…** while in flight; the
  result lands in the rule's line as **last 12:40 · ran** / **blocked** / **failed — <reason>**
  / **dry run**, "no runs yet" before the first), and switching one off asks **why off?
  (optional)** → **switch off** / **back**; a paused rule shows **off: <reason>** under its name.
- **CONTROLS**: the same cockpit as the desktop **CONTROLS** panel, per project: the
  **project** and **task** sections, run buttons ("run", "started"), send buttons ("sent"),
  values; actions the phone cannot do say "Mac only"; agent-defined command probes run only
  after **allow**. "no controls — ask the agent on the Mac to define some".
- **⚙ (SETTINGS)**, the **settings** tab: **server** (host and connection state), **tailnet**
  login, **version** ("server X · phone Y" with **reload the app** for when the update banner
  did not show); **servers** (since 0.20.17, only when the Mac relays other servers): each
  relayed server with its state and version, and the line "a server paired in ⚙ → Servers on
  the Mac shows up here with its projects; its pushes come through this server"; **appearance on this phone** (since 0.20.16): **theme** = **server** (follow
  the Mac's ⚙ → Appearance, the default) / **system** (this phone's OS setting) / **dark** /
  **light**, and **text size** = **smaller** / **normal** / **larger** — both stored on the
  phone only, so the phone can be light in daylight while the Mac stays dark; **limits**
  (since 0.20.16): each subscription window (**5 hours**, **7 days**, per-model windows by
  name) as a bar with "n % · resets 16:30" (a weekday when further away), the Claude
  **account**, and "last known values — the refresh failed" / "no Claude sign-in on the
  server" / "the limits are not available" when that is the case; **notifications**; the
  **automations** button; the install steps; **unpair this phone**.

## New task and new project

- **new task** under a project: a **task title**, an optional "first instruction for the
  agent" (it gets it right after start) and **own worktree** ("the task gets its own worktree;
  main stays yours"). The switch is pre-set from **⚙ → project → New task by hand: own
  worktree** on the Mac.
- **new project** (since 0.9): the kind (**Workshop** — "I want to make a thing: on the Mac the
  design panel leads" — or **Development**) and a **folder name**; Robion creates the folder
  with `git init` under the **Folder for new projects** set on the Mac (**⚙ → Application**).
  Without it: "first set 'Folder for new projects' on the Mac". A folder created this way is
  pre-trusted, so Claude Code does not stop at "do you trust this folder?" (since 0.12.11).

## The chat

- The conversation from the Claude Code transcript: **you** and **agent** rows, the agent's
  replies rendered as markdown, tool calls expandable (**output** / **hide**, "running…",
  "error"), edits as a red/green diff, "interrupted", "context compacted", **older messages**.
- **message the agent…** → **send**. The box grows for multi-line prompts and a half-typed
  draft survives the app being killed. Since 0.20.28 the message reaches the session through
  Robion's hooks module as a prompt of your own (no paste, no pause, no "pasted text" marker);
  a slash command, a shell pane or a session whose module is not listening still get it as one
  paste with Enter a beat later. iOS autocorrect and capitalization are on.
- A sent message shows at once as "sent · waiting for the agent · tap to hide" until the
  transcript has it; a busy agent takes it up at the end of its turn. Since 0.20.16 the bubble
  also goes when the agent's turn ends after the send, after 20 minutes at the latest, and a
  tap hides it by hand; a slash command (`/clear`, `/compact`) shows no bubble at all, Claude
  Code runs it without a chat row. Before, a prompt of 20+ characters (Claude Code wraps such
  a paste in `<pasted_content>` tags) or a slash command left the bubble hanging for good.
  While a permission is open the box says
  "the agent is waiting for an answer — allow or deny first"; while a question is open it
  hides under the question sheet.
- **attach image**: photo library, camera or clipboard paste; "N images" pending,
  "uploading images…". On Android, share a screenshot straight to Robion (share target) and
  pick the task. The files land in the task's `.robion/uploads/` and the prompt gets an
  `[Attached image: …]` line per file; the agent reads them itself.
- Pictures the agent shows: a markdown image in its reply (path relative to the working
  directory) or an `open_file` / `open_files` on a picture (PNG, JPG, SVG and other browser
  image types; a Gerber file renders as SVG). A tap opens it full screen: pinch zooms, one
  finger pans, a double tap zooms in and back out. **close the picture** returns.

- Since 0.20.28 the chat is fed by the session itself (Robion's hooks module inside Claude
  Code): a prompt, a tool row, a result or a reply shows the moment the session stores it, and
  the agent's reply appears while it is being written (plain text with a cursor, replaced by
  the rendered reply when it is done). The transcript file on disk only supplies the history.
- The chat holds what the terminal shows of the agent's words. A short note the agent leaves
  between tool calls (what it found, what it does next) is an **agent** row like a reply
  (since 0.20.34); Claude Code stores such a note as a thinking block with text, and before
  it was left out of the chat while the terminal showed it. Thinking blocks without text
  stay out.
- Coming back to a chat that moved on by more than a page (80 rows) while the phone was
  elsewhere or asleep, the chat shows the newest page whole and **older messages** loads the
  rest in order (since 0.20.34). Before, the rows from the last visit stayed in front of the newest page with
  a hole between them, the stretch in the hole could not be loaded, and a reply read while
  it was being stored could stay a second time at the top.
- Since 0.20.36 the chat ends with the newest row whatever the transcript's size. The file is
  read as it grows (the last 32 MB at first, then only what was written since); before, a 2 MB
  window at its end was all the file supplied, and a session whose transcript held many
  screenshots the agent looked at (half a megabyte a row) had most of its chat outside the
  window — those rows were taken for newer than the file and laid after its newest ones, so
  the chat ended with an old tool call while the terminal showed the reply (since 0.20.28).
  **older messages** reaches back as far as that first window.
- While the agent works and no reply is streaming yet, a dim "the agent is working…" bubble
  with three pulsing dots sits at the foot of the chat (since 0.20.30) — the header says
  "working" too, but the bubble is where you look after sending a message. The streamed reply
  takes its place.

## Side questions (the aside tab)

- The **aside** tab (since 0.20.35) asks about the task beside its conversation while the
  agent keeps working: type into **side question…** → **ask**. A fork of the session answers
  from what the conversation already holds, with no tools; neither the question nor the answer
  is written into the agent's conversation, the chat or the terminal. What it knows and what
  it cannot do is the same as on the desktop ([tasks.md](https://robion.app/docs/tasks/) → Side questions).
- It has its own prompt line, so it also works while the agent waits for a permission or
  holds a question for you (the chat's prompt line is hidden then). "answering…" usually takes
  a few seconds; a dot pulses on the **aside** tab meanwhile, so you can go back to the chat.
- The thread is the one the desktop's **side question** window shows (last 20 questions, gone
  when the task closes or the server restarts); the bin button clears it.
- No answer, each with **ask again**: "This session cannot take side questions (Robion's hooks
  module is not running in it) — restart it."; "Nothing to ask about yet: the agent has not
  replied in this conversation."; "No answer came in time."; "The answer failed.".

## The agent's question (AskUserQuestion)

- "the agent asks": a sheet of cards. A single choice sends on the tap of its chevron; several
  questions or a multi-select ("pick everything that applies") are marked first and sent with
  the **send answer** button that stays in view under the list. **own answer…** is a card
  that turns into a text field.
- **answer in the terminal** at the foot of the sheet (since 0.20.24) opens the **terminal**
  tab: the same dialog as text, with the key bar (1–3, Enter) to answer it there.
- Since 0.20.28, while nobody on the Mac shows the pane, the question is held inside the
  session for as long as a permission would be (the status line reads "robion: the question is
  on the phone"): the phone's answer becomes the tool's result and no dialog is drawn.
  Otherwise the answer is typed into the task's terminal as keystrokes, so the server reads the screen
  first (since 0.20.24). When the dialog is not there — the session that asked runs in another
  client, such as the Claude app or Remote Control continuing this conversation as a fork —
  the phone says "the question is not in this task's terminal — the session that asked runs
  in another client; answer it there" and the cards stay until that client answers. Before,
  such a question showed as an empty "the agent asks for permission" card whose **allow**
  typed `1` into the terminal ([troubleshooting.md](https://robion.app/docs/troubleshooting/)).
- A plan (plan mode) shows whole, with the terminal's real choices: **approve + auto-accept
  edits**, **approve (manual approvals)**, **keep planning** with optional feedback.
- A choice in a dialog (elicitation) is Mac only: "the agent waits for a choice in a dialog".

## Permission prompts on the phone

- "the agent asks for permission" with the whole request — the command, or the file and the
  replace/with text of an edit, a pattern, a URL. Buttons: **allow**, **allow and don't ask
  again**, **deny**, **deny with a reason** ("why / what to do instead" — the agent gets it).
- Several requests can be open at once: subagents working in parallel each ask on their own
  (the main agent asks one thing at a time). The card shows the oldest — the one Claude Code
  shows on top in the terminal — with "1 of N" after the tool name; **allow** / **deny** answer
  that one and the next moves up. A tap that arrives after its request was answered elsewhere
  is refused ("that permission request has been answered already — the pane shows the next
  one") instead of landing on the next request.
- Under the buttons: "the server holds the hook — the answer goes straight to Claude Code" or,
  after the hold expired, "the prompt is already on screen — the answer is sent as keystrokes".
- The **permission mode** chip switches the session: **manual** (every edit and command asks
  first), **accept edits**, **plan mode** (read-only), **auto** (a model reviews permissions).

## The terminal tab and the key bar

- The text of the screen ("last N lines", **more history**, **refresh**) — reading, not a
  shell. **keys for the terminal**: Esc, Tab, ⇧Tab, arrows, ⏎, ^C, 1–3 — for the dialogs
  Claude Code draws itself: the folder trust check, a login, an update prompt.
- A freshly launched Claude Code that stays silent for 10 seconds gets a hint in the chat:
  "Claude Code has not reported yet — it is probably waiting for an answer in the terminal".
- "Robion's hooks module did not load in this session (Claude Code x.y.z)" (since 0.20.28):
  the session started without Robion's plugin; the task's state stays unknown and the chat
  stays empty until it is restarted from Robion ([troubleshooting.md](https://robion.app/docs/troubleshooting/)).

## Task state on the phone

- The same reading as the desktop: "working", "waiting for you", "waiting for a prompt" (idle —
  the agent finished and waits for your next message; it read "waiting for input" before
  0.20.27, which sounded like a question), "done — not reviewed" (a finished turn nobody looked
  at), "error", "session ended". A task
  whose agent waits on a background shell reads "working · ⏳ shell still running: <command>"
  until that shell's result has been handled (since 0.20.27; [tasks.md](https://robion.app/docs/tasks/)).
- A task an automation or the Steward started carries a **Steward** tag. A worktree task shows
  its branch; **Merge into main** (since 0.20) opens a sheet with **squash**, **fast-forward**,
  **merge commit** or **pull request** and names the blockers first: uncommitted changes in the
  worktree, "the agent is still working", files also changed in main; "main has moved —
  update the worktree first" (the update is done on the Mac).

## What the phone cannot show, and limits

- No desktop panels: no file editor, tree, 3D, media, LOG or web preview, no diffs beyond the
  **changes** tab. What the agent wants you to see on the phone must be a picture it opens
  (`open_file`) or a markdown image in its reply; only pictures reach the chat this way.
- Projects are created only under the new-projects folder; adding an existing folder, defining
  controls, writing automation rules and resolving a dialog choice happen on the Mac.
- Controls actions marked "Mac only" (opening a file or URL on the desktop) do nothing here.
