feat: switch from ttyd to real kitty via Xpra HTML5
Replaced the tmux+ttyd web terminal with actual kitty running through Xpra's HTML5 streaming. Full GPU rendering, native kitty tabs/splits, persistent sessions, and multi-client support.
This commit is contained in:
@@ -1,137 +1,129 @@
|
||||
# kitty-web
|
||||
|
||||
Mobile-first web terminal powered by [ttyd](https://github.com/tsl0922/ttyd) + [tmux](https://github.com/tmux/tmux). One persistent tmux session, multiple tabs, accessible from any browser. Designed for phones and tablets.
|
||||
Run the real [kitty terminal](https://sw.kovidgoyal.net/kitty/) in your browser. Mobile-friendly, GPU-accelerated, with full tab and split support.
|
||||
|
||||
Powered by [Xpra](https://xpra.org/) — serves kitty as an HTML5 application via its built-in web client. This is not a terminal emulator in JavaScript; it's the actual kitty running on your server, streamed to your browser.
|
||||
|
||||
## Features
|
||||
|
||||
- **Single persistent session** — one tmux session shared across all connected clients
|
||||
- **Mobile touch toolbar** — on-screen buttons for common shortcuts (new tab, ^C, ^D, Esc, arrows, split panes, etc.)
|
||||
- **Text selection mode** — tap `Sel` to enter selection mode, long-press to select and copy text, tap `Done` to resume typing
|
||||
- **Push notifications** — send browser notifications from any terminal command
|
||||
- **PWA installable** — add to home screen for an app-like experience
|
||||
- **Dark theme** — styled for dark-mode terminals with orange accents
|
||||
- **Real kitty** — GPU rendering, ligatures, image protocol, all of it
|
||||
- **Kitty tabs and splits** — native `ctrl+shift+t`, splits, layouts
|
||||
- **Persistent session** — close the browser, reconnect later, everything is still there
|
||||
- **Multi-client** — multiple browsers can view/interact with the same session
|
||||
- **Mobile-friendly** — Xpra's HTML5 client handles touch input, keyboard, and scaling
|
||||
- **Push notifications** — optional notification API for long-running commands
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Browser -> Caddy (HTTPS + Auth) -> ttyd (port 7681) -> tmux session
|
||||
-> notify-server (port 7682) -> /api/notifications
|
||||
Browser -> Caddy (HTTPS + Auth) -> Xpra HTML5 (port 7681) -> kitty
|
||||
```
|
||||
|
||||
- **ttyd** serves the terminal UI with a custom index page that loads `toolbar.js`
|
||||
- **toolbar.js** injects the mobile toolbar (shortcut buttons, selection mode) into the page at runtime
|
||||
- **notify-server.py** provides a simple HTTP endpoint for push notifications
|
||||
- **kitty-notify** is a CLI command to trigger notifications from scripts
|
||||
Xpra runs kitty inside a virtual X display (Xvfb) and streams the rendered output to browsers via WebSocket. The HTML5 client handles input, clipboard, and display scaling.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Debian/Ubuntu (tested on Debian 13 Trixie)
|
||||
- kitty (`apt install kitty`)
|
||||
- Xpra (`https://xpra.org/` — add their repo for latest version)
|
||||
|
||||
### Install
|
||||
|
||||
```bash
|
||||
sudo ./install.sh
|
||||
# Add Xpra repo (Debian example)
|
||||
curl -sL https://xpra.org/xpra.asc | sudo tee /usr/share/keyrings/xpra.asc
|
||||
echo "deb [signed-by=/usr/share/keyrings/xpra.asc] https://xpra.org/ $(lsb_release -cs) main" | \
|
||||
sudo tee /etc/apt/sources.list.d/xpra.list
|
||||
sudo apt update && sudo apt install -y xpra kitty
|
||||
|
||||
# Create a service user (optional)
|
||||
sudo useradd -m -s /bin/bash rdp
|
||||
|
||||
# Install systemd service
|
||||
sudo cp systemd/kitty-web.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now kitty-web
|
||||
```
|
||||
|
||||
This installs ttyd, tmux, systemd services, and the notification system. The terminal will be available at `http://YOUR_IP:7681`.
|
||||
### Manual Start
|
||||
|
||||
```bash
|
||||
xpra start --bind-ws=0.0.0.0:7681 \
|
||||
--start="kitty" \
|
||||
--html=on \
|
||||
--sharing=yes \
|
||||
--no-daemon
|
||||
```
|
||||
|
||||
Then open `http://YOUR_IP:7681` in a browser.
|
||||
|
||||
### Configuration
|
||||
|
||||
Set environment variables before running the installer:
|
||||
Edit `systemd/kitty-web.service` to customize:
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `KITTY_USER` | `rdp` | System user that owns the tmux session |
|
||||
| `TTYD_PORT` | `7681` | Port for ttyd web terminal |
|
||||
| `NOTIFY_PORT` | `7682` | Port for notification API |
|
||||
| `FONT_SIZE` | `18` | Terminal font size (optimized for mobile) |
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--bind-ws=HOST:PORT` | WebSocket listen address |
|
||||
| `--start="CMD"` | Application to launch (default: `kitty`) |
|
||||
| `--sharing=yes` | Allow multiple clients to connect |
|
||||
| `--readonly=no` | Allow keyboard/mouse input |
|
||||
|
||||
### Reverse Proxy
|
||||
### Reverse Proxy (Caddy)
|
||||
|
||||
See `caddy-example.conf` for a complete Caddy v2 configuration with authentication. The setup supports OAuth2 Proxy, Authentik, or Authelia for access control.
|
||||
|
||||
Key requirements for the reverse proxy:
|
||||
- WebSocket support (ttyd uses WebSockets for terminal I/O)
|
||||
- Serve `/toolbar.js`, `/manifest.json`, `/icon-*.png` as static files
|
||||
- Proxy `/api/*` to the notification server (port 7682)
|
||||
- Proxy everything else to ttyd (port 7681)
|
||||
|
||||
## Mobile Toolbar
|
||||
|
||||
The toolbar appears automatically on screens narrower than 900px. Buttons:
|
||||
|
||||
| Button | Action | tmux Key |
|
||||
|--------|--------|----------|
|
||||
| **+Tab** | New tab | `Ctrl-A c` |
|
||||
| **Next** | Next tab | `Ctrl-A n` |
|
||||
| **Prev** | Previous tab | `Ctrl-A p` |
|
||||
| **^C** | Interrupt | `Ctrl-C` |
|
||||
| **^D** | EOF / logout | `Ctrl-D` |
|
||||
| **Clr** | Clear screen | `Ctrl-L` |
|
||||
| **Esc** | Escape key | `Escape` |
|
||||
| **Tab** | Tab completion | `Tab` |
|
||||
| **Up/Down** | History navigation | Arrow keys |
|
||||
| **Sel** | Toggle text selection mode | — |
|
||||
| **Spl** | Split pane vertically | `Ctrl-A %` |
|
||||
| **Pane** | Cycle between panes | `Ctrl-A o` |
|
||||
| **Kill** | Kill current pane/tab | `Ctrl-A x` |
|
||||
|
||||
## Push Notifications
|
||||
|
||||
Send notifications from any terminal session:
|
||||
|
||||
```bash
|
||||
# Direct message
|
||||
kitty-notify "Build complete!"
|
||||
|
||||
# Pipe output
|
||||
echo "Deploy finished" | kitty-notify
|
||||
|
||||
# Use in scripts
|
||||
make build && kitty-notify "Build succeeded" || kitty-notify "Build FAILED"
|
||||
```
|
||||
terminal.example.com {
|
||||
# Add your auth here (OAuth2 Proxy, Authentik, etc.)
|
||||
reverse_proxy YOUR_SERVER:7681
|
||||
}
|
||||
```
|
||||
|
||||
Notifications appear as browser push notifications on mobile. Tap the bell icon in the terminal UI to enable them. Notifications expire after 30 seconds.
|
||||
WebSocket support is required. Caddy handles this automatically. See `caddy-example.conf` for a full example with authentication options.
|
||||
|
||||
## tmux Keybindings
|
||||
### Kitty Config
|
||||
|
||||
The included tmux config uses `Ctrl-A` as the prefix (easier on mobile than the default `Ctrl-B`):
|
||||
Place your kitty config at `~/.config/kitty/kitty.conf` for the service user. See `config/kitty.conf` for a dark-themed example.
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `Ctrl-A c` | New window/tab |
|
||||
| `Ctrl-A n` / `Ctrl-A p` | Next / previous window |
|
||||
| `Ctrl-A %` / `Ctrl-A "` | Split vertical / horizontal |
|
||||
| `Ctrl-A o` | Cycle panes |
|
||||
| `Ctrl-A x` | Kill pane |
|
||||
| `Alt-1` through `Alt-5` | Jump to window 1-5 |
|
||||
| `Alt-Left` / `Alt-Right` | Previous / next window |
|
||||
| `Alt-t` | New window |
|
||||
| Mouse scroll | Scroll through history |
|
||||
## Optional: Push Notifications
|
||||
|
||||
The `notify-server.py` and `kitty-notify` command provide a simple browser notification system:
|
||||
|
||||
```bash
|
||||
# Install
|
||||
sudo cp notify-server.py /opt/kitty-web/
|
||||
sudo cp kitty-notify /usr/local/bin/
|
||||
sudo cp systemd/kitty-notify.service /etc/systemd/system/
|
||||
sudo systemctl enable --now kitty-notify
|
||||
|
||||
# Usage
|
||||
kitty-notify "Build complete!"
|
||||
echo "done" | kitty-notify
|
||||
```
|
||||
|
||||
Requires proxying `/api/*` to port 7682 — see `caddy-example.conf`.
|
||||
|
||||
## Files
|
||||
|
||||
```
|
||||
kitty-web/
|
||||
toolbar.js # Mobile toolbar (injected into ttyd page)
|
||||
notify-server.py # Push notification HTTP API
|
||||
kitty-notify # CLI notification command
|
||||
manifest.json # PWA manifest
|
||||
icon-192.png # PWA icon (192x192)
|
||||
icon-512.png # PWA icon (512x512)
|
||||
install.sh # Installer script
|
||||
caddy-example.conf # Reverse proxy configuration example
|
||||
README.md
|
||||
LICENSE
|
||||
install.sh # Automated installer
|
||||
caddy-example.conf # Reverse proxy config template
|
||||
notify-server.py # Push notification HTTP API (optional)
|
||||
kitty-notify # CLI notification command (optional)
|
||||
manifest.json # PWA manifest
|
||||
icon-192.png # PWA icon
|
||||
icon-512.png # PWA icon
|
||||
config/
|
||||
tmux.conf # tmux configuration (dark theme, mobile-friendly)
|
||||
tmux.conf # tmux config (for optional tmux-inside-kitty usage)
|
||||
kitty.conf # Kitty terminal config (dark theme)
|
||||
systemd/
|
||||
ttyd-kitty.service # ttyd systemd unit
|
||||
kitty-web.service # Xpra + kitty systemd unit
|
||||
kitty-notify.service # Notification API systemd unit
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- Linux (Debian/Ubuntu tested)
|
||||
- tmux
|
||||
- Python 3 (for notification server)
|
||||
- [ttyd](https://github.com/tsl0922/ttyd) (installed automatically)
|
||||
- Caddy, nginx, or another reverse proxy (for HTTPS and auth)
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
Reference in New Issue
Block a user