An open API service indexing awesome lists of open source software.

https://github.com/lamngockhuong/termote

Remote control CLI tools (Claude Code, GitHub Copilot, any terminal) from mobile/desktop via PWA
https://github.com/lamngockhuong/termote

cli remote-control terminal tmux ttyd

Last synced: 3 months ago
JSON representation

Remote control CLI tools (Claude Code, GitHub Copilot, any terminal) from mobile/desktop via PWA

Awesome Lists containing this project

README

          


Termote


Release
CI
License
GHCR
Docker Pulls


Go
React
TypeScript
PWA

Remote control CLI tools (Claude Code, GitHub Copilot, any terminal) from mobile/desktop via PWA.

> **Termote** = Terminal + Remote
>
> [TiαΊΏng Việt](README.vi.md)

## Features

- **Session switching**: Multiple tmux sessions with create/edit/delete
- **Mobile-friendly**: Virtual keyboard toolbar (Tab/Ctrl/Shift/arrows, expandable)
- **Gesture support**: Swipe for Ctrl+C, Tab, history navigation
- **PWA**: Installable to homescreen, offline-capable
- **Persistent sessions**: tmux keeps sessions alive
- **Collapsible sidebar**: Desktop UI with toggleable session sidebar
- **Fullscreen mode**: Immersive terminal experience
- **Config persistence**: Auto-save installation settings with AES-256 encrypted password

## Screenshots


Mobile Terminal
Β Β 
Session Sidebar

## Architecture

```mermaid
flowchart TB
subgraph Client["Client (Mobile/Desktop)"]
PWA["PWA - React + TypeScript"]
Gestures["Gesture Controls"]
Keyboard["Virtual Keyboard"]
end

subgraph Server["tmux-api Server :7680"]
Static["Static Files"]
Proxy["WebSocket Proxy"]
API["REST API /api/tmux/*"]
Auth["Basic Auth"]
end

subgraph Backend["Backend Services"]
ttyd["ttyd :7681"]
tmux["tmux"]
Shell["Shell"]
Tools["CLI Tools"]
end

Gestures --> PWA
Keyboard --> PWA
PWA --> Static
PWA <--> Proxy
PWA --> API
Auth -.-> Static & Proxy & API
Proxy <--> ttyd
API --> tmux
ttyd --> tmux --> Shell --> Tools
```

## Quick Start

> πŸ“– **New to Termote?** Check out the [Getting Started Guide](docs/getting-started.md) for a complete walkthrough with examples.

```bash
./scripts/termote.sh # Interactive menu
./scripts/termote.sh install container # Container mode (docker/podman)
./scripts/termote.sh install native # Native mode (host tools)
./scripts/termote.sh link # Create 'termote' global command
make test # Run tests
```

> After `link`, use `termote` from anywhere: `termote health`, `termote install native --lan`

