Skip to content

cs01/termpair

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

163 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TermPair

View and control remote terminals from your browser with end-to-end encryption

termpair share command

termpair browser view

Why TermPair?

  • Zero-knowledge sharing -- your terminal is end-to-end encrypted (AES-128-GCM). The server is a blind relay that never sees your data.
  • One command to share -- run termpair share, send the link. Anyone with the link watches your terminal live in their browser.
  • Let others type -- viewers can type in your terminal from the browser, or set --read-only to keep them watching.
  • Public or private -- private sessions are encrypted and link-only. Public sessions (--public) are listed on the landing page for anyone to discover and watch.
  • Survive server restarts -- if the server goes down, both sides automatically reconnect and resume where they left off.
  • Single binary, no deps -- one static binary bundles the server, client, and web frontend. No Node, no Python, no Docker required.
  • Works anywhere -- Linux, macOS, Windows. Share any terminal app: your shell, vim, htop, Claude Code, anything with a TTY.

Installation

Quick Install

curl -fsSL https://raw.githubusercontent.com/cs01/termpair/main/install.sh | sh

Installs to ~/.local/bin. Customize with environment variables:

INSTALL_DIR=/usr/local/bin sh    # custom install directory
VERSION=v1.2.0 sh               # specific version

GitHub Releases

Download a prebuilt binary from the releases page. Available for Linux (x86_64, aarch64), macOS (x86_64, Apple Silicon), and Windows (x86_64).

Build from Source

git clone https://github.com/cs01/termpair.git
cd termpair/termpair-rs
cargo build --release
cp target/release/termpair ~/.local/bin/

Usage

Start the server:

termpair serve

Share your terminal:

termpair share

This prints a URL containing a unique terminal ID and encryption key. Share it with whoever you want to give access. Anyone with the link can access your terminal while the session is running.

By default, termpair share runs your $SHELL. The server multicasts terminal output to all connected browsers.

How it Works

┌─────────────────┐                                    ┌─────────────────┐
│                 │    encrypted terminal output        │                 │
│  Terminal       │───────────────────────────────────▶│  Browser(s)     │
│  (termpair      │         ┌───────────────┐          │  (xterm.js +    │
│   share)        │◀────────│  Server       │─────────▶│   Web Crypto)   │
│                 │         │  (blind relay) │          │                 │
│  - forks pty    │    encrypted browser input          │  - decrypts     │
│  - encrypts I/O │         │  never sees   │          │    output       │
│  - manages keys │         │  plaintext    │          │  - encrypts     │
│                 │         └───────────────┘          │    input        │
└─────────────────┘                                    └─────────────────┘

The server is a blind relay -- it routes encrypted WebSocket messages without access to keys or plaintext. The terminal client forks a pty, encrypts all output with AES-128-GCM, and decrypts browser input. Browsers decrypt and render with xterm.js + Web Crypto API.

Encryption

Three AES-128-GCM keys are created per session:

  1. Output key -- encrypts terminal output before sending to the server
  2. Input key -- encrypts browser input before sending to the server
  3. Bootstrap key -- delivered via the URL hash fragment (never sent to the server), used to securely exchange keys #1 and #2

Keys are rotated after 2^20 messages. IVs are monotonic counters to prevent reuse.

The browser must be in a secure context (HTTPS or localhost).

Deployment

NGINX

upstream termpair_app {
  server 127.0.0.1:8000;
}

server {
    server_name myserver.com;
    listen 443 ssl;
    ssl_certificate fullchain.pem;
    ssl_certificate_key privkey.pem;

    location /termpair/ {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_pass http://termpair_app/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

systemd

# /etc/systemd/system/termpair.service
[Unit]
Description=TermPair terminal sharing server
After=network.target

[Service]
ExecStart=/usr/local/bin/termpair serve --port 8000
Restart=on-failure
RestartSec=1s

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable termpair.service
sudo systemctl restart termpair

TLS

Generate a self-signed certificate:

openssl req -newkey rsa:2048 -nodes -keyout host.key -x509 -days 365 -out host.crt -batch

Then pass it to the server:

termpair serve --certfile host.crt --keyfile host.key

CLI Reference

$ termpair serve [OPTIONS]
  -p, --port <PORT>          port to listen on [default: 8000]
      --host <HOST>          host to bind to [default: localhost]
  -c, --certfile <CERTFILE>  path to SSL certificate for HTTPS
  -k, --keyfile <KEYFILE>    path to SSL private key for HTTPS

$ termpair share [OPTIONS]
      --cmd <CMD>     command to run [default: $SHELL]
  -p, --port <PORT>   server port [default: 443]
      --host <HOST>   server URL [default: https://chadsmith.dev/termpair]
  -r, --read-only     prevent browsers from typing
  -b, --open-browser  open the share link in a browser
      --public        public session (no encryption, read-only)

See Also

sharemyclaude -- share your Claude Code session with a browser. Built on termpair, with a public relay server so you can share instantly without self-hosting.

About

View and control terminals from your browser with end-to-end encryption 🔒

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors