termio: add zmx backend for persistent terminal sessions
MD
7mo ago+1112+1112+0123456789012345678901234567890123456789-48-48-01234567890123456789
Summary
This PR adds a new zmx termio backend to Ghostty and wires it through config + embedded APIs so surfaces can attach to persistent zmx sessions instead of always spawning a direct PTY subprocess.
Why
cmux persistence work (manaflow-ai/cmux#143, manaflow-ai/cmux#480) needs terminal processes to outlive the app. The existing exec backend ties process lifetime to the Ghostty surface, so restart always loses shell state. zmx provides a daemon-owned PTY/session model that can survive app restarts and reconnect cleanly.
What Changed
1) New zmx backend in termio
- Added
src/termio/Zmx.zig. - Added
zmxvariant tosrc/termio/backend.zigunions/dispatch. - Exported backend in
src/termio.zig.
Backend behavior:
- Connects to zmx over Unix domain socket.
- Supports session creation (
zmx run <session>) when configured. - Uses framed IPC for input/output/resize.
- Sends Init + immediate Resize on attach.
- On unexpected socket disconnect, emits
.child_exitedmetadata so surface UX remains coherent. - On planned shutdown, sends Detach and exits cleanly without false error reporting.
2) Surface/backend selection and fallback
- Updated
src/Surface.zigto choose zmx backend whenzmx-sessionis set. - If zmx binary is unavailable, falls back to
execwith warning (no crash/no hard failure).
3) Config + embedded API surface
- Added config keys in
src/config/Config.zig:zmx-sessionzmx-create
- Added per-surface embedded options in
src/apprt/embedded.zigand synced C header:zmx_sessionzmx_createzmx_mode
- Updated
include/ghostty.hto keep C API struct in sync.
zmx_mode is used for split/tab inheritance in embedded mode: new surfaces created from a zmx-backed surface stay in zmx mode, each with a fresh session.
4) Build compatibility in this branch
- Added a local
uucodefallback vendoring path and compatibility wiring (deps-uucode, unicode build updates) to keep fork builds stable in environments where fetching unicode dependency sources is constrained.
Design Rationale
- Backend extension point: implemented as a first-class termio backend variant, not a special-case branch inside
exec, so behavior stays isolated and maintainable. - Per-surface control: zmx session is configured at surface creation time (embedded options), which maps naturally to split/tab semantics.
- Graceful degradation: missing zmx does not break terminal creation; it downgrades to
exec. - Socket-readiness gating: session creation is treated as successful when the socket is connectable, avoiding false negatives from launcher exit-code quirks.
Validation
Zmx.zigincludes targeted tests for IPC framing/buffer behavior and disconnect metadata.- End-to-end integration validated through cmux Debug builds and persistence workflows using this Ghostty branch.
Related
- Companion cmux PR: https://github.com/manaflow-ai/cmux/pull/510
- Enables:
manaflow-ai/cmux#480 - Follow-up to closed cmux tracking issue:
manaflow-ai/cmux#143
Summary by CodeRabbit
-
New Features
- Added a zmx terminal backend and new options to specify/create/use zmx sessions (zmx-session, zmx-create, zmx-mode). Surfaces propagate zmx settings to newly opened surfaces.
-
Behavior
- zmx backend is selected when configured; abnormal-exit messages include session info. Surfaces now emit a persistent “child disconnected” notification for backend disconnects.
-
Tests
- Added unit tests for zmx IPC framing, config parsing, socket/path handling, and disconnect metadata.