A minimal, secure web GUI for the pi coding agent.
The server only binds to 127.0.0.1 – it is never reachable from the internet.
You access it by forwarding a port through your existing SSH connection:
ssh -L 8080:localhost:8080 root@your-vps
Then open http://localhost:8080 in your browser. Authentication is provided entirely by your SSH key – no passwords, tokens, or TLS certificates required.
Add this to ~/.ssh/config on your local machine so the tunnel opens automatically every time you SSH in:
Host your-vps
HostName <your-vps-ip>
User root
IdentityFile ~/.ssh/your_key
LocalForward 8080 127.0.0.1:8080
Browser tabs (localhost:8080)
│
│ SSH tunnel
│
└─► pi-remote-web-ui server (127.0.0.1:8080) ← binds here only
│
├─► AgentSession (in-process, shared across all tabs)
│ Uses pi SDK directly — no subprocess spawning
│ tools: bash, read, edit, write …
│
└─► static files (dist/)
A single AgentSession instance runs inside the server process.
All browser tabs share the same conversation — events are broadcast to every
connected client in real time. When a new tab connects, it receives the
full conversation history via a state_sync message.
This follows the pi SDK recommendation
for Node.js applications to use AgentSession directly rather than spawning
pi --mode rpc subprocesses.
cd ~/dev/pi-remote-web-ui
npm install
npm run build # builds frontend → dist/
npm run build:server # compiles server → dist-server/# Option A – run compiled server
node dist-server/index.js
# Option B – dev mode (Vite HMR + tsx watch)
npm run devThen SSH in with port forwarding and open http://localhost:8080.
sudo cp pi-remote-web-ui.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now pi-remote-web-ui
sudo systemctl status pi-remote-web-uiView logs:
journalctl -u pi-remote-web-ui -f| Action | How |
|---|---|
| Send message | Type and press Enter |
| New line in message | Shift+Enter |
| Abort generation | Click Stop button |
| New session | Click + New in the header (resets for all tabs) |
| Expand tool output | Click any tool card |
| Expand thinking | Click the 💭 Thinking block |
Note: All connected tabs share the same session. Starting a new session or sending a prompt from any tab affects every tab.
pi-remote-web-ui/
├── index.html Frontend entry point
├── src/
│ ├── main.ts Frontend app (TypeScript)
│ └── style.css Dark theme
├── server/
│ └── index.ts WebSocket + HTTP server (AgentSession in-process)
├── dist/ Built frontend (git-ignored)
├── dist-server/ Built server (git-ignored)
├── pi-remote-web-ui.service systemd unit file
├── vite.config.ts Frontend build config
├── tsconfig.json Frontend TypeScript config
└── tsconfig.server.json Server TypeScript config