> **Tip**: Install [gum](https://github.com/charmbracelet/gum) for enhanced interactive menus (optional, bash fallback available)

## Installation

### One-liner (recommended)

**macOS/Linux:**

```bash
# Download and prompt before install (defaults to native mode)
curl -fsSL https://raw.githubusercontent.com/lamngockhuong/termote/main/scripts/get.sh | bash

# Auto-install without prompt
curl -fsSL .../get.sh | bash -s -- --yes

# Download only (no install)
curl -fsSL .../get.sh | bash -s -- --download-only

# Auto-update with saved config
curl -fsSL .../get.sh | bash -s -- --update

# Install specific version
curl -fsSL .../get.sh | bash -s -- --version 0.0.4

# With explicit mode and options
curl -fsSL .../get.sh | bash -s -- --yes --container --lan
curl -fsSL .../get.sh | bash -s -- --yes --native --tailscale myhost

# Force new password (ignore saved config)
curl -fsSL .../get.sh | bash -s -- --yes --container --fresh
```

**Windows (PowerShell):**

> **Note:** If script execution is disabled on your system, run this first:
>
> ```powershell
> Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
> ```

```powershell
# Download and prompt before install (defaults to native mode)
irm https://raw.githubusercontent.com/lamngockhuong/termote/main/scripts/get.ps1 | iex

# Auto-install without prompt
$env:TERMOTE_AUTO_YES = "true"; irm .../get.ps1 | iex

# With explicit mode
$env:TERMOTE_MODE = "container"; irm .../get.ps1 | iex

# Auto-update with saved config
$env:TERMOTE_UPDATE = "true"; irm .../get.ps1 | iex
```

### Docker

```bash
# All-in-one (auto-generates credentials, check logs: docker logs termote)
docker run -d --name termote -p 7680:7680 ghcr.io/lamngockhuong/termote:latest

# With custom credentials
docker run -d --name termote -p 7680:7680 \
-e TERMOTE_USER=admin -e TERMOTE_PASS=secret \
ghcr.io/lamngockhuong/termote:latest

# Without auth (local dev only)
docker run -d --name termote -p 7680:7680 \
-e NO_AUTH=true \
ghcr.io/lamngockhuong/termote:latest

# With volume for persistence
docker run -d --name termote -p 7680:7680 \
-v termote-data:/home/termote \
ghcr.io/lamngockhuong/termote:latest

# Mount custom workspace directory
docker run -d --name termote -p 7680:7680 \
-v ~/projects:/workspace \
ghcr.io/lamngockhuong/termote:latest

# With Tailscale HTTPS (requires Tailscale on host)
docker run -d --name termote -p 7680:7680 \
-e TERMOTE_USER=admin -e TERMOTE_PASS=secret \
ghcr.io/lamngockhuong/termote:latest
sudo tailscale serve --bg --https=443 http://127.0.0.1:7680
# Access at: https://your-hostname.tailnet-name.ts.net
```

### From Release

```bash
# Download latest release
VERSION=$(curl -s https://api.github.com/repos/lamngockhuong/termote/releases/latest | grep tag_name | cut -d '"' -f4)
wget https://github.com/lamngockhuong/termote/releases/download/${VERSION}/termote-${VERSION}.tar.gz
tar xzf termote-${VERSION}.tar.gz
cd termote-${VERSION#v}

# Install (interactive menu or with mode)
./scripts/termote.sh install
./scripts/termote.sh install container
```

### From Source

```bash
git clone https://github.com/lamngockhuong/termote.git
cd termote
./scripts/termote.sh install container
```

> **Note**: `termote.sh` is the unified CLI supporting `install` (builds from source, uses pre-built artifacts when available), `uninstall`, and `health` commands.

## Deployment Modes

```mermaid
flowchart LR
subgraph Container["Container Mode"]
direction TB
C1["Docker/Podman"] --> C2["tmux-api :7680"] --> C3["ttyd :7681"] --> C4["tmux"]
end

subgraph Native["Native Mode"]
direction TB
N1["Host System"] --> N2["tmux-api :7680"] --> N3["ttyd :7681"] --> N4["tmux + Host Tools"]
end

User["User"] --> Container & Native
```

| Mode | Description | Use Case | Platform |
| ------------- | -------------- | ------------------------------- | ------------ |
| `--container` | Container mode | Simple deployment, isolated env | macOS, Linux |
| `--native` | All native | Host tool access (claude, gh) | macOS, Linux |

### Options

| Flag | Description |
| --------------------------- | ----------------------------------------------- |
| `--lan` | Expose to LAN (default: localhost only) |
| `--tailscale ` | Enable Tailscale HTTPS |
| `--no-auth` | Disable basic authentication |
| `--port ` | Host port (default: 7680, Windows: 7690) |
| `--fresh` | Force new password prompt (ignore saved config) |
| `--update` | Auto-update with saved config |
| `--version ` | Install specific version (with or without `v`) |

| Environment Variable | Description |
| -------------------- | ------------------------------------------------ |
| `WORKSPACE` | Host directory to mount (default: `./workspace`) |
| `TERMOTE_USER` | Basic auth username (default: auto-generated) |
| `TERMOTE_PASS` | Basic auth password (default: auto-generated) |
| `NO_AUTH` | Set to `true` to disable authentication |

### Container Mode (recommended for simplicity)

Scripts auto-detect `podman` or `docker` β€” both work identically.

```bash
./scripts/termote.sh install container # localhost with basic auth
./scripts/termote.sh install container --no-auth # localhost without auth
./scripts/termote.sh install container --lan # LAN accessible
# Access: http://localhost:7680

# Custom workspace directory (mounted to /workspace in container)
WORKSPACE=~/projects ./scripts/termote.sh install container
WORKSPACE=/path/to/code make install-container
```

> **Security note**: Avoid mounting `$HOME` directly β€” sensitive directories like `.ssh`, `.gnupg` will be accessible in container. Mount specific project directories instead.

### Native (recommended for host binary access)

Use when you need access to host binaries (claude, git, etc):

```bash
# Linux
sudo apt install ttyd tmux
# Or: sudo snap install ttyd
./scripts/termote.sh install native

# macOS
brew install ttyd tmux go
./scripts/termote.sh install native
# Access: http://localhost:7680
```

### With Tailscale HTTPS (all modes)

Uses `tailscale serve` for automatic HTTPS (no manual cert management):

```bash
# Tailscale only (default port 443)
./scripts/termote.sh install container --tailscale myhost.ts.net

# Custom port
./scripts/termote.sh install native --tailscale myhost.ts.net:8765

# Tailscale + LAN accessible
./scripts/termote.sh install container --tailscale myhost.ts.net --lan

# Access: https://myhost.ts.net (or :8765 for custom port)
```

### Uninstall

```bash
./scripts/termote.sh uninstall container # Container mode
./scripts/termote.sh uninstall native # Native mode
./scripts/termote.sh uninstall all # Everything
```

### Updating

```bash
# Option 1: Auto-update with saved config
curl -fsSL .../get.sh | bash -s -- --update

# Option 2: Re-run one-liner (compares versions, prompts before install)
curl -fsSL .../get.sh | bash

# Option 3: Manual update
./scripts/termote.sh uninstall [container|native]
git pull origin main # If installed from source
./scripts/termote.sh install [container|native] [--lan] [--tailscale ...]
```

## Platform Support

| Platform | Container | Native | CLI Script |
| -------- | ---------------- | ---------------- | ----------- |
| Linux | βœ“ | βœ“ | termote.sh |
| macOS | βœ“ | βœ“ | termote.sh |
| Windows | ⚠️ (experimental) | ⚠️ (experimental) | termote.ps1 |

> **⚠️ Windows Support (Experimental)**: Windows support is currently in early stages and needs more testing. Container mode requires Docker Desktop, native mode requires psmux. Please report issues on GitHub.

### Windows Native Mode

Windows native mode uses [psmux](https://github.com/psmux/psmux) (tmux-compatible terminal multiplexer for Windows):

```powershell
# Install psmux
winget install psmux

# Run Termote
.\scripts\termote.ps1 install native
.\scripts\termote.ps1 install container # Or container mode with Docker Desktop
```

## Mobile Usage

| Action | Gesture |
| ---------------- | ------------------- |
| Cancel/interrupt | Swipe left (Ctrl+C) |
| Tab completion | Swipe right |
| History up | Swipe up |
| History down | Swipe down |
| Paste | Long press |
| Font size | Pinch in/out |

Virtual toolbar provides: Tab, Esc, Ctrl, Shift, Arrow keys, and common key combos. Supports Ctrl+Shift combinations (paste, copy). Toggle between minimal and expanded mode for additional keys (Home, End, Delete, etc.).

## Project Structure

```
termote/
β”œβ”€β”€ Makefile # Build/test/deploy commands
β”œβ”€β”€ Dockerfile # Docker mode (tmux-api + ttyd)
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ entrypoint.sh # Docker entrypoint
β”œβ”€β”€ docs/ # Documentation
β”‚ └── images/screenshots/ # App screenshots
β”œβ”€β”€ pwa/ # React PWA
β”‚ └── src/
β”‚ β”œβ”€β”€ components/
β”‚ β”œβ”€β”€ contexts/
β”‚ β”œβ”€β”€ hooks/
β”‚ β”œβ”€β”€ types/
β”‚ └── utils/
β”œβ”€β”€ tmux-api/ # Go server
β”‚ β”œβ”€β”€ main.go # Entry point
β”‚ β”œβ”€β”€ serve.go # Server (PWA, proxy, auth)
β”‚ └── tmux.go # tmux API handlers
β”œβ”€β”€ scripts/
β”‚ β”œβ”€β”€ termote.sh # Unix CLI (install/uninstall/health)
β”‚ β”œβ”€β”€ termote.ps1 # Windows PowerShell CLI
β”‚ β”œβ”€β”€ get.sh # Unix online installer (curl | bash)
β”‚ └── get.ps1 # Windows online installer (irm | iex)
β”œβ”€β”€ tests/ # Test suite
β”‚ β”œβ”€β”€ test-termote.sh
β”‚ β”œβ”€β”€ test-termote.ps1 # Windows tests
β”‚ β”œβ”€β”€ test-get.sh
β”‚ └── test-entrypoints.sh
└── website/ # Astro Starlight docs site
└── src/content/docs/ # MDX documentation
```

## Development

```bash
make build # Build PWA and tmux-api
make test # Run all tests
make health # Check service health
make clean # Stop containers

# E2E tests (requires running server)
./scripts/termote.sh install container # Start server first
cd pwa && pnpm test:e2e # Run Playwright tests
cd pwa && pnpm test:e2e:ui # Run with UI debugger
```

**Manual Testing:** See [Self-Test Checklist](docs/self-test-checklist.md)

## Troubleshooting

### Session not persisting

- Check tmux: `tmux ls`
- Verify ttyd uses `-A` flag (attach-or-create)

### WebSocket errors

- Check tmux-api logs: `docker logs termote`
- Verify ttyd is running on port 7681

### Mobile keyboard issues

- Ensure viewport meta tag is present
- Test on real device, not emulator

### Native mode: processes not starting

```bash
ps aux | grep ttyd # Check if ttyd is running
ps aux | grep tmux-api # Check if tmux-api is running
lsof -i :7680 # Verify port is in use
```

## Security Notes

- **Default: localhost only** - not exposed to LAN unless `--lan` flag used
- **Basic auth enabled by default** - use `--no-auth` to disable for local dev
- **Built-in brute-force protection** - rate limiting (5 attempts/min per IP)
- Use HTTPS (Tailscale) for production
- Restrict to trusted networks/VPN

## License

MIT