SSHler Featured
A local-only web UI for browsing remote files over SFTP and accessing tmux sessions in the browser. Vue 3 SPA, WebSocket terminal, mobile-first input bar, no remote installation required. 232 tests.
SSHler
A local-only web UI for browsing remote files over SFTP and accessing tmux sessions in the browser. No agent, no daemon, nothing installed on the remote host. Point it at your SSH config and go.
Why SSHler?
I have multiple machines. A workstation, a home server, a couple of VPSes. I’m constantly jumping between them; opening terminals, navigating to project directories, checking files, restarting services. The workflow is repetitive: SSH in, cd to the right place, tmux attach, do the thing, detach, switch machines, repeat.
VS Code Remote handles this if you live in VS Code. I don’t always want a full IDE; sometimes I just need a terminal in the right directory and a quick look at a file. JuiceSSH on mobile is solid but it’s Android-only and doesn’t do file browsing. There’s a gap between “full IDE” and “raw SSH” that nothing fills cleanly.
SSHler sits in that gap. One pip install, one command, browser opens, all your SSH hosts are there. Click a box, browse files, open a terminal. The terminal is tmux on the remote host bridged through WebSocket + xterm.js; persistent sessions that survive browser closes, page reloads, network hiccups.
Good fit: multi-machine workflows, mobile SSH access with a real terminal, browsing remote files without mounting filesystems, running LLM agents across multiple boxes from a single browser tab, anything where you want “quick access” without “full IDE.”
Quick Start
pip install sshler
sshler serve
Opens http://127.0.0.1:8822/app/ in your browser. Reads ~/.ssh/config automatically; every Host entry appears as a box.
Architecture
Browser (Vue 3 SPA)
│
├── REST API ──→ FastAPI ──→ asyncssh ──→ SFTP (file ops)
│
└── WebSocket ──→ FastAPI ──→ asyncssh ──→ tmux (terminal)
│
SSH connection pool
(per-box, idle timeout)
The backend is a single FastAPI process. File operations go through asyncssh’s SFTP client. Terminal sessions open tmux new -As <session> -c <dir> on the remote host and pipe stdin/stdout over WebSocket as raw bytes. The local box bypasses SSH entirely; subprocess tmux, direct filesystem access.
Features
File Browser
Vue-based file browser with preview, edit, delete, upload, and “Open Terminal Here”:
- Drag & drop upload with progress indicators
- Batch operations via multi-select (Shift+Click, Cmd/Ctrl+Click)
- Archive support — create and extract
.tar.gzand.zipfrom context menu - Content search — grep across remote files with clickable results
- Inline rename (F2), context menus, breadcrumb navigation
- File preview with CodeMirror, markdown rendering, line numbers, word wrap toggle
- Git branch display in the breadcrumb bar
Terminal
WebSocket terminal backed by tmux on the remote host:
- Multi-pane layouts — split horizontally, vertically, or grid
- Session persistence — layout saved to localStorage, restored on reload
- Per-box themes — color-code terminals by environment (prod=red, staging=green)
- Command snippets — save and quick-insert frequently used commands per box or globally
- Port forwarding UI — visual SSH tunnel management (local/remote) per box
- Session switcher — dropdown listing active tmux sessions per box
- Desktop notifications — bell and OSC 777 notify when the tab is hidden
Mobile Terminal
Touch-optimized terminal with a custom input bar for keys that are murder on mobile keyboards:
| Button | Key | Purpose |
|---|---|---|
| Arrow keys | Navigation | Menu navigation, vim, Claude Code |
| Enter | Confirm | Submit commands |
| Tab | Autocomplete | Shell completion |
| Escape | Cancel | Interrupt agent turns |
| Ctrl+C | Kill | Stop processes (red) |
| Scroll mode | Ctrl+B [ | tmux copy mode (orange) |
| PgUp/PgDn | Scroll | Navigate in copy mode |
| Ctrl+D | EOF | Graceful exit (teal) |
14px ultra-thin header with live CPU/MEM stats. Maximum terminal real estate.
Search
Directory search with frecency-based ranking:
- Local box: queries zoxide directly for instant results
- Remote boxes: SQLite frecency table + SSH
findfor discovery - Formula:
score = visit_count * exp(-0.1 * days_since_last_visit) - Command palette (Cmd/Ctrl+K) with fuzzy search across all features
Security
- Session cookies (httpOnly, Secure, SameSite=Lax) with Argon2id password hashing
- CSRF protection via Origin header validation on state-changing requests
- Path traversal prevention on all file operations
- Rate limiting on auth endpoints with lockout
- Loopback-only by default — binds to 127.0.0.1; expose through a reverse proxy with TLS
Honest Limitations
- Single user — Designed for one person on localhost. Not a multi-tenant service.
- In-memory sessions — Session store doesn’t survive restarts or work across multiple instances. Fine for single-process deployment.
- No file sync — Browse and edit, but no bidirectional sync. Use rsync or git for that.
- tmux required — Terminal sessions are tmux. No tmux on the remote host, no terminal. (It’s 2026; install tmux.)
- Large directories — Virtual scrolling handles 10K+ files, but SFTP
readdiron a directory with 100K entries is still slow. That’s SFTP, not sshler.
Getting Started
pip install sshler # or: uv add sshler
sshler serve # opens browser automatically
sshler serve --no-browser # headless / systemd
Python 3.12+ · tmux on remote hosts (or local for the local box).
uv run pytest # 232 tests (167 backend + 65 frontend)
日本語版: このプロジェクトを日本語で読む