$ cat ~/map/pi-remote.md
pi-remote
Phone remote control for pi: one Python process wraps pi --mode rpc — self-hosted, relay-free, built for native Windows.
tools✓ Sep 15
Facts
- repository
- github.com/jotapemj/pi-remote ↗
- GitHub Stars
- ★ 10
- Forks
- 0
- license
- MIT
- npm / mo
- —
- last push
- Oct 11, 2026
install
pip install -r requirements.txt, run python pi_web_bridge.py from the repository with the static folder next to it, and open the machine's port 8770 in a browser (over Tailscale, use the tailnet name)## TL;DR
A self-hosted remote control for the pi coding agent: one Python bridge wraps pi --mode rpc and serves a phone-friendly web app over a tailnet, built for native Windows with no relay and no third party in the path.
## Key points
- $Architecture: the browser speaks WebSocket to pi_web_bridge.py, which spawns pi --mode rpc and owns its stdin and stdout over JSONL; agent events stream back as messages while prompt, steer, and abort commands go the other way, and a prompt or steer can carry attached images for a model with vision.
- $Why it exists: the README's comparison table faults pi-web with WSL-only operation, remote-pi with needing a relay, pi-remote-control with a hosted relay, and Telegram bridges with the transcript leaving home; this bridge keeps everything on your own machines, and it also documents leave-it-running setups for Windows (startup folder, Task Scheduler — a real service via NSSM or pywin32 is documented but not recommended), Linux (systemd user service), and macOS (launchd).
- $Recommended topology: an always-on Linux box runs llama.cpp serving a local model over HTTP, the Windows PC holds the code and runs pi plus the bridge, and the phone is a thin client over Tailscale — though everything can run on one machine and the inference endpoint can be any OpenAI-compatible server.
- $Security model: a token is always required (one is generated at startup when PI_WEB_TOKEN is unset, and PI_WEB_TOKEN=off drops the bridge to read-only, refusing prompts, shell, and project opening at the server, not just by disabling buttons); the WebSocket checks Origin, the page ships a strict CSP with nosniff and no-referrer, and the token is compared in constant time — still a single-user tool whose port must stay inside the tailnet.
- $Permissions: pi ships no prompts of its own, so a guardrails extension supplies the dialogs, and over RPC only the four standard dialog kinds (select, confirm, input, editor) reach the browser — an extension drawing with ctx.ui.custom() gets undefined, and the bridge can neither see nor answer it; pi-guardrails is recommended with its pathAccess feature turned off, because that guard's prompt uses ctx.ui.custom() and would otherwise deny file access outside the working directory silently.
- $Projects are working directories holding pi's existing sessions, and the transcript is rebuilt from pi's own get_messages so the history is the session on disk, not an in-memory copy; removing a session moves the jsonl file to a _trash folder beside it because pi has no delete over RPC.
- $The rest: twenty-three slash commands filtered as you type (/bash skips the model and the guardrails; /restart relaunches the bridge via a one-shot scheduled task outside the process tree), a Functions page with show-reasoning, response suggestions, summary-on-stop, local notifications, and smooth generation, PWA install over HTTPS via tailscale serve, and a fake_pi.py harness that exercises the whole surface without a model in headless Chrome.
## FAQ
Q Can the browser attach to a pi session already running in a terminal?
No — a process has one stdin, so the bridge spawns its own pi --mode rpc. What is shared is the session file on disk: a session started in the terminal can be opened in the browser and the other way round, one writer at a time.
Q Is it safe to expose the bridge port to the internet behind a token?
No. The README's caution is explicit: this is a single-user tool, and whoever can reach the port and holds the token can run commands on your machine — there is no sandbox. Keep the port inside your tailnet.
Q Why do pi-guardrails file-access prompts never show up?
That guard draws its prompt with ctx.ui.custom(), which does not exist over RPC — the call returns undefined, which is read as deny, so every access outside the working directory is refused silently. Disable that one feature in guardrails.json (pathAccess false) or set its mode to allow; the command gate keeps working because it uses select.
## References
## Related entries
$ Pi Chat
Minimal local browser chat for pi: streaming replies, persistent sessions, and in-pi launching via /pi-chat-start.
$ Pi Studio
Local web UI for pi: project folders, searchable history, streaming replies, and live token and cost meters.
$ vagusPI
Web GUI sharing the pi CLI's ~/.pi/agent config — chat, sessions, models, and packages from your browser.
## Guides
Last updated: Sep 15, 2026