copilot-mobile v1.0

Control gh copilot
from any device.

Share a running Copilot CLI session via a URL. Any browser — phone, tablet, second monitor — joins the same PTY and can type, scroll, and interact in real time.

on your machine

Run copilot-mobile

The CLI spawns gh copilot in a PTY and connects to the relay server as a host. A one-time session URL is printed to stderr.

relay server

Relay bridges the PTY

The relay holds the session. It forwards PTY output to every connected viewer and routes viewer keystrokes back to the host PTY.

on your phone

Open the share URL

A full terminal renders in the browser. Arrow keys, Enter, Ctrl+C — all inputs reach the running Copilot session live.

# Prerequisites: Node.js 20+, gh CLI, gh copilot extension

# install gh copilot extension
gh extension install github/gh-copilot

# install copilot-mobile
npm install -g copilot-mobile

# Usage

# via relay (default: copilot.tatsut.jp)
copilot-mobile --relay --password yourpassword

# local mode (no relay, direct port share)
copilot-mobile

# with custom relay server
copilot-mobile --relay wss://your.host/relay --password pw

# Persist config in ~/.config/copilot-mobile/config.json

{
  "defaultRelayUrl": "wss://copilot.tatsut.jp/relay",
  "password":        "yourpassword"
}

Per-session passwords

Each session requires a password set by the host. Passwords are hashed (SHA-256) before storage — the relay never holds plaintext.

Timing-safe comparison

Password checks use crypto.timingSafeEqual to prevent timing-based attacks on session IDs and passwords.

No session enumeration

The server returns identical errors for "session not found" and "wrong password", making brute-force enumeration impractical.

Rate limiting

WebSocket connection attempts are rate-limited per IP (10 per 60s). Excess requests receive HTTP 429.

Strict CSP headers

Content-Security-Policy, X-Frame-Options DENY, X-Content-Type-Options, and Referrer-Policy headers are set on all responses.

One-time sessions

Session IDs are cryptographically random (20 hex chars) and generated fresh on every launch. Sessions are destroyed on host disconnect.