A persistent SSH terminal. Powered by zmx.
Download v0.7.0 · User guide · Website · Changelog
Give a terminal on your VM a name. Leave it running. Come back to the same shell, directory, and programs after you detach or lose your connection.
sess init user@host
sess set --host user@host
sess new work
# Ctrl+\ to detach
sess attach workRun sess to browse your sessions. Your terminal application handles tabs and windows; sess handles session management and reconnection.
brew install deepaksilaych/tap/sessUpdate with brew update && brew upgrade sess. Shell completions are installed automatically.
Requires Node.js 22+:
npx sess-cli --help
npx sess-cli init dev
npx sess-cli set --host dev
npx sess-cli new work
npx sess-cli # open the session browserFor a permanent sess command:
npm install -g sess-cli
sess --helpThe package downloads the matching official binary on first run and verifies its pinned SHA-256 checksum. Later runs use the cached binary in ${XDG_CACHE_HOME:-~/.cache}/sess/npm/. Node.js runs on your laptop only; the VM does not need npm or Node.js. The npm package is named sess-cli; npx sess refers to a different project.
Download the archive for your laptop from v0.7.0:
| Platform | Archive |
|---|---|
| macOS, Apple Silicon | sess-darwin-arm64.tar.gz |
| macOS, Intel | sess-darwin-amd64.tar.gz |
| Linux, ARM64 | sess-linux-arm64.tar.gz |
| Linux, AMD64 | sess-linux-amd64.tar.gz |
For example, on an Apple Silicon Mac, after downloading the archive:
tar -xzf sess-darwin-arm64.tar.gz
mkdir -p ~/.local/bin
install -m 755 sess ~/.local/bin/sess
export PATH="$HOME/.local/bin:$PATH"
sess versionAdd the PATH line to your shell configuration if needed for future terminals. Use the corresponding archive name on another platform. SHA256SUMS accompanies the release. Each archive includes the user documentation and all four remote helpers.
The client needs OpenSSH. The VM needs working SSH access; sess init installs its helper and zmx. A compiler is only needed when building from source.
Requires Go 1.26.5+ to build. The installed client needs OpenSSH. Linux and macOS on AMD64 and ARM64 are supported build targets.
git clone https://github.com/DeepakSilaych/sess.git
cd sess
make build
./dist/sess --help
make install # default: ~/.local/bin
# or: make install PREFIX=/usr/localEnsure the installation's bin directory is in your PATH. make build embeds the remote helpers for all four targets, so the remote VM needs neither Go nor a compiler. go install alone does not generate those helpers; use make build or a release archive.
Version 0.7.0 uses zmx. Existing tmux sessions from 0.5 continue to belong to tmux; they cannot be converted into live zmx sessions. Attach to them with tmux while finishing that work. The old ~/.sess state is left untouched.
sess init dev # existing SSH alias, or user@host
sess set --host dev # choose the default separately
sess doctorinit verifies SSH, detects the VM's platform, installs the remote helper and pinned zmx 0.8.1 under ~/.local/share/sess/bin, and checks the result. No sudo, extra network port, or laptop daemon. The zmx archive is downloaded over HTTPS and verified against its pinned SHA-256 digest before installation. A different existing managed zmx version is not silently replaced.
Use SSH keys available to ssh-agent for session operations and reconnecting. Provisioning can prompt through SSH; subsequent operations use OpenSSH's batch mode so a background refresh cannot ask for a password. Configure ports, jump hosts, and identities in ~/.ssh/config.
init does not change your default host. It also does not edit your shell startup files.
| Command | Shortcut | Purpose |
|---|---|---|
sess init <host> |
Prepare the VM | |
sess set --host <host> |
sess set -h <host> |
Save the default SSH destination |
sess new <name> |
sess n <name> |
Create and attach |
sess attach <name> |
sess a <name> |
Attach to an existing session |
sess remove <name> |
sess rm <name> |
End the session and its programs |
sess ls |
List live sessions | |
sess upload <file> [file...] |
Upload local files and print remote paths | |
sess detach |
Inside a session, detach all attached terminals | |
sess |
Open the interactive session browser | |
sess doctor |
Check SSH and remote dependencies | |
sess version |
Print version | |
sess completion bash |
Generate bash, zsh, or fish completion |
Use --host or -h to override the host for a session command without changing the default:
sess n build -h staging
sess a build -h staging
sess ls -h staging
sess rm build -h stagingHost resolution is explicit flag → saved default → actionable error. There is no local fallback. -h means host; help is --help.
Names are 1–48 letters, numbers, hyphens, or underscores, starting with a letter or number. Names are scoped to the remote account. Aliases for the same account and VM see the same sessions.
New shells start in the remote home directory. Use cd normally. Sessions share the VM's filesystem and Git checkout; creating a session does not copy a repository.
sess new build --detach # also: -d; does not need a terminal
sess ls --json # {"host": "dev", "sessions": [...]}
sess ls --quiet # names only; also: -qRedirected bare sess prints a list. new without --detach and attach require an interactive terminal. Errors go to stderr with a nonzero exit status. JSON list output includes stable session IDs, client counts, creation time, backend-reported directory, PID, and attached/detached state.
- *Ctrl+* detaches only your current terminal. Work stays on the VM.
- Close the terminal tab to disconnect; attach from another terminal later.
sess detachinside the remote shell detaches every client of that session.- Ctrl+C while attached interrupts the foreground program as usual.
exitat the main shell prompt ends the session.sess rm <name>terminates the session; files written to the VM remain.
When SSH reports a transient connection failure, sess retries with delays of 1, 2, 4, 8, then at most 15 seconds. Ctrl+C during reconnection cancels the attempt. Intentional detach and normal shell exit do not reconnect. Authentication, host-key, configuration, and unknown SSH errors are reported instead of blindly retried.
A reconnect request carries the original session ID. If somebody removed the session and reused its name, reconnect stops instead of knowingly attaching you to the replacement. Missing sessions are never recreated by the sess attach command.
No reconnect process remains after you close the client terminal. Run sess a <name> to return. The VM and zmx must stay alive: live sessions do not survive a VM reboot, backend crash, or operating-system process cleanup.
Run sess init <host> once after upgrading to 0.7.0 to update the VM helper. Attach normally, start Claude Code on the VM, and drop a local PNG, JPEG, GIF, or WebP image into its prompt.
When the terminal sends the dropped paths as a bracketed paste, sess uploads the images over SSH and inserts their remote paths. It does not press Enter. Spaces, shell-quoted filenames, and up to eight images per paste are supported, with a combined limit of 25 MiB. Ctrl+C cancels an upload in progress.
This uses the standard terminal paste protocol, with no terminal-specific plugin. A terminal that sends an unmarked path cannot be reliably distinguished from ordinary typing. For that case, or for other file types, run this in another local terminal:
sess upload ~/Desktop/screenshot.png
sess upload ./report.pdf --host devCopy the printed remote path into Claude Code. Each file may be up to 25 MiB. upload uses the default host unless overridden; it does not require an attached session.
Files are stored in unique directories under ~/.local/share/sess/uploads/ on the VM, with private directory/file permissions and a verified SHA-256 digest. They remain after detach, session removal, and VM reboot; delete them on the VM when no longer needed. No project file is overwritten.
Use sess a work --no-upload-images (or sess --no-upload-images for the browser) to pass image paths through unchanged. Clipboard image pixels, folders, mixed prose-and-path pastes, and plain unmarked drops are not automatically uploaded. See file transfer for details and compatibility limits.
Captured from the real SSH integration test. Names shown are test sessions.
The browser refreshes asynchronously, shows host context and client counts, and has distinct loading, empty, filtered, and unreachable-host states.
| Key | Action |
|---|---|
↑ / ↓, k / j |
Select a session |
Enter |
Attach; return to the browser on detach |
n |
Create and attach |
x |
Remove; type the session name to confirm in the browser |
/ |
Filter by name |
h |
Browse another host without changing your saved default |
r |
Refresh |
? |
Keyboard help |
q, Esc |
Quit or cancel the current form |
While attached, a small local PTY bridge forwards SSH output unchanged and handles pasted image paths. sess does not draw another TUI around the shell. NO_COLOR is supported. Start attachments from an ordinary laptop terminal, rather than nesting persistent terminals across SSH.
laptop VM
sess CLI / browser ── SSH ──> sess agent
│
zmx client
│ Unix socket
zmx daemon
│ PTY
shell + programs
The agent speaks a versioned JSON command protocol over ordinary SSH. Requests are encoded as a single argument; names and hosts are validated rather than interpolated into shell commands. Interactive attachment uses SSH's PTY. The client never reimplements SSH authentication or terminal emulation.
zmx owns the terminal process and restores its display when a client returns. sess uses a private, account-specific runtime directory, separate from ordinary zmx sessions. A random ID is stored on each session; zmx supplies live session state, so a local cache cannot claim that an unreachable VM is empty.
The remote agent is installed at an absolute path relative to $HOME; SSH startup PATH is not required. Session shells receive the managed binary directory on PATH so sess detach works there. User shell configuration can still override PATH. The displayed directory comes from zmx. It starts at the creation directory and can update when the shell reports directory changes using OSC 7. Run pwd in the shell for its current directory.
Local configuration is ~/.config/sess/config.json (XDG_CONFIG_HOME supported), written atomically with private permissions. SESS_CONFIG overrides its path. Runtime and backend overrides SESS_RUNTIME_DIR and SESS_ZMX are intended for development/testing. SESS_SSH selects a local SSH executable or wrapper.
make build # builds client and embeds remote helpers
make test # unit tests with race detector + go vet
make integration # real SSH and zmx in disposable DockerIntegration testing requires Docker, Python 3, and OpenSSH. It generates temporary keys/configuration, binds SSH only on loopback, and removes its test container on exit. It does not operate on your configured VMs.
cmd/sess/ Client entry point
cmd/sess-agent/ Small remote helper entry point
internal/api/ Shared data types and validation
internal/cli/ Commands and completion
internal/tui/ Interactive session browser
internal/transport/ System SSH and reconnect policy
internal/backend/ zmx lifecycle and namespace
internal/agent/ Remote request handling
internal/provision/ Embedded helpers and verified zmx installer
internal/store/ Local configuration
scripts/ Cross-build and release packaging
test/integration/ Isolated SSH lifecycle tests
- User guide: setup, commands, detach, reconnect, and persistence.
- Troubleshooting: authentication, missing sessions, PATH, and backend versions.
- Contributing: local builds, tests, and project conventions.
- Release process: manual tagging, archives, and verification.
- Changelog: version history and migration notes.
