Skip to main content

Mirror a terminal to your phone — E2E encrypted, peer-to-peer, no daemon

Project description

TerminalSync

Mirror a terminal to your phone — E2E encrypted, peer-to-peer, no daemon.

Run terminalsync in any terminal tab. Scan the QR code with your phone. See your terminal, live, in your browser. Close the tab and everything shuts down — no background process, no cloud relay, no stored keys.

Built for leaving AI coding agents (Claude Code, etc.) running at home while staying able to observe and intervene from anywhere.

How it works

  1. terminalsync allocates a PTY, runs your shell, and starts a WebSocket server on an ephemeral port.
  2. A QR code is printed in the terminal encoding the endpoint and cryptographic pairing data.
  3. Scan it with your phone's browser. A full X25519 key exchange happens — both sides derive a shared key that never leaves their devices.
  4. Your terminal is mirrored to the phone in real time, encrypted with XSalsa20-Poly1305.
  5. When the terminal tab closes, the process exits, keys are zeroed, and the phone's session drops.

Zero servers. Zero daemons. Zero persistent keys.

Install

pipx install terminalsync

Or with pip:

pip install terminalsync

Or via the install script:

curl -fsSL https://raw.githubusercontent.com/lucarp/terminalsync/master/install.sh | bash

Or from source:

git clone https://github.com/lucarp/terminalsync
cd terminalsync/cli
pip install -e .

Usage

# In any terminal tab:
terminalsync

# Scan the QR code with your phone — open the printed URL in mobile Safari/Chrome
# Accept the self-signed certificate warning
# Your terminal appears in the browser

Options

Option Default Description
--host auto-detect LAN IP IP/hostname to advertise in the QR code
--port random ephemeral Port to listen on
--command $SHELL Command to run inside the PTY
--public-url Public URL for QR (ngrok, cloudflared, SSH tunnel). Overrides --host/--port in QR.

Environment variable TERMINALSYNC_HOST overrides --host.

Connecting from your phone

Same Wi-Fi (LAN)

Works out of the box. terminalsync auto-detects your LAN IP and prints it in the QR code.

The first time you open the URL, your phone's browser will show a self-signed certificate warning — tap Advanced → Proceed to accept it. The WebSocket will connect on the same certificate.

Tailscale (recommended for remote)

Tailscale is free for personal use. Install it on your computer and phone, then:

terminalsync --host $(tailscale ip -4)

Your phone connects directly to your computer over the Tailscale network — no intermediate server, no extra setup.

ngrok / cloudflared (remote, no VPN)

# Terminal 1 — expose local port via ngrok
ngrok http 12345

# Terminal 2 — start terminalsync pointing at the ngrok URL
terminalsync --port 12345 --public-url https://YOUR-SUBDOMAIN.ngrok-free.app

When --public-url is set, terminalsync binds plain HTTP locally and lets the tunnel handle TLS. The QR code encodes the tunnel URL so your phone connects through ngrok.

The same pattern works with cloudflared:

cloudflared tunnel --url http://localhost:12345
terminalsync --port 12345 --public-url https://YOUR-SUBDOMAIN.trycloudflare.com

SSH tunnel (remote, own server)

If you have a VPS:

# Forward a port on the server back to your local machine
ssh -R 12345:localhost:12345 user@your-server.com

terminalsync --port 12345 --public-url http://your-server.com:12345

Local browser testing (no phone)

terminalsync --port 12345 --public-url http://localhost:12345

Binds plain HTTP, no certificate warning — open the printed URL directly in your browser.

Why not just SSH?

SSH opens a new session. terminalsync mirrors the session already running — the one with your AI agent mid-task, your build in progress, your REPL state intact. Without terminalsync you'd need tmux or screen to share a running process. terminalsync does it with a QR scan, no extra setup required.

You can also work on your computer and phone simultaneously — both see and interact with the same live terminal.

Security

Property Implementation
Key exchange X25519 (Curve25519 ECDH)
Symmetric encryption XSalsa20-Poly1305 via libsodium/PyNaCl
Key derivation HKDF-SHA256 over (DH output ‖ PSK)
Transport TLS with per-invocation self-signed cert
Cert trust Fingerprint pinned in the QR code
Verification 12-digit Short Authentication String (compare on both screens)
Key storage Process memory only — zeroed on exit
Attack surface No open port when not actively sharing

TerminalSync refuses to run detached: it checks isatty() on stdin/stdout and rejects execution if its parent is PID 1. There is no daemon mode.

Requirements

  • Python 3.11+
  • macOS or Linux
  • Phone with a modern browser (Safari, Chrome)

Roadmap

  • Phase 0: PTY + WebSocket validation
  • Phase 1: Full encryption, QR pairing, web client
  • Phase 2: Native iOS/Android app (React Native, Keychain/Keystore vault, push notifications)
  • Phase 3: Optional zero-knowledge relay for NAT traversal without Tailscale

Contributing

Issues and PRs welcome. See docs/architecture.md for design decisions and protocol details.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

terminalsync-0.1.1.tar.gz (24.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

terminalsync-0.1.1-py3-none-any.whl (19.7 kB view details)

Uploaded Python 3

File details

Details for the file terminalsync-0.1.1.tar.gz.

File metadata

  • Download URL: terminalsync-0.1.1.tar.gz
  • Upload date:
  • Size: 24.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for terminalsync-0.1.1.tar.gz
Algorithm Hash digest
SHA256 e779a8730c41f52298cc27a34ae458248141867b891daee7f61c74fdbc9b234b
MD5 240b205971007ca841df92ba722caa24
BLAKE2b-256 8bcf2ae61973f6b6b820a34c0e38866bbb6a6cc37a1221f02716d12be614b778

See more details on using hashes here.

File details

Details for the file terminalsync-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: terminalsync-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 19.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for terminalsync-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0620c97f660f38f8fb927550b6080a388f8b8152f5654e793d064c70c3876915
MD5 c13b311f7265c400c3a1fe77698a665c
BLAKE2b-256 b510a98c18ff608e7725ed20e347f0803b143858b7fabe50243fdb92b4a249be

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page