conchv0.1.8

Using conch

Phone

conch web lets a phone reach the agents on this machine: see what each is doing, reply to one, and answer the question it is waiting on. It is a small web server that conch runs on your laptop, in front of the same server the TUI talks to. Nothing goes through a service of conch's; the phone reaches the laptop over Tailscale.

Notifications are built but haven't been seen on a real phone yet. On an iPhone they need iOS 16.4 or later and conch added to the Home Screen.

What you need

  • Tailscale on both. The phone reaches the laptop over your tailnet, so install Tailscale on the phone as well and sign in to the same account. There is no relay and no service of conch's in between: the two talk directly, and a phone that is not on the tailnet cannot reach the gateway at all.
  • tailscale serve, once, to put HTTPS in front — phones need it, and the next section says why.
  • conch running on the laptop. conch web starts the server itself if it is not up.

Without a tailnet there are two other ways — a certificate of your own (-cert/-key) or any tunnel that terminates HTTPS in front of a loopback gateway (-url). Both are below under Without Tailscale.

Start it

The simplest way is from the TUI: ⚙ Settings → Web. Set the address phones open — the https://…ts.net name tailscale serve gives this computer — and, if you like, the port; then Start conch web. It runs in the background, with no window of its own, and keeps running when the TUI closes; Stop conch web there, or conch web stop, ends it. Put Tailscale's HTTPS in front of it once: tailscale serve --bg http://127.0.0.1:8722. The settings are saved in config.toml:

[web]
url = "https://laptop.tail1234.ts.net"
port = 8722

From a terminal it is the same:

tailscale serve --bg http://127.0.0.1:8722        # Tailscale's HTTPS in front of the gateway
conch web -url https://laptop.tail1234.ts.net      # listens on 127.0.0.1:8722, for tailscale serve
conch web pair            # a QR code and a one-time code for the phone — or P in the TUI

conch web starts conch's server if it isn't running, and keeps running in the foreground; stop it with ctrl+c. It is a client of that server like the TUI is, so agents keep running whether or not the gateway does.

It is reachable only over your tailnet. A paired phone can type into your agents, so this is remote access to your machine. With -url, tailscale serve is in front and conch web listens on 127.0.0.1 only: the Tailscale app can't proxy back to the machine's own tailnet address (on macOS it gets "connection refused"), and nothing on the tailnet should reach the plain-http port directly. Without -url it listens on the Tailscale address, and with no Tailscale address it refuses to start. -listen ADDR names another address (with a warning unless it is loopback or Tailscale's); -port N changes the port.

Phones need HTTPS. The device's token travels in a cookie marked Secure, which a browser only sends over HTTPS. tailscale serve puts HTTPS in front with a certificate for your machine's tailnet name; tell conch that address with -url, so the QR code points phones there rather than at the plain address it listens on, and so pages from it count as the gateway's own. Without either, conch web prints the two commands to run, with your machine's tailnet name filled in. Pairing from a plain http:// page is refused with that reason — the browser would pair and then drop the key — except on the laptop itself (localhost), which browsers treat as secure. Or give conch a certificate yourself with -cert FILE -key FILE (tailscale cert makes one).

The app

The gateway serves the app itself, so it always matches your conch build; there is nothing to install from a store. It is plain HTML, CSS and ES modules with no framework and no build step, deflated as it is served — about 34 KB for a cold start, which matters on a phone that is on mobile data rather than at home. The live connection is deflated too, with the window kept between messages: a pane's frame is the whole screen as styled text and the next one is that screen with a few rows changed, so a burst of them crossed the wire about fifteen times smaller when measured end to end, and cost less processor time a frame than compressing each on its own. Open the gateway's address in the phone's browser. Add it to the Home Screen (Safari: Share → Add to Home Screen; Chrome: Install app) and it opens like an app, without the browser's bars. It is laid out like the Claude app: warm colours, one conversation at a time, everything else a tap away.

  • Inbox — what needs you, first. An agent waiting on a question shows the question and its choices right there: tap one to answer without opening anything. Below it, what finished, what is working, what is idle, and your terminals.
  • ☰ the drawer — every agent and terminal, by project, as Claude lists its chats; + New and Settings at the ends. When the device reaches more than one machine, they are grouped by machine as the tree is: this computer first, then the others, each with its projects under it, and a dot saying whether it answers. A machine with nothing running on it is listed all the same — it is the one thing a list made of panes cannot show, and leaving it out reads as the phone having lost it. With one machine there are no machine headings at all. A machine whose own conch is older than this one says what that costs (“replies need a newer conch there”), since each machine is reached with the conch installed on it: the capabilities the app needs are asked of every machine, not of this computer once. Trying it anyway is refused with the machine named and conch machine upgrade NAME to mend it.
  • An agent — a conversation: its state, the question it asks with its choices as buttons, its output, and a composer pinned to the bottom. Scroll the output up to read back. An agent with a full-screen view — Claude Code, Codex — keeps its own history, as at the laptop: scroll to the top of the box and keep pulling down (or turn the mouse wheel, or tap the line at the top) and the agent scrolls back through its conversation; at the bottom, pushing up brings it forward. Because it is the agent's own view, the laptop sees it scrolled too. For anything else — a shell — earlier output loads above, as far back as the pane keeps. Back at the bottom it follows along again. While it works and the box is empty, the send button becomes ■ stop (Esc, as at the laptop; needs full). A reply is held back while it waits on a question, and it says why.
  • Its terminal — the whole screen, live, fitted to what the keyboard leaves visible so the prompt and the key row stay in view. In a window with room to spare — a browser on a laptop, rather than a phone — conch asks for the pane itself to be made wider, since the page can otherwise only grow its type and the columns are the laptop's. It only ever asks to grow: a phone never shrinks the pane your laptop is working in, and a pane already as wide as the window is left alone. A terminal has one size, so the laptop sees the change too, and if its TUI is also showing that pane it will set the size back to its own — the last one to look wins, which is the rule the TUI has always had. Devices with view cannot resize, because it changes what everybody sees. Tap it and type: the phone's keyboard goes straight to the pane, accents, paste and all. A row of keys rides on top of the keyboard — esc, tab, ctrl and alt (tap once for the next key, twice to lock), the arrows, shift+tab, | ~ / -, and ^C (two taps). Aa changes the text size; a wide terminal scrolls sideways, and scrolling up loads its history. A plain terminal opens straight on its screen.
  • Your other machines — the gateway reaches every machine in your catalog, not only the one it runs on, keeping a client per machine as the TUI does. A row on another machine carries its name beside the agent's, and nothing is drawn for the laptop itself or when there is only one machine: the machine is only ever news when there is more than one. A machine still being reached, or one that would not answer, shows above the lists with what is wrong (ssh: connect to host vm1 port 22: Host is unreachable), so an empty list is an empty list rather than a phone quietly missing half of them. Notifications come from every machine, and + New asks which machine to start on.
  • ⋯ on an agent or terminal — switch between conversation and terminal, rename it, or close it (two taps).
  • + (devices with full) — a new task (branch, worktree and agent), a new agent in a project or your home folder, with an optional first message, or a new terminal.

When the laptop can't be reached — asleep, or off the tailnet — the app still opens, says so, and shows the agents and terminals as they were when it last heard from the laptop, with the time. It keeps trying and catches up by itself. Questions are not kept on the phone between visits: they can quote a command.

Pair a phone

conch web pair                       # full: everything, typing into terminals included
conch web pair -permission reply     # see agents, reply, answer questions — no typing
conch web pair -permission view      # look only
conch web pair -machine busybox      # and let it reach that machine, not only this computer

Pairing says where the device will reach as well as what it may do, because a phone that reaches this computer alone is the thing people are surprised by: a machine it was not given is not shown to it at all. -machine pairs it for one in the same breath; without it, conch web permission ID PERM -machine NAME adds one afterwards.

This prints a QR code and a six-digit code. Point the phone's camera at the QR code: it opens conch with the code filled in, and one tap pairs it. Or open the gateway's address on the phone and type the code. Either way the phone gets a token of its own, kept in a cookie the page's scripts can't read.

In the TUI, click 🌐 in the status bar (beside ⚙ Settings), or press P in the tree, for the same: a QR code and a code, for a phone with full. v, r and f give a new code for view, reply or full. The dialog draws over the screen, so its text can't be selected: click the code to copy it, or anywhere else in the dialog to copy the pairing link (c and y do the same). The QR code needs about 37 columns by 17 rows, so it shows in an 80×24 terminal; a smaller one shows the code and the address alone. It pairs with this computer's conch web, whichever machine is selected in the tree. The dialog also says whether conch web is running; if it has run before and isn't now, s starts it again in the background, with the same flags as last time — no terminal to keep open — logging to web.log in conch's directory. conch web stop ends it.

The code travels in the link's fragment (#code=…), which a browser never sends to a server, so it is in no log.

  • A code works once, for 5 minutes. Asking for a new one cancels the old.
  • Guesses are limited: a few attempts a minute, and five wrong codes cancel the one outstanding.
  • conch keeps only a hash of each token, in phone.json in its directory. The file alone doesn't let anyone in.
PermissionThe device can
viewsee the agents, their state and their screens
replyalso send an agent its next message, pick an answer to the question it asks, and resize a pane it has open
full (default)also type into terminals, stop an agent, start tasks, agents and terminals, rename and close them

The permission is checked on every request and on every message over the live connection, not only when the phone connects.

And where: a phone reaches this computer only

A permission says what a device may do. It does not say where, and the gateway can reach every machine in your catalog — over ssh, with your own credentials. So a device reaches this computer and nothing else until you name a machine for it:

conch web permission d_7h2k full -machine busybox          # and this computer, always
conch web permission d_7h2k full -machine busybox -machine gpu-1
conch web permission d_7h2k full -machine local            # take them away again

A phone without a machine is not merely unable to act on it: it is never told the machine exists. It is left out of the machine list, out of the agent and pane lists, out of hello, and out of notifications — a push names a pane and taps through to it, so one about a machine the device may not see is not sent. A request naming it is refused before conch says whether there is such a machine, so asking is not a way to learn what is in your catalog.

The reason is that conch cannot tell a test box from a production one, and a machine is reached as you. Pairing a phone is one decision; letting it into a machine is another, taken on purpose, one machine at a time. Devices paired before conch could reach machines keep this computer alone — nobody's phone gained reach because conch was updated.

Giving or taking away a machine applies at once, on connections already open: a phone loses a machine mid-session without being paired again, and gains one without reconnecting.

Devices

In the TUI, ⚙ Settings → Web → Devices… opens a panel of every paired phone and browser: its name, ID, what it may do, the machines it reaches and when it was paired. v, r and f set what the selected one may do, x twice revokes it, esc goes back. A change applies to the device's next request at once.

From a terminal:

conch web devices        # ID, name, permission, the machines it reaches, when it was paired
conch web revoke d_7h2k  # cut one off
conch web permission d_7h2k full   # let it type into terminals, without pairing again
conch web permission d_7h2k full -machine busybox   # and let it reach a machine

permission without -machine leaves the machines as they were: what a device may do and where it may do it are two decisions, and changing one does not quietly change the other.

Revoking takes effect at once: the token stops working, and a running gateway closes the connections that device has open — within a quarter of a second — telling the phone why.

An agent that conch keeps to its own work may not run conch web pair or revoke: a paired phone acts as you, not as the agent.

Replying and answering

These are two different things, on purpose.

A reply is the agent's next message, as conch agent prompt sends it. It is refused while the agent waits on a question — typed in, its Enter would answer the question — and the phone is told the agent is waiting.

An answer picks one of the choices of the question an agent is asking. conch reads the choices off the agent's screen — a numbered menu with a cursor on one row — and the phone shows them as buttons. Tapping one moves the cursor to that row and presses Enter; nothing else is sent. The answer carries the ID of the question it was shown for, and is refused if the agent is no longer asking that question, so a tap that arrives late can't answer the next one.

When conch can't read choices — no menu, or no cursor to steer by — the question comes without any. A device with full answers it in the terminal, with the key bar; the others at the laptop.

Review comments

Comments written on a branch's diff — in the changes view at the laptop, with conch review add, or by a reviewing agent — show on the card of the agent working on that branch, with ✎ 2 on its row in the list. Send to the agent hands them all over as one message, as S does at the laptop, and needs what a reply needs: reply or full. Like a reply, it is refused while the agent is waiting on a question, and the comments stay until it can take them. The phone shows and sends comments; writing them needs the diff, which is at the laptop. The unresolved comments on the branch's pull request are on the card too, with the reviewer's name, the thread's replies and Open on GitHub to answer them there.

Without Tailscale

Phones need HTTPS, so something has to provide it. Three ways, in the order they are worth trying:

  1. Any tunnel in front of a loopback gateway — Cloudflare Tunnel, ngrok, a VPS with Caddy. conch does not care which: conch web -listen 127.0.0.1 -port 8722 -url https://conch.example.com, with the tunnel forwarding to that port. -url is the same seam tailscale serve uses: it tells conch what phones will open, so the QR code and the cookie's origin agree. This is the one that works away from your network.
  2. A certificate of your own — conch web -listen 0.0.0.0 -port 8443 -cert FILE -key FILE -url https://192.168.1.23:8443. The work is on the phone, not in conch: it has to trust the certificate, which for a local CA (mkcert) means installing the root on the phone and turning full trust on. Your LAN only.
  3. An SSH port forward from the phone, if its SSH app can hold one: conch web -listen 127.0.0.1, forward the port, and open http://localhost:8722 on the phone. Browsers treat localhost as a secure context, so the cookie and the service worker work with no certificate anywhere. It lasts as long as the forward does.

-listen on anything but loopback or a Tailscale address warns, deliberately: a paired phone can type into your agents, so you are publishing control of this machine and the pairing code is what stands in the way.

What it doesn't do yet

  • Showing the question in a notification: there is no setting for it, so it is never shown.
  • A list of the agents installed on the laptop: the new-task form offers every agent conch knows, installed or not.

What is logged

One line per request and per message: its kind, its status and its size. Never a screen, a reply, a question or a token.