repos / zmx

session persistence for terminal processes
git clone https://github.com/neurosnap/zmx.git

  1<img src="./logo.png" width="50px" height="50px">
  2
  3# zmx
  4
  5session persistence for terminal processes
  6
  7Reason for this tool: [You might not need `tmux`](https://bower.sh/you-might-not-need-tmux)
  8
  9## features
 10
 11- Persist terminal shell sessions (pty processes)
 12- Ability to attach and detach from a shell session without killing it
 13- Native terminal scrollback
 14- Multiple clients can connect to the same session
 15- Re-attaching to a session restores previous terminal state and output
 16- Works on mac and linux
 17- This project does **NOT** provide windows, tabs, or splits
 18
 19## install
 20
 21### binaries
 22
 23- https://zmx.sh/a/zmx-0.1.0-linux-aarch64.tar.gz
 24- https://zmx.sh/a/zmx-0.1.0-linux-x86_64.tar.gz
 25- https://zmx.sh/a/zmx-0.1.0-macos-aarch64.tar.gz
 26- https://zmx.sh/a/zmx-0.1.0-macos-x86_64.tar.gz
 27
 28### homebrew
 29
 30```bash
 31brew tap neurosnap/tap
 32brew install zmx
 33```
 34
 35### src
 36
 37- Requires zig `v0.15`
 38- Clone the repo
 39- Run build cmd
 40
 41```bash
 42zig build -Doptimize=ReleaseSafe --prefix ~/.local
 43# be sure to add ~/.local/bin to your PATH
 44```
 45
 46## usage
 47
 48> [!IMPORTANT]
 49> Press `ctrl+\` to detach from the session.
 50
 51```
 52Usage: zmx <command> [args]
 53
 54Commands:
 55  [a]ttach <name> [command...]  Create or attach to a session
 56  [d]etach                      Detach all clients from current session  (ctrl+\ for current client)
 57  [l]ist                        List active sessions
 58  [k]ill <name>                 Kill a session and all attached clients
 59  [v]ersion                     Show version information
 60  [h]elp                        Show this help message
 61```
 62
 63### examples
 64
 65```bash
 66zmx attach dev              # start a shell session
 67zmx attach dev nvim .       # start nvim in a persistent session
 68zmx attach build make -j8   # run a build, reattach to check progress
 69zmx attach mux dvtm         # run a multiplexer inside zmx
 70```
 71
 72## shell prompt
 73
 74When you attach to a zmx session, we don't provide any indication that you are inside `zmx`. We do provide an environment variable `ZMX_SESSION` which contains the session name.
 75
 76We recommend checking for that env var inside your prompt and displaying some indication there.
 77
 78### fish
 79
 80Place this file in `~/.config/fish/config.fish`:
 81
 82```fish
 83functions -c fish_prompt _original_fish_prompt 2>/dev/null
 84
 85function fish_prompt --description 'Write out the prompt'
 86  if set -q ZMX_SESSION
 87    echo -n "[$ZMX_SESSION] "
 88  end
 89  _original_fish_prompt
 90end
 91```
 92
 93### bash
 94
 95todo.
 96
 97### zsh
 98
 99Place this in `.zshrc`, update current `$PROMPT/$PS1` to `BASE_PROMPT`
100
101```zsh
102BASE_PROMPT=$PS1/$PROMPT
103PROMPT="${ZMX_SESSION:+[$ZMX_SESSION]} $BASE_PROMPT"
104```
105
106## philosophy
107
108The entire argument for `zmx` instead of something like `tmux` that has windows, panes, splits, etc. is that job should be handled by your os window manager. By using something like `tmux` you now have redundent functionality in your dev stack: a window manager for your os and a window manager for your terminal. Further, in order to use modern terminal features, your terminal emulator **and** `tmux` need to have support for them. This holds back the terminal enthusiast community and feature development.
109
110Instead, this tool specifically focuses on session persistence and defers window management to your os wm.
111
112## ssh workflow
113
114Using `zmx` with `ssh` is a first-class citizen. Instead of `ssh`ing into your remote system with a single terminal and `n` tmux panes, you open `n` terminals and run `ssh` for all of them. This might sound tedious, but there are tools to make this a delightful workflow.
115
116First, create an `ssh` config entry for your remote dev server:
117
118```bash
119Host = d.*
120    HostName 192.168.1.xxx
121
122    RemoteCommand zmx attach %k
123    RequestTTY yes
124    ControlPath ~/.ssh/cm-%r@%h:%p
125    ControlMaster auto
126    ControlPersist 10m
127```
128
129Now you can spawn as many terminal sessions as you'd like:
130
131```bash
132ssh d.term
133ssh d.irc
134ssh d.pico
135ssh d.dotfiles
136```
137
138This will create or attach to each session and since we are using `ControlMaster` the same `ssh` connection is reused for every call to `ssh` for near-instant connection times.
139
140Now you can use the [`autossh`](https://linux.die.net/man/1/autossh) tool to make your ssh connections auto-reconnect. For example, if you have a laptop and close/open your laptop lid it will automatically reconnect all your ssh connections:
141
142```bash
143autossh -M 0 -q d.term
144```
145
146Or create an `alias`/`abbr`:
147
148```fish
149abbr -a ash "autossh -M 0 -q"
150```
151
152```bash
153ash d.term
154ash d.irc
155ash d.pico
156ash d.dotifles
157```
158
159Wow! Now you can setup all your os tiling windows how you like them for your project and have as many windows as you'd like, almost replicating exactly what `tmux` does but with native windows, tabs, splits, and scrollback! It also has the added benefit of supporting all the terminal features your emulator supports, no longer restricted by what `tmux` supports.
160
161## socket file location
162
163Each session gets its own unix socket file. Right now, the default location is `/tmp/zmx-{uid}`. You can configure this using environment variables:
164
165- `TMPDIR` => overrides `/tmp`
166- `ZMX_DIR` => overrides `/tmp/zmx-{uid}`
167
168## debugging
169
170We store global logs for cli commands in `/tmp/zmx-{uid}/logs/zmx.log`. We store session-specific logs in `/tmp/zmx-{uid}/logs/{session_name}.log`. These logs rotate to `.old` after 5MB.
171
172## a note on configuration
173
174We are evaluating what should be configurable and what should not. Every configuration option is a burden for us maintainers. For example, being able to change the default detach shortcut is difficult in a terminal environment.
175
176## a smol contract
177
178- Write programs that solve a well defined problem.
179- Write programs that behave the way most users expect them to behave.
180- Write programs that a single person can maintain.
181- Write programs that compose with other smol tools.
182- Write programs that can be finished.
183
184## impl
185
186- The `daemon` and client processes communicate via a unix socket
187- Both `daemon` and `client` loops leverage `poll()`
188- Each session creates its own unix socket file
189- We restore terminal state and output using `libghostty-vt`
190
191### libghostty-vt
192
193We use `libghostty-vt` to restore the previous state of the terminal when a client re-attaches to a session.
194
195How it works:
196
197- user creates session `zmx attach term`
198- user interacts with terminal stdin
199- stdin gets sent to pty via daemon
200- daemon sends pty output to client *and* `ghostty-vt`
201- `ghostty-vt` holds terminal state and scrollback
202- user disconnects
203- user re-attaches to session
204- `ghostty-vt` sends terminal snapshot to client stdout
205
206In this way, `ghostty-vt` doesn't sit in the middle of an active terminal session, it simply receives all the same data the client receives so it can re-hydrate clients that connect to the session. This enables users to pick up where they left off as if they didn't disconnect from the terminal session at all. It also has the added benefit of being very fast, the only thing sitting in-between you and your PTY is a unix socket.
207
208## prior art
209
210Below is a list of projects that inspired me to build this project.
211
212### shpool
213
214You can find the source code at this repo: https://github.com/shell-pool/shpool
215
216`shpool` is a service that enables session persistence by allowing the creation of named shell sessions owned by `shpool` so that the session is not lost if the connection drops.
217
218`shpool` can be thought of as a lighter weight alternative to tmux or GNU screen. While tmux and screen take over the whole terminal and provide window splitting and tiling features, `shpool` only provides persistent sessions.
219
220The biggest advantage of this approach is that `shpool` does not break native scrollback or copy-paste.
221
222### abduco
223
224You can find the source code at this repo: https://github.com/martanne/abduco
225
226abduco provides session management i.e. it allows programs to be run independently from its controlling terminal. That is programs can be detached - run in the background - and then later reattached. Together with dvtm it provides a simpler and cleaner alternative to tmux or screen.
227
228### dtach
229
230You can find the source code at this repo: https://github.com/crigler/dtach
231
232A simple program that emulates the detach feature of screen.
233
234dtach is a program written in C that emulates the detach feature of screen, which allows a program to be executed in an environment that is protected from the controlling terminal. For instance, the program under the control of dtach would not be affected by the terminal being disconnected for some reason.
235
236## comparison
237
238| Feature                        | zmx | shpool | abduco | dtach | tmux |
239| ------------------------------ | --- | ------ | ------ | ----- | ---- |
240| 1:1 Terminal emulator features | ✓   | ✓      | ✓      | ✓     | ✗    |
241| Terminal state restore         | ✓   | ✓      | ✗      | ✗     | ✓    |
242| Window management              | ✗   | ✗      | ✗      | ✗     | ✓    |
243| Multiple clients per session   | ✓   | ✗      | ✓      | ✓     | ✓    |
244| Native scrollback              | ✓   | ✓      | ✓      | ✓     | ✗    |
245| Configurable detach key        | ✗   | ✓      | ✓      | ✓     | ✓    |
246| Auto-daemonize                 | ✓   | ✓      | ✓      | ✓     | ✓    |
247| Daemon per session             | ✓   | ✗      | ✓      | ✓     | ✗    |
248| Session listing                | ✓   | ✓      | ✓      | ✗     | ✓    |