Documentation

Remote Pi docs

Last updated: 2026-05-31License: MIT

This is the reference. Remote Pi is a mesh for coding agents: agents on the same machine talk through a local UDS broker, agents on different machines reach each other through an open-source relay, and your phone authenticates new peers and drives sessions. The first supported harness is the Pi coding agent; /remote-pi wires everything up. To learn by doing, start with the tutorials; for why it works this way, see Why Pi. The pages below are for looking things up.

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.

See the Getting started 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).

See the Getting started tutorial.

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).

See the Getting started tutorial.

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):

On your relay host — bash
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:

In Pi — text
/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):

  1. REMOTE_PI_RELAY environment variable (CI / one-off overrides)
  2. ~/.pi/remote/config.json
  3. The built-in default (https://relay-rp1.jacobmoura.work)

Verify the active URL and its source with:

In Pi — text
/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

CommandDescription
/remote-piConnect (join local mesh + start relay), or run setup on first use
/remote-pi setupRun the setup wizard and update local config
/remote-pi statusShow local mesh + relay status
/remote-pi peersList local and cross-PC mesh peers, grouped by PC label
/remote-pi stopStop everything for this terminal (mesh + relay)
/remote-pi pairShow QR + copy-paste pairing URI for a new mobile device
/remote-pi devicesList 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.

CommandDescription
/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 daemonsList registered daemons + state
/remote-pi daemon startStart every registered daemon
/remote-pi daemon stopStop every running daemon (/remote-pi stop stops only the local terminal)
/remote-pi daemon restartStop + start all daemons
/remote-pi daemon statusDetailed runtime status (pid, uptime, restart count)
/remote-pi daemon send <id> "<text>"Send a prompt to a specific daemon
/remote-pi installInstall pi-supervisord as a system service and symlink the remote-pi CLI into ~/.local/bin/
/remote-pi uninstallRemove 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

PathScopeWhat's in it
<cwd>/.pi/remote-pi/config.jsonPer-directoryagent_name, auto_start_relay
~/.pi/remote/config.jsonPer-userrelay URL
~/.pi/remote/peers.jsonPer-machinePaired mobile devices
~/.pi/remote/daemons.jsonPer-machineDaemon registry (list of { cwd } entries)
~/.pi/remote/identity.jsonPer-machinePi-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-machineBroker socket + audit.jsonl
~/.pi/remote/skills/agent-network/SKILL.mdPer-userAgent skill the LLM reads

Override the relay for a single run without persisting:

Shell — bash
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

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 peers to 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_error envelope with re=<your-send-id> — the relay returns one when a forwarded message can't be delivered. A Delivered ACK 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.