ztmux 3.7.10

A Rust port of tmux — the full terminal multiplexer, server and client
Documentation
███████╗████████╗███╗   ███╗██╗   ██╗██╗  ██╗
╚══███╔╝╚══██╔══╝████╗ ████║██║   ██║╚██╗██╔╝
  ███╔╝    ██║   ██╔████╔██║██║   ██║ ╚███╔╝
 ███╔╝     ██║   ██║╚██╔╝██║██║   ██║ ██╔██╗
███████╗   ██║   ██║ ╚═╝ ██║╚██████╔╝██╔╝ ██╗
╚══════╝   ╚═╝   ╚═╝     ╚═╝ ╚═════╝ ╚═╝  ╚═╝

CI Docs Port Report Parity vs tmux Status Reference License: MIT

[TMUX, REWRITTEN IN RUST — DONE RIGHT]

"The world's first 100%-functional tmux in Rust — the whole multiplexer, server and client, running."

"Not a wrapper. Not control mode. The multiplexer itself."

"Ported against the C, verified against the C — byte for byte, 1080/1080 parity cases passing."

[FROM SOURCE, NOT FROM SCRATCH]

ztmux is a from-source port of tmux to Rust — the whole program: the server, the client, the grid/screen model, the input parser, layouts, the command language, formats, and the terminal back end. It is not a wrapper around the tmux binary and it is not control mode (tmux -CC); it is tmux, reimplemented. The port stands on the upstream tmux C sources, vendored under vendor/ as a plain, read-only, SHA-pinned copy — the source of truth every module is diffed against. Correctness is measured, not claimed — a parity suite runs identical inputs through the real tmux and ztmux and diffs them byte-for-byte, and an anti-drift gate fails the build if a Rust function is added whose name has no counterpart in the tmux C source.

Docs · Port Report · Parity · ztmux-core · tmux


Table of Contents


[0x00] OVERVIEW

A terminal multiplexer keeps your shells alive: split panes, detach and reattach, script the whole thing. tmux is the reference implementation, ~30 years of C. ztmux ports that C to Rust one subsystem at a time, holding behavior identical to upstream at every step. It opens its own socket namespace (ztmux-<uid>) so it never collides with a running tmux.

Status: 100% functional. The port builds, runs, and self-hosts — ztmux new-session, splits, detach/reattach, the command language, formats, and layouts all work — and the parity suite is green at 1080/1080 (100%) against the vendored tmux. Every bug the harness found has been root-caused and fixed (see docs/BUGS.md).

On top of the port, ztmux ships original subcommands with no tmux counterpart — live dashboards and JSON-emitting inspectors for the running server (ztmux --help, and [0x08]).

Distinct from ztmux-core, a native tmux client engine that speaks the wire protocol to an existing server for GUI hosts. This repo is the whole server + client. The two pair: ztmux and ztmux-core pin the identical PROTOCOL_VERSION = 8 (src/ported/tmux_protocol_h.rs here, src/transport.rs there), so a GUI drives this server over the same wire protocol — and because both ends are MenkeTechnologies-owned, upstream tmux's release cadence can never break that contract.


[0x01] INSTALL

Requires a C libevent (tmux's event-loop library) and a terminfo database (ncurses), exactly like tmux.

# macOS
brew install libevent ncurses
cargo build --release
cargo run --release -- new-session       # start a server + session, like `tmux`

# Debian / Ubuntu
sudo apt-get install libncurses-dev libevent-dev
cargo build --release

The binary is ztmux. On macOS the build links Homebrew's libevent automatically; set TMUX_RS_DISABLE_HOMEBREW_LIBS=1 to skip the Homebrew search path. Linking can be forced with the static / dynamic features.


[0x02] HOW THE PORT IS BUILT

One reference, vendored under vendor/ as a plain committed copy (the clone is self-contained and never depends on an upstream staying alive):

Path Upstream Role
vendor/tmux/ tmux/tmux (C) Source of truth. Every ported module is diffed against its C counterpart.
src/ The port. The crate we own and evolve. Edit here.

Cargo.toml declares its own [workspace] excluding vendor/, so Cargo never walks into the reference. Every ported function carries a back-link comment to its C origin, e.g.:

// vendor/tmux/grid.c:320  grid_create()
pub fn grid_create(sx: u32, sy: u32, hlimit: u32) -> *mut grid {

[0x03] "DONE RIGHT"

The port began as a faithful but almost-entirely-unsafe mechanical transpile. "Done right" is turning that working skeleton into good Rust without ever drifting from tmux:

  1. Start from a working skeleton — a running program to refactor, not a blank page.
  2. Shrink the unsafe surface — replace raw-pointer intrusive lists and C-isms with safe Rust where behavior allows.
  3. Verify against C at every step — a module isn't "ported" until it matches the C reference (see [0x04]).
  4. Keep it greencargo build and cargo clippy stay clean as code comes over.

[0x04] PARITY VS SYSTEM tmux

ztmux is a port of tmux, so "correct" means tmux itself. The parity suite runs the same inputs through the real tmux (reference) and ztmux (port) and compares byte-for-byte — the same shape as the sibling ports (zshrs vs zsh, strykelang vs perl).

bash parity/run_parity.sh --summary       # ztmux vs tmux, every case

Cases live in parity/cases/ as tmux FORMAT strings (#{e|+|:2,3}) or shell scenarios. It earns its keep: it root-caused a #{l:…} server crash to a dropped pointer increment in format_unescape, fixed even-horizontal layout rounding and #{pane_current_command} on macOS, regex backreferences, #{!:}, named buffers, loop variables, and the last layout divergences — each pinned to a single case and then ported correctly. It now stands at 1080/1080 cases passing (100%), byte-for-byte vs the vendored tmux, with zero known divergences. See parity/PARITY_ROADMAP.md and the bug log docs/BUGS.md.


[0x05] ANTI-DRIFT GATE — NO FAKE FUNCTIONS

A port can be faked by inventing Rust-only "helper" functions that don't exist in tmux, inflating apparent completeness. tests/ported_fn_names_match_c.rs fails the build when a free fn is added to src/ whose name has no counterpart in vendor/tmux. Pre-existing exceptions (libc/libevent wrappers, Rust glue) are frozen in tests/data/fake_fn_allowlist.txt — an audit trail to burn down, not a free pass. The port report tracks C→Rust coverage per function.


[0x06] LAYOUT

ztmux/
├── Cargo.toml         # the ztmux crate (own workspace root; excludes vendor/)
├── build.rs           # lalrpop (command grammar) + libevent linking
├── src/               # THE PORT — edit here
│   └── extensions/    # original ztmux subcommands (see [0x08]); not a port
├── completions/       # _ztmux zsh completion (generated by scripts/)
├── parity/            # ztmux-vs-tmux byte-for-byte suite + roadmap
├── scripts/           # gen_port_report.py, annotate_c_links.py
├── tests/             # anti-drift gate + allowlist
├── docs/              # GH Pages hub: index / report / port_report
├── vendor/
│   └── tmux/          # C source of truth  (read-only reference)
└── COPYING            # ISC (upstream notices)

[0x07] PORTING WORKFLOW

  1. Pick a subsystem (a .rs module under src/).
  2. Open its C counterpart in vendor/tmux/.
  3. Bring the Rust toward correct, idiomatic, memory-safe Rust — replacing the raw-pointer / unsafe C-isms with safe equivalents where behavior allows.
  4. Keep it building (cargo build) and lint-clean (cargo clippy), and green against the parity suite (bash parity/run_parity.sh) at every step.

[0x08] EXTENSIONS

Beyond the port, ztmux adds original subcommands with no upstream counterpart, under src/extensions/. They live apart from the ported core — and are exempt from the anti-drift gate ([0x05]) — precisely because they are not tmux. Each is either a read-only query over the running server (built on the same structured list-* -o json output) or a small mutating helper, and every one accepts -o json / --json for scripting.

They fall into a few families:

  • Inspection — one-shot, pipeable views of the live server: process tables (ps, pstree, mem, state, elapsed), geometry (size, density, layouts, solo), directories and repositories (cwd, project, git, remote, ahead, changes, stash, commit, conflicts, vcs, worktree, submodules, gone), network (ssh, net, ports), clients (who, readonly, idle, viewers, connected, constrain, keytable, control, utf8), and configuration (hooks, keys, monitor, remain, sync, limit, visual, mouse, …).
  • Live TUIsdashboard (full-screen server monitor), switcher (fuzzy session/window/ pane picker), watch (top-like per-pane process monitor).
  • Actionsprune, equalize, revive, clearall, retitle, bcast, layout, and pick (batch sync/unmark/clear over a multi-pane mark set).
  • Automationtriggers runs any ztmux command when a regex matches a pane's output (rules in ~/.ztmux/triggers.json, armed with ztmux triggers arm), reviving tmux's removed monitor-content as a general sense→act loop. Add rules without touching the JSON via the inline wizard: ztmux triggers wizard (or ztmux triggers add <name> <pane> <match> <action>).
  • Ratatui UI (on by default) — original interactive surfaces rendered with ratatui rather than tmux's server draw: a which-key hint bar on the prefix, a floating command palette with inline Tab/arrow completion, ratatui clock and display-panes, edit-scrollback-in-$EDITOR (prefix e), and multi-pane selective sync — mark panes (prefix C-s), sync the set (prefix M). Sync state is shown on the pane border — synced (red), selected (orange), trigger-armed (cyan) — which output can never overwrite. Opt into zellij-style pane frames with @ztmux-zellij-mode on (off by default): every pane is inset by a one-cell ring (like zellij, so a program can never draw on the frame) and gets a rounded box with its name in the top border; the box recolours for sync state. In this mode prefix + toggles a zellij-style pane stack — the focused pane fills the column, the rest collapse to one-row title bars (ztmux stack / :stack). A zellij-style tab bar of windows along the top (session badge, active tab highlighted) is a separate toggle — ztmux tabs on / :tabs — which restyles the status line and restores your prior status settings on tabs off. A zellij-style session manager (ztmux sessions / :sessions) opens a ratatui list of sessions: type to filter, Enter switches, Ctrl-r renames, Ctrl-x kills (with confirm), Ctrl-n makes a new one. prefix C-f toggles a zellij-style floating pane — a persistent pane that floats above the tiled layout in a popup (kept live in a hidden holding session, so its state survives between toggles; press prefix C-f again to hide it). ztmux modal on (opt-in) installs zellij-style modal keybindings: Ctrl-p pane mode, Ctrl-t tab, Ctrl-n resize, Ctrl-s scroll, Ctrl-o session, Ctrl-g lock — each a sticky key table entered without a prefix; the hint bar (turned on automatically) shows the current mode's keys. modal off removes the entry keys and restores the prefix. Since the Ctrl-* keys are intercepted globally (the zellij trade-off), it is off by default. ztmux resurrect save / ztmux resurrect restore persist the whole server across restarts (zellij-style resurrectable sessions): save writes every session/window/pane — layout, cwd and command — to ~/.ztmux/resurrect/, and restore recreates them (windows, panes and exact tiled geometry; shell panes come back in their cwd, --run also re-runs saved commands). Existing sessions are never clobbered; resurrect list shows saved snapshots. For continuum-style automatic persistence, set -g @ztmux-resurrect-auto on: the first client to attach spawns a detached daemon that re-saves every 15 minutes (pidfile-guarded, one per server); add set -g @ztmux-resurrect-restore on and it also restores the last snapshot once on a fresh server start. ztmux open / :open (also in the pane menu) scans the current pane for URLs and file paths and shows a ratatui picker — Enter opens the selection (a URL in open/xdg-open, a file in $EDITOR at its file:line, a directory revealed), y copies it (tmux buffer + OS clipboard). Like tmux-open / tmux-urlview, built in. Settings (all set -g): @ztmux-ratatui off disables the whole ratatui renderer for a classic plain-tmux server (on by default; takes effect on the next redraw); @ztmux-hint on shows the prefix hint bar (off by default); @ztmux-zellij-mode on enables the framed/inset mode (off by default; @ztmux-pane-names is a back-compat alias); @ztmux-pane-name-format overrides the frame name with a tmux format (e.g. #{pane_index}: #{pane_current_command}). With @ztmux-ratatui off the default draw path and the byte-for-byte parity suite are untouched.

Run ztmux --help for the current list, or man ztmux for the full reference — each extension has its own entry under the EXTENSIONS section, and the zsh completion (completions/_ztmux) describes every one inline.


[0xFF] LICENSE

MIT — see LICENSE. ztmux is a derivative work of tmux (Nicholas Marriott et al.), ISC; the original notices are retained in COPYING and under vendor/.