Quick start
Install the plugin, run the setup wizard, and pair your phone in a few commands — then send your first prompt from the app. The full walkthrough, including the mobile side, is a tutorial.
What it does
Remote Pi adds two independent layers on top of Pi. The agent network lets agents discover and message each other — over a local socket on one machine, or through the relay across PCs. The mobile control plane is your phone: it authenticates new peers into the mesh and drives sessions. Each is covered hands-on:
- Local mesh — agents discovering and messaging on the same machine.
- Remote mesh — routing between agents on different PCs.
- Getting started — pairing your phone and driving an agent from it.
Install
Requirements: Node 20+ and Pi (the host coding agent). Remote Pi installs as a Pi plugin with pi install npm:remote-pi, which self-registers the /remote-pi slash command and deploys the agent-network skill. The complete setup — wizard, pairing, first command — is in the tutorial.
→ See the Getting started tutorial. Daemon mode has its own one-time install — see Daemon mode below.
Using /remote-pi
/remote-pi is the everyday entry point. The first run opens a short wizard (agent name, whether to use the relay) that creates the per-folder config; later runs join the local mesh and start the relay automatically. Re-run the wizard with /remote-pi setup. Every subcommand is in the command reference.
→ See the Getting started tutorial for the guided flow.
Pairing a mobile device
/remote-pi pair prints a QR (and a copy-paste URI); scan it with the Remote Pi app. Pairing is per machine — once a device is paired, every Pi process on that machine accepts it. Manage devices with /remote-pi devices and /remote-pi revoke <shortid> (see the command reference).
Quick actions from the phone
Beyond chatting, the app drives a session with a small set of typed actions — compact context, new session, set model, and set thinking level. The model picker reads live from the host, so it always reflects what that machine can run. The full vocabulary, wire format, and fallback semantics live in PROTOCOL.md (the App actions section).
Agent network
Each agent is a peer in a mesh. The LLM gets three tools — list_peers (who is online), agent_send (send with an ACK), and get_messages (drain the inbox). On one machine, peers talk over a Unix-domain-socket broker; across machines, the same agent_send routes through the relay, addressing remote peers as pc_label:peer. Both paths are covered hands-on:
- Local mesh — the broker, the three tools, a concrete exchange.
- Remote mesh — cross-PC addressing and what an ACK does (and doesn't) guarantee.
Daemon mode
Promote a folder to a 24/7 background agent: run /remote-pi install once per machine to install the supervisor (launchd on macOS, systemd --user on Linux) and link the CLI, then remote-pi create <folder> --name "…" to register and start a daemon. One supervisor per machine, N daemons under it. Every command is in the command reference below.
→ See the Daemon mode tutorial for the full how-to; the why (and how it compares to all-in-one platforms) is Why Pi.
The relay
The relay is the only network-touching piece of Remote Pi. In the current MVP it sees both message payloads (forwarded but never logged or inspected by the community operator) and connection metadata: which keypair is online, which room/cwd identifiers exist, message timing, sizes. Traffic is encrypted in transit (TLS) and peers authenticate with Ed25519 pairing, but payloads are not encrypted at the application layer — see Protocol & Security and the Privacy Policy, section 9, for the full picture.
The relay also persists a small SQLite table called mesh_versions — blobs signed by your Owner key listing the Pi devices that belong to your mesh (a few KB per Owner). The relay verifies the Ed25519 signature on every POST /mesh/<owner_pk_hash> and stores what you signed; it never decides membership itself. New devices restoring your Owner key recover their peer list from this blob. A relay compromise means DoS, not impersonation. See the PROTOCOL.md mesh-membership section for the wire format.
You have two options.
Option A — Use the community relay
https://relay-rp1.jacobmoura.work (default). Zero setup. Good for trying things out or for casual use. (Internally the extension uses the WebSocket form wss://… — both schemes point at the same endpoint.)
Caveats:
- Shared infrastructure — availability is best-effort.
- TLS in transit is the only network protection — the relay operator sees plaintext envelopes (payloads, routing metadata, peer pubkeys, timing). Self-host for confidentiality from the operator.
- No IP allow-listing or VPN gating built in. Anyone with a paired keypair can connect; layer a VPN on top via Option B if you want network-level isolation.
Option B — Self-host (recommended for privacy)
Run the relay yourself in Docker and put it behind a VPN like Tailscale, WireGuard, or your own VPC. Because the relay's network-level protection is just TLS + keypair authentication, layering a VPN on top means only your devices can even reach the WebSocket port — defense in depth.
Quick Docker outline (see the relay README for the full setup, environment variables, and reverse-proxy guidance):
docker run -d \ --name remote-pi-relay \ -p 3000:3000 \ -v remote-pi-data:/data \ --restart unless-stopped \ jacobmoura7/remote-pi-relay
The -v remote-pi-data:/datamount is required — that's where the relay keeps mesh.db (the Owner-signed mesh blobs). Skip the volume and the table is wiped on every container restart, forcing every client to re-publish.
The relay serves the WebSocket upgrade, /health, and /mesh/<owner_pk_hash>on the same port (default 3000) — point your reverse proxy at one upstream and you're done. Use /health for liveness probes (Coolify, Kubernetes, Fly health checks).
Bind the container to your VPN interface, terminate TLS in a reverse proxy, and point both your Pi and your phone at the resulting https://… URL.
Pointing Pi at your own relay
Once your relay is reachable, tell the extension:
/remote-pi set-relay https://relay.yourdomain.tld
The URL must be http:// or https:// — wss:// / ws:// are rejected at validation. The extension converts to the WebSocket form internally when it opens the connection, so you can paste whatever URL your reverse proxy or PaaS dashboard exposes.
This writes ~/.pi/remote/config.json with { "relay": "..." }. Resolution order (highest precedence first):
REMOTE_PI_RELAYenvironment variable (CI / one-off overrides)~/.pi/remote/config.json- The built-in default (
https://relay-rp1.jacobmoura.work)
Verify the active URL and its source with:
/remote-pi config
To switch URLs while connected: /remote-pi stop then /remote-pi again. The mobile app has its own relay-URL setting in its preferences pane — keep both pointing at the same relay.
Protocol & Security
The canonical spec for everything wire-level — envelope format, identity model (Owner key + per-device subkeys), ACK protocol, cross-PC routing, mesh membership, trust model, and failure modes — lives in PROTOCOL.md on GitHub. It is the source of truth that the Pi extension, the mobile apps, and the relay all implement against. Read it when you need exact behavior or when writing a new harness adapter.
Security posture, in short. Connections to the relay are encrypted in transit (TLS), and devices authenticate each other with Ed25519 pairing, so paired peers verify identity cryptographically. Message payloads are not encrypted at the application layer — the relay operator could in principle read plaintext in memory while forwarding. If you need confidentiality from the relay operator, self-host the relay behind a VPN. The Privacy Policy, section 9, restates this in plain language, and PROTOCOL.md is the deep dive that matches the code.
Command reference
Every command works as a Pi slash command (interactive) and as a shell-level remote-pi <subcommand> when the package is installed globally (npm install -g remote-pi).
Local session — one Pi, one terminal
| Command | Description |
|---|---|
/remote-pi | Connect (join local mesh + start relay), or run setup on first use |
/remote-pi setup | Run the setup wizard and update local config |
/remote-pi status | Show local mesh + relay status |
/remote-pi peers | List local and cross-PC mesh peers, grouped by PC label |
/remote-pi stop | Stop everything for this terminal (mesh + relay) |
/remote-pi pair | Show QR + copy-paste pairing URI for a new mobile device |
/remote-pi devices | List paired mobile devices (online/offline per device) |
/remote-pi revoke <shortid> | Revoke a paired device by its shortid |
/remote-pi set-relay <url> | Persist a new relay URL (http:// or https://) |
Daemon fleet — one supervisor, N background Pis
See Daemon mode for the overview and the Daemon mode tutorial for the full how-to.
| Command | Description |
|---|---|
/remote-pi create <cwd> [--name X] | Register a folder as a daemon (starts it when the supervisor is running) |
/remote-pi remove <id> | Unregister a daemon (local config preserved) |
/remote-pi daemons | List registered daemons + state |
/remote-pi daemon start | Start every registered daemon |
/remote-pi daemon stop | Stop every running daemon (/remote-pi stop stops only the local terminal) |
/remote-pi daemon restart | Stop + start all daemons |
/remote-pi daemon status | Detailed runtime status (pid, uptime, restart count) |
/remote-pi daemon send <id> "<text>" | Send a prompt to a specific daemon |
/remote-pi install | Install pi-supervisord as a system service and symlink the remote-pi CLI into ~/.local/bin/ |
/remote-pi uninstall | Remove the system service and the ~/.local/bin symlinks (daemon registry preserved) |
The footer in the Pi TUI reflects state live:
📡 local (N)— local mesh session and peer count🟢 relay— relay connected, at least one device paired on this machine🟡 relay waiting for pairing— relay connected, no device paired yet📱 <shortid>— a mobile device is actively connected right now
The window title is two parts — <agent-name> · On when the relay is up or <agent-name> · Off otherwise — so you can tell your terminal tabs apart at a glance.
Configuration files
| Path | Scope | What's in it |
|---|---|---|
<cwd>/.pi/remote-pi/config.json | Per-directory | agent_name, auto_start_relay |
~/.pi/remote/config.json | Per-user | relay URL |
~/.pi/remote/peers.json | Per-machine | Paired mobile devices |
~/.pi/remote/daemons.json | Per-machine | Daemon registry (list of { cwd } entries) |
~/.pi/remote/identity.json | Per-machine | Pi-secret fallback when the OS keyring is unavailable (headless Linux). Stored with chmod 0600. See PROTOCOL.md for the keyring details. |
~/.pi/remote/sessions/local/ | Per-machine | Broker socket + audit.jsonl |
~/.pi/remote/skills/agent-network/SKILL.md | Per-user | Agent skill the LLM reads |
Override the relay for a single run without persisting:
REMOTE_PI_RELAY=https://staging.example.tld pi
Only http:// / https:// are accepted — wss:// / ws:// are rejected at validation, the extension converts to the WebSocket form internally when it opens the connection.
Troubleshooting
Footer says 🟡 relay waiting for pairing even though I paired a device
The icon reflects whether any device has been paired on this machine, not whether one is connected right now. If you really have a paired device in /remote-pi devices, restart Pi — the cache may be stale (fixed in current release; report a bug if it recurs).
Mobile app times out connecting
Verify the same relay URL is configured on both sides. If you self-host behind a VPN, your phone must also be on the VPN (Tailscale on iOS/Android works fine).
Reply never arrives
agent_send returned { status: "received" } but no reply ever lands in your inbox. Possible causes:
- Receiver crashed or never processed. Run
/remote-pi peersto see whether the peer is still online. - The receiver chose not to reply.
agent_sendis not RPC — there's no obligation to respond. If the conversation needs a reply, the prompt to the receiver must say so explicitly. - Cross-PC: peer went offline. Look in your inbox for a
transport_errorenvelope withre=<your-send-id>— the relay returns one when a forwarded message can't be delivered. ADeliveredACK only means the remote broker accepted the envelope, not that the peer is alive — validate by roundtrip.
Note: agent_request is deprecated (still available as a wrapper for backward compat, emits a warning). New agents call agent_send and observe the inbox in a future turn.
Two Pi processes can't share a directory
A cwd lock allows one Pi process per directory. If you try to run /remote-piin a second terminal that's already in the same folder, the second start is rejected (and the relay, separately, refuses a duplicate room with RoomAlreadyOpenError).
To run two agents side by side: put them in two different directories — each gets its own workspace and both meet in the same local session. See the Local mesh tutorial.
If you actually wanted a second terminal at the same workspace (e.g. just to read state), stop the running Pi first or open a shell that does not launch Pi.
Links
- Homepage: remote-pi.jacobmoura.work
- Tutorials: hands-on guides
- Source: github.com/jacobaraujo7/remote_pi
- Protocol spec: PROTOCOL.md
- Pi coding agent: github.com/earendil-works/pi
- Relay (self-hosting guide): relay/README.md
- Issues / bugs: github.com/jacobaraujo7/remote_pi/issues
License: MIT.