
Use one session from several devices
A phone, tablet and laptop can watch one session at once, each at its own screen size.
Sessions that survive, on every device you own.

A phone, tablet and laptop can watch one session at once, each at its own screen size.

Find text anywhere in a session's scrollback, step between matches and see them highlighted in the terminal.
Ctrl+Shift+F

A session's row shows the program's own window title, such as an agent's current task.

Give a session a display name every device shows, or change its real tmux name.
Pencil icon on a session rowDisplay name / tmux nameSave

End a session after a confirmation, on every device that has it open.
Trash icon on a session row, or Kill in the rename sheet

One key press creates a session with a default name and opens it, without the naming dialog.
Ctrl+Shift+L

Switch straight to the first to ninth session in the sidebar with one key combination.
Ctrl+Shift+1 to 9
Files, browser tabs or changes open beside a session come back when you return to it.
A keyboard, a mic and gestures made for a terminal on a small screen.

Fix a dictated word before sending and the fix is applied next time; review or delete what it learned.

Shift+Enter adds a line break in an agent's input box instead of submitting it.
Shift+Enter
Tap the mic and speak; your own computer turns it into text, with no cloud service involved.
Mic key in the message box (phone key bar, laptop key pill)
Dictated file names, commands and flags are fixed from your screen and folder, so "pie test" becomes pytest.
Type or dictate a longer message in a box, edit it, then send it in one piece.
Message box under the terminal (always shown on touch; the mic key toggles it on a laptop)Send
Esc, Tab, Ctrl, Shift and arrow keys sit above the phone keyboard for driving terminal programs.
Key bar above the keyboard on the terminal screen; the arrows key expands the arrow row
Swipe up on a key-bar key for its second character: pipe, tilde, backtick, Delete, Home or Page Up.
Swipe up on a key-bar key
Pasting an image saves it on your computer and types its path for agents like Claude Code to read.
Ctrl+V over the terminal or message box with an image copied; long-press Paste on touch
Pick photos or documents on the phone and their paths on the computer go into your message, in order.
Message box+photo or file picker
Paste files copied in Finder or Explorer to upload them and insert their paths.
Ctrl+V or Cmd+V with files on the clipboard
A paste goes into the terminal or the message box depending on which paste control you used.
Long-press Paste (terminal), + attach (message box), Ctrl+V (wherever the cursor is)
Browse, edit and review what is on the computer without leaving the session.

On a laptop the file browser, browser and changes view sit beside the live terminal, not over it.
Folder key, globe key or Ctrl+Shift+G on a wide screen; each pane's cross closes it

Stack two panes beside the terminal, for example files above a browser.
Pane barMoreanother pane's name

Open any text file in a code editor with syntax colours and save it back to your computer.
Tap a text file in the file browser, or Edit in its action sheet

See the uncommitted and staged changes of the repository your terminal is in, beside the terminal.
Ctrl+Shift+G, or the pane More menuChanges

Markdown files open formatted, with tables, task lists and maths, and one tap switches to editing.
Tap a .md file; Edit in the reader's bar

In grid view, images, videos and PDFs show a small preview instead of a generic icon.

Look through the files of any branch of a repository without switching your working copy to it.
File browserbranch pop-up at the right of the path bar (inside a repo)

Stage, revert or unstage a single block of changed lines rather than the whole file.
Hover or tap a changed blockits tick, cross or curved arrow

Paths printed in the terminal become links that open the file or folder in the right viewer.
Tap or click a path printed in the terminal

Drag a file onto a folder, tile or breadcrumb to move it, even between two file browsers.
Drag a row onto a folder, tile or breadcrumb

Images and videos open in a viewer with pinch and double-tap zoom, panning and download.
Tap an image or video in the file browser or terminal; pinch, drag or double-tap inside the viewer

Browse your computer's folders, starting at home or at the folder your terminal is in.
Folder button on the session list (home), or the folder key in the key bar (session's folder)

Send files from your device into the folder on screen, with a prompt before replacing one.
File browser+Upload

Give a file or folder a new name in place.
Long-press (or right-click) a fileRename

The docked file browser keeps showing whatever folder the terminal beside it has moved into.

Make a new empty file, which opens in the editor, or a new folder.
File browser+New file / New folder

Download a folder as one zip file, built as it downloads.
Long-press a folderDownload
PDFs open beside the terminal on a laptop and in a new browser tab on a phone.
Tap a .pdf in the file browser or terminal
Open what your programs print: pages, dev servers, documents.

A browser pane shows what your computer can reach, such as dev servers and intranet pages.
Globe key in the laptop key pill

Sites that need a real login or break in the pane run in your computer's Chrome, streamed to you.
Browser address fieldpage-mode keyStream this site from the computer's Chrome
Tap a localhost address a dev server prints and it opens on your phone, no port forwarding needed.
Tap a http://localhost:PORT (or 127.0.0.1, 0.0.0.0, [::1]) link printed in the terminal
HTML files open as real pages in a new tab, isolated so they cannot reach your pairing code.
Tap an .html file in the file browser or a printed .html path
Pair phones and laptops, and switch between computers.

The installer finds Tailscale, prints the command that puts PocketTUI on your private network, and shows the address.
On the computer: curl -fsSL https://pockettui.com/install.sh | bash

An already paired device shows a QR code a second device scans, without going back to the computer.
SettingsConnectionPair another device (scan with the new device)

Pair one device with several computers and keep each one's address and code under its own name.
SettingsConnectionComputerAdd another computer

Start the server update from your device and watch it finish; older servers show the command instead.
SettingsAboutUpdate server, or the Update pill on the session list

When a command would fix a problem, the app types it at your prompt; you press Enter.
Type it for me on the connection-error card, or SettingsAbout after a failed update
Without Tailscale, the installer prints your computer's local network address for devices on the same Wi-Fi.
On the computer: the installer, when Tailscale is absent
A pockettui command on the computer updates, reports status and version, and installs the browser.
On the computer: pockettui update | version | status | browser
Badges and notifications when a session is waiting on you.

When a program asks yes/no or offers numbered choices, buttons for each answer appear above the keys.

Each session shows whether it is working, waiting for an answer, ready for your next message, or idle.

Turn on sound or silent alerts per session, so your phone tells you when it needs input or finishes.
Bell icon on a session row (cycles off, sound, silent)

A session that starts waiting while you are looking elsewhere keeps a dot until you open it.
Alerts reach your phone with the app closed when a session asks a question, finishes or rings the bell.
Turning on the first session's bell asks for permission
Themes, text size and keys set to your taste.

Pick one of twelve colour schemes for light mode and one for dark; the whole app follows them.
SettingsAppearanceTerminal palette (and Reset)

Paste or upload a scheme from iTerm2, Windows Terminal or xterm, and remove it later if you want.
SettingsAppearancePaste a scheme / Choose fileUse scheme; x beside Custom to remove

Turn on Vim keys in the editor; your .vimrc settings and mappings apply, and :w, :q, :wq work.
EditorVim

A list of every shortcut the app understands, with a short version in the sidebar.
SettingsKeys, or the Shortcuts card in the sidebar
Put an Alt key on the phone's key bar for programs that need it.
SettingsKeysAlt key
Diagnostics, reports and the plumbing that keeps it running.

Send a bug report with optional diagnostics straight to the PocketTUI team, even when your computer is unreachable.
SettingsAboutReport a problem, or the report key in the laptop key pill
Your device talks straight to your computer; sessions, keystrokes and files never pass through PocketTUI's servers.
Add the app to your home screen to run it full-screen like a native app.
ShareAdd to Home Screen (iPhone) or MenuInstall app (Android); reminder on setup step 2
A pretend terminal lets you try the app before installing anything.
Try demo on the session list when unpaired, or pockettui.com/demo
Nothing matches.
| What | Where | Notes |
|---|---|---|
| Install dir | ~/pockettui (or $POCKETTUI_DIR at install time) | install.sh:37 |
| Port | default 5560; the running value is in ~/pockettui/runtime.json | runtime.json = {version, pid, host, port, started_at}, written at start (mode 0644) and removed on a clean exit; a kill -9 leaves it behind, so check that pid is alive (app.py:11169-11196). The wrapper ~/.local/bin/pockettui also carries PORT="..." (install.sh:903). |
| Bind address | 0.0.0.0 by default (app.py:11241); the service unit passes only --port (install.sh:1381) | From the same machine, use http://127.0.0.1:<port>. The tailscale serve path (https://<host>.<tailnet>/pockettui) is for remote devices only. |
| Token | ~/pockettui/.token, 10 base32 chars + newline, mode 0600 (app.py:76, app.py:257-267, install.sh:1265-1298) | Readable by the agent: tmux is spawned by app.py (or already runs as the same user), so the pane runs under the same uid that owns the file. No env var carries it into sessions (new-session sets only ZDOTDIR, app.py:3161). |
| Header | X-PocketTUI-Token: <token> on every /api/* request (app.py:154, 507-539) | No loopback exemption. A server started with --no-auth (loopback bind only) accepts requests without it. |
Rules for the token:
401 {error:"bad token", hint} and counts toward the per-IP backoff (5 free tries, then doubling delays up to 300 s, app.py:296-368). Do not retry in a loop.RATE_SESSION_MUTATE, app.py:410).dev resolve to that device's own grouped view. An agent has no device name, so leave dev out and the base session is used.Minimal probe:
PORT=$(python3 -c 'import json;print(json.load(open("'"$HOME"'/pockettui/runtime.json"))["port"])' 2>/dev/null || echo 5560)
curl -s http://127.0.0.1:$PORT/.well-known/pockettui
curl -s -H "X-PocketTUI-Token: $(cat ~/pockettui/.token)" http://127.0.0.1:$PORT/api/version
GET /.well-known/pockettui: no token. Returns {"product":"pockettui","version":"<VERSION or "">"} and nothing else (no hostname, port, paths or capabilities). The phone and the installer use it as a reachability probe (app.py:1268-1283).
GET /api/version: token. Returns {"version": str, "host": socket.gethostname(), "capabilities": {flag: bool}} (app.py:1463-1490). The map is built once at import (server_capabilities(), app.py:1378-1462), so a Chromium or ffmpeg installed later shows up only after a service restart. A flag that is absent or false means the feature is not there.
| flag | meaning | value |
|---|---|---|
fs | /api/fs/* explorer and editor | always true |
image_paste | /api/image | true |
upload | /api/upload (composer "+") | true |
voice | /api/transcribe, /api/voice_status (the live engine is voice_status's answer) | true |
learned | /api/learned | true |
push | Web Push available | false without pywebpush |
dbg | /api/dbg | true |
git | /api/git/changes|diff|apply | true |
git_ref | /api/git/branches and ref= on /api/fs/list|read | true |
search | /api/search | true |
ping | attach socket answers {"type":"ping"} | true |
update | /api/update | false with no pockettui wrapper or no tmux |
pair_qr | /api/pair_qr.svg | true |
update_status | /api/update_status | true |
type | /api/session/type | true |
relay | /api/relay | true |
browse | /b/<tok>/... proxy | false without httpx |
browse_tab | POST /api/browse {mode:"tab"} | true |
bookmarks | /api/browse/bookmarks | true |
zip_dir | a folder at /api/fs/download streams a zip | true |
thumbs | /api/fs/thumb | false without ffmpeg |
browser_full | /ws/browser/<pane>, /api/browser/status | false without a Chromium |
pdf_thumbs | PDF page-one thumbnails | false without pdftoppm, gs or sips |
Nothing in this section is an API call: it is how the agent's printed output behaves on the user's device. Printing something never opens it; the user taps it.
08-links.js:13, trimPath :225)LOCAL_PATH_RE = /(?:^|[\s"'`(\[{<=:,])((?:~\/|\/)[^\s"'`()\[\]{}<>:;,]+)/g
/ or ~/, at the start of a line or right after whitespace, a quote, a backtick, (, [, {, <, =, : or ,. A bare logo.png or a relative src/app.py is not a link.<, >, :, ; or ,. So app.py:42 links only app.py, and a path containing spaces is cut at the first space. Trailing . ! ? ' " $ % # are trimmed, and a bare / is dropped.logicalLine, stitchedText).https?:// URL on the same row takes precedence over any path inside it.What a tap opens (activateLink 08-links.js:240, openEntry 28-file-explorer.js:1840-1866):
| printed path | opens |
|---|---|
.png .jpg .jpeg .gif .webp .svg .bmp .mp4 .webm .mov (FILES_MEDIA_RE) | full-screen image/video viewer |
.pdf | PDF view in the docked pane (laptop) or a new browser tab (phone) |
.md .markdown | rendered Markdown reader (tables, task lists, $…$/$$…$$ KaTeX); Edit swaps to the editor |
.html .htm .xhtml | rendered page in a new tab from a signed, sandboxed address; relative links to sibling files resolve |
| a directory | file explorer at that folder |
| any other file | CodeMirror editor (read-write; .vimrc honoured in Vim mode) |
A path that does not exist gets a "Couldn't find" toast. Media and PDF paths are not previewed at a git ref.
http://localhost:PORT and the relay (08-links.js:18, :320-408)URL_RE = /https?:\/\/[^\s"'`<>]+/gi; trailing . , ; : ! ? ' " and unbalanced closing brackets are trimmed.browse capability: every http(s) URL opens in the in-app browser pane, fetched by the computer, so http://localhost:PORT works there directly with no relay.browse): the URL opens in a new browser tab. If the host is localhost, 127.0.0.1, [::1], ::1 or 0.0.0.0, the phone first rewrites it to the computer's address as the phone knows it, then fires POST /api/relay {host, port} without waiting. The backend binds that port number on that address and forwards it to the loopback listener. It answers relayed, direct (something already listens there) or refuses (not_listening if nothing is on loopback at that port, bad_host for a loopback or wildcard target).http://localhost:<port> after the server is actually listening. It needs no API call. The relay binds on tap, not on print, and a server bound to 0.0.0.0 is reachable as printed.printf '\033]2;%s\033\\' 'Running tests'. The list strips leading non-alphanumeric glyphs and shows the rest. A title of the form user@host: /path, or equal to the hostname, is ignored and the folder name is shown instead (06-session-list.js:250-263, app.py:711-716).pane_current_path, the cwd of the pane's foreground process (pane_cwd, app.py:786-829). A cd typed at the shell moves it; a cd inside an agent's own tool subprocess does not. To point the user at a folder, print its absolute path.ssh, the title is read as user@host: /abs/path (everything after the first colon; ~ paths refused). A global tmux hook (pane-title-changed, TITLE_HOOK, app.py:741-775) copies every title that matches *@*: /* in an ssh pane into the pane option @ptui_remote_cwd, so a TUI that later overwrites the title does not erase it. The path is used only if it exists on this machine, directly or through POCKETTUI_PATH_REWRITES. For this to work, the remote shell must have drawn such a title in that folder before the TUI started.app.py:10170-10780)The watcher polls every 2 s. A session is active while its newest output is less than 6 s old (POCKETTUI_NOTIFY_IDLE_S). At the busy-to-idle edge it reads the visible pane once, if the busy episode lasted 10 s or more or a non-shell program still holds the pane. detect_prompt then checks the last 5 non-empty lines above an empty composer, in this order:
[y/n], (y/n) or yes/no → waiting, chips y n.^\s*│?\s*(❯\s*)?\d+[.)]\s (numbered menu, box border tolerated) → waiting, chips are the first four digits.❯ <text> with a non-empty line under it (unnumbered chooser) → waiting, no answer chips.do you want | would you like | proceed? | continue? | are you sure → waiting, chips y n.> or ❯, optionally after │) that is empty, or holds only dim (SGR 2) placeholder text → ready. Non-dim text in it → drafting, shown as idle and never notified.? → waiting, no answer chips.Chips always add Enter (\r) and Esc. A chip tap sends only that key, with no Enter after y or a digit, and the chips come down as soon as the pane prints again.
Notifications (Web Push and/or ntfy) go out only for sessions whose @notify is on or quiet. At most one goes out per 30 s per session, and a repeat of the same text is suppressed:
POCKETTUI_NOTIFY_READY_BUSY_S);printf '\a' notifies immediately, with no text guard, provided tmux sets the window's bell flag (tmux may not flag a bell in a window a client is currently viewing).For an agent to be detected correctly:
(y/n) or a numbered list (1. …, 2. …), then stop printing. Spinners, clocks or status lines that repaint keep the session active and defer detection.POST /api/notify only stores the per-session preference.~/.pockettui/images/paste-<stamp>-<hex>.<ext>. The type is sniffed from the bytes and the newest 30 are kept (app.py:8660-8735). The absolute path is typed at the user's cursor.~/.pockettui/uploads/<name>-<stamp>-<hex>.<ext>, newest 30 kept (app.py:8766-8837).~/.pockettui/uploads/browser/<hex>/ (app.py:8126). Streamed downloads: ~/.pockettui/downloads.All need X-PocketTUI-Token except /.well-known/pockettui. None of them changes what is on the user's screen.
GET /.well-known/pockettui (no token) -> {product:"pockettui", version}
GET /api/version - -> {version, host, capabilities:{<flag>: bool}}
GET /api/sessions - -> {sessions:[{name, created, attached, windows, command, title, cwd, alias, notify, state: active|waiting|ready|idle, last_activity}]}
POST /api/session {name, dir?} -> {session} | 400 {error} (name required, <=60 chars, no "." or ":", unique; dir defaults to ~)
POST /api/alias {session, alias} -> {session, alias} (display name only; "" clears)
POST /api/session/rename {session, name} -> {session} (real tmux rename; drops every device view)
POST /api/session/type {name, dev?, text} -> {session, chars} | 400 empty | 404 | 413 too_long (>4096) (typed with send-keys -l, \r and \n stripped, never submitted)
POST /api/notify {session, mode: off|on|quiet} -> {session, notify} (preference only; sends nothing)
POST /api/session/kill {session} -> {killed} (whole group; only on explicit user request)
GET /api/update_status - -> {state: <install.sh update-state.json> | null, session_alive: bool}
Prefer the CLI where one exists: pockettui update | version | status | browser install|check|status. Prefer plain tmux for reading your own pane.
Set your pane title with an OSC 2 (or OSC 0) escape, e.g. printf '\033]2;%s\033\\' 'Refactoring auth'; the row shows it on the next list refresh with leading symbols stripped. A title shaped like user@host: /path, or equal to the hostname, is ignored and the row shows the folder name instead.
Sessions named <device>-<session> (or <device>-<session>-2..9, or ptui-<session>) grouped onto a user's session are PocketTUI's per-device views; tmux ls lists them as grouped. Do not attach to, rename or kill them: they are recreated on every connect and swept when stale.
Sets the display name every device shows without renaming the session (an empty alias clears it), same as tmux set-option -t <session> @alias '<text>'. POST /api/session/rename {session, name} instead changes the real tmux name and drops every device's view of it, so use that only when asked.
POST /api/alias {session, alias} -> {session, alias}Only on the user's explicit request: kills the session and every device view grouped onto it, with no undo. A plain tmux kill-session on the base session would leave the device views alive.
POST /api/session/kill {session} -> {killed}Read the list with the pairing token to see every session's title, cwd and watcher state (active, waiting, ready, idle). It is read-only: nothing changes on the user's device.
GET /api/sessions - -> {sessions:[{name,created,attached,windows,command,title,cwd,alias,notify,state,last_activity}]}Creates a detached tmux session (name required, max 60 chars, no '.' or ':'; dir defaults to ~). It appears in the user's list on the next refresh but does not open on their screen; the sess-MMDD-HHMM default name is the app's, not the route's.
POST /api/session {name, dir?} -> {session}Run bash ~/pockettui/setup_voice.sh --parakeet, --whisper or --all (no prompts; piped with no flag it installs both). Nothing leaves the machine; the phone's Settings > Dictation rows turn available once /api/voice_status reports the engine.
An attached file is saved as ~/.pockettui/uploads/<name>-<stamp>-<hex>.<ext> (newest 30 kept) and its absolute path is typed into the user's message; images go through the image-paste path instead.
A pasted image is saved as ~/.pockettui/images/paste-<stamp>-<hex>.<ext> (type sniffed from the bytes, newest 30 kept) and that absolute path is typed at the user's cursor; read the file at that path to see the image.
Text the user sends from the box arrives in your pane as one bracketed paste followed by Enter, so a multi-line message is a single submission, never line by line.
Only for a pane whose foreground command is ssh: the remote shell must set its title to user@host: /absolute/path before any TUI takes over (tmux keeps the last such title in the pane option @ptui_remote_cwd). The path is used only if it exists locally, directly or through POCKETTUI_PATH_REWRITES; ~ paths are refused.
The pane follows tmux's pane_current_path, which is the cwd of the pane's foreground process: a cd typed at the shell moves it, but a cd inside an agent's own subprocess does not. Print the folder's absolute path instead so the user can tap it.
If you write a file the user has open in the editor, their next Save gets a conflict prompt (Overwrite or Reload) instead of silently replacing your change; nothing is shown to them until they press Save.
The pane lists the repository that contains the session pane's cwd, so edits you make there show up for the user to stage or revert per block; it refreshes by polling, you do not need to signal it.
Print absolute or ~/ paths (e.g. /home/u/out/report.md) and the user can tap them; printing does not open anything. The path must start at line start or after whitespace, a quote, ( [ { < = : or a comma, and stops at whitespace, quotes, brackets, : ; , so file.py:42 links only file.py; wrapped lines are stitched back together.
Write a .md file (tables, task lists, $...$ and $$...$$ maths, images by relative path) and print its absolute path; the user taps it and reads it rendered. The file's content can never run script in the reader.
Print the absolute path of an image or video you produced (png, jpg, jpeg, gif, webp, svg, bmp, mp4, webm, mov); the user taps it to view it full-screen. A printed .pdf path opens the PDF view, .md the rendered reader, .html a sandboxed page in a new tab, and anything else the editor.
Print http://localhost:PORT (or 127.0.0.1 / 0.0.0.0) for a server listening on loopback. On tap the phone rewrites the host to the computer's address and asks the backend (POST /api/relay {host, port}) to relay that port number to your loopback listener; printing alone opens and binds nothing, and nothing is relayed until the server is actually listening.
On a wide layout every http(s) URL you print opens in the in-app browser pane, fetched by the computer, so http://localhost:PORT works there directly with no relay.
If the user wants streamed pages, run pockettui browser install (then pockettui browser check / status) on the computer; the pane offers the mode once the server reports browser_full, which is frozen at server start, so the service must restart.
Write a self-contained .html report and print its absolute path; tapping opens it rendered in a new tab from a signed, sandboxed address, and relative links to sibling files resolve.
Print full http:// or https:// URLs; trailing punctuation and unbalanced closing brackets are trimmed. Printing does not open anything; the user taps it.
Types literal text at the prompt of a named session's active pane; every \r and \n is stripped so it never runs, and the user must press Enter. Use it to stage a command for the user in another session (max 4096 chars); it does not create a session and does not switch the user's screen.
POST /api/session/type {name, dev?, text} -> {session, chars}Linux: systemctl --user status pockettui and journalctl --user -u pockettui; macOS: the launchd agent logs to ~/pockettui/pockettui.log. Restarting the service keeps tmux sessions alive (KillMode=process).
Commands: pockettui update [--allow-downgrade], pockettui version, pockettui status, pockettui browser install|check|status. Installed at ~/.local/bin/pockettui; none of them touch the user's device directly.
Run pockettui update on the computer (--allow-downgrade to go back). The service restarts but tmux sessions survive; from the app the same update runs in a tmux session named pockettui-update, and GET /api/update_status reports {state, session_alive}.
Run pockettui version (or pockettui status) on the computer; GET /api/version returns the same server version with the capability map.
The code lives in ~/pockettui/.token (mode 0600, readable by the user the service runs as). Send it as the X-PocketTUI-Token header on /api/* calls; never print it into the terminal, since every device attached to the session would see it.
Set POCKETTUI_NTFY_URL (and optionally POCKETTUI_APP_URL for the tap-through link) in the service's environment and restart it; the same notifications as push go to that topic, lower priority in quiet mode.
A push fires only for sessions whose @notify is on or quiet, at most once per 30 s per session: when a question is detected, when an agent returns to an empty composer after at least 30 s of work, when a command of 10 s or more finishes back at the shell, or at once when the pane rings the terminal bell (printf '\a', subject to tmux flagging the bell). No route sends one.
State is read from your pane after output stops for 6 s: a y/n or yes/no question, 'do you want / would you like / proceed? / continue? / are you sure', two or more numbered options (1. 2.), or one ❯-marked choice over another line reads as waiting; an empty ❯ or > composer reads as ready; anything else is idle. There is no route to set it; end a question with a clear '(y/n)' or a numbered list and then stop printing.
A detected y/n question gets y and n chips, a numbered menu gets its first four digits, and an unnumbered chooser or trailing question gets only Enter and Esc (always added). A tap sends just that key to your pane, with no Enter after a digit or letter; the chips go away as soon as the pane prints again.
This only stores the session's notification preference (tmux option @notify: off, on or quiet), like tmux set-option -t <session> @notify on; it sends nothing. Change it only when the user asks.
POST /api/notify {session, mode: off|on|quiet} -> {session, notify}The editor reads ~/.vimrc when Vim mode is switched on and applies the set options and map/noremap lines it can translate; edits you make to ~/.vimrc apply the next time the user turns Vim mode on (plugins and functions are skipped).
No token needed; answers only product and version, no hostname, paths or capabilities. Use it to confirm the local backend (default http://127.0.0.1:5560) is up before an authenticated call.
GET /.well-known/pockettui (no token) -> {product:"pockettui", version}Read them as lines 'dbg[<device>] <text>' in journalctl --user -u pockettui (Linux) or ~/pockettui/pockettui.log (macOS); nothing is stored elsewhere and nothing arrives unless the user has switched Debug log on.
With POCKETTUI_VOICE_KEEP=<n> set, the newest n takes are kept in ~/pockettui/.voice_takes/<stem>.{orig,wav,json}; nothing in the app reads them back.
Read the capability map to learn which features this server has (a missing or false flag means absent): fs, image_paste, upload, voice, learned, push, dbg, git, git_ref, search, ping, update, pair_qr, update_status, type, relay, browse, browse_tab, bookmarks, zip_dir, thumbs, browser_full, pdf_thumbs. It is frozen at server start.
GET /api/version - -> {version, host, capabilities:{<flag>: bool}}Nothing matches.
| # | method | route | class | reason |
|---|---|---|---|---|
| 1 | GET | / | infrastructure | serves the self-hosted app shell |
| 2 | GET | /.well-known/pockettui | documented integration | unauthenticated reachability and version probe |
| 3 | GET | /manifest.json | infrastructure | PWA manifest for home screen |
| 4 | GET | /sw.js | infrastructure | service worker, cache-busted per start |
| 5 | GET | /icon.svg | infrastructure | static app icon file served |
| 6 | GET | /icon-{size}.png | infrastructure | static PNG icons for PWA |
| 7 | GET | /vendor/{name} | infrastructure | bundled third-party JS libraries |
| 8 | GET | /api/version | documented integration | version, hostname and capability map |
| 9 | GET | /api/pair_qr.svg | internal transport | QR embeds token; device-only |
| 10 | GET | /api/sessions | documented integration | session list with watcher states |
| 11 | GET | /api/session_cwd | internal transport | explorer asks device view cwd |
| 12 | POST | /api/alias | documented integration | set shared display name safely |
| 13 | POST | /api/session/kill | documented integration | kill group; explicit request only |
| 14 | POST | /api/session/rename | documented integration | real rename; drops device views |
| 15 | GET | /api/voice_status | internal transport | dictation settings read engines |
| 16 | POST | /api/transcribe | internal transport | phone audio to local STT |
| 17 | POST | /api/learn | internal transport | composer records dictation edits |
| 18 | GET | /api/learned | internal transport | Settings lists learned corrections |
| 19 | POST | /api/learned_delete | internal transport | Settings deletes learned corrections |
| 20 | POST | /api/session | documented integration | create detached session by name |
| 21 | POST | /api/session/type | documented integration | stage command, never submits |
| 22 | POST | /api/update | internal transport | app button; agents use CLI |
| 23 | GET | /api/update_status | documented integration | read last update verdict safely |
| 24 | GET | /api/file | infrastructure | header-authed media bytes by path |
| 25 | GET | /api/fs/list | internal transport | explorer listing; agent has shell |
| 26 | GET | /api/fs/read | internal transport | editor load; agent has filesystem |
| 27 | POST | /api/fs/write | internal transport | editor save with hash guard |
| 28 | POST | /api/fs/mkdir | internal transport | explorer new-folder action only |
| 29 | POST | /api/fs/rename | internal transport | explorer rename and drag-move |
| 30 | POST | /api/fs/delete | internal transport | explorer delete, non-recursive dirs |
| 31 | POST | /api/fs/upload | internal transport | device file into folder |
| 32 | GET | /api/fs/download | internal transport | file or zip to device |
| 33 | GET | /api/fs/download_link | internal transport | mints signed download URL |
| 34 | GET | /api/fs/signed_download | infrastructure | signed link, no token header |
| 35 | GET | /api/file_link | internal transport | mints signed media viewer URL |
| 36 | GET | /api/signed_file | infrastructure | signed media link for tags |
| 37 | GET | /api/fs/thumb | internal transport | grid thumbnails via ffmpeg/poppler |
| 38 | GET | /api/fs/render_link | internal transport | mints sandboxed HTML page URL |
| 39 | GET | /api/fs/site/{exp}/{sig}/{root}/{rest} | infrastructure | signed sandboxed rendered pages |
| 40 | GET | /api/git/changes | internal transport | diff pane's changed-file list |
| 41 | GET | /api/git/branches | internal transport | explorer branch picker entries |
| 42 | GET | /api/git/diff | internal transport | one file's diff for pane |
| 43 | POST | /api/git/apply | internal transport | stage/revert blocks from pane |
| 44 | POST | /api/search | internal transport | find bar drives copy-mode |
| 45 | POST | /api/relay | internal transport | fired by tap; print URL |
| 46 | POST | /api/browse | internal transport | mints browser pane proxy token |
| 47 | GET | /api/browse/bookmarks | internal transport | pane bookmarks bar contents |
| 48 | PUT | /api/browse/bookmarks | internal transport | replaces whole bookmark list |
| 49 | GET | /api/browser/status | internal transport | streamed Chrome state for Settings |
| 50 | POST | /api/browser/reset | internal transport | Settings clears Chrome profile |
| 51 | POST | /api/browser/upload | internal transport | streamed page file-input upload |
| 52 | GET/POST/… | /b/{tok}/{sch}/{hostport}/{rest} | infrastructure | reverse proxy for pane pages |
| 53 | GET/POST/… | /b/{tok}/{sch}/{hostport} | infrastructure | reverse proxy, bare host form |
| 54 | GET | /b/{tok}/enter | infrastructure | tab entry clearing site data |
| 55 | WS | /b/{tok}/{sch}/{hostport}/{rest} | infrastructure | proxied page WebSockets, pathed |
| 56 | WS | /b/{tok}/{sch}/{hostport} | infrastructure | proxied page WebSockets, bare |
| 57 | POST | /api/image | internal transport | clipboard image staging for paste |
| 58 | POST | /api/upload | internal transport | composer attachment staging for paste |
| 59 | POST | /api/dbg | internal transport | device debug lines to log |
| 60 | WS | /ws/browser/{pane} | internal transport | streamed Chrome frames and input |
| 61 | WS | /ws/attach/{session_name} | internal transport | the terminal PTY bridge itself |
| 62 | GET | /api/push/status | internal transport | push setup question from app |
| 63 | POST | /api/push/subscribe | internal transport | stores browser push subscription |
| 64 | POST | /api/push/unsubscribe | internal transport | drops browser push subscription |
| 65 | POST | /api/notify | documented integration | sets preference, sends nothing |