Skip to content

Repository files navigation

vvmux

vvmux is a detachable terminal multiplexer for Vivido. A persistent per-session server owns shell PTYs, terminal grids, scrollback, layout, and virtual Vivid producer state. The foreground client owns the current terminal and the outer Vivido capability, so detaching never gives a background daemon access to a window token.

The implementation requires Rust 1.88 or newer and supports macOS, Linux, Windows 11, and Windows 10 version 1809/build 17763 or newer on x86_64-pc-windows-msvc. It does not import or depend on the untracked Zellij or Alacritty reference trees. Windows ARM64 is compile-checked but remains a follow-up runtime target.

Build and install

cd vvmux
cargo build --release
tic -x -o "$HOME/.terminfo" terminfo/vvmux.info
install target/release/vvmux "$HOME/.local/bin/vvmux"

The primary Windows distribution is the signed Vivido Suite EXE/MSI. It installs vvmux.exe beside Vivido, Vivi, and vvssh below %LOCALAPPDATA%\Programs\Vivido, adds that directory to the user PATH, refuses upgrade or uninstall while a live session exists, and preserves %APPDATA%\vvmux, including config.toml. The older signed ZIP scripts remain available only for transition/testing and are not the primary public release path.

When the terminfo entry cannot be found, pane shells use TERM=xterm-256color. They always receive TERM_PROGRAM=vvmux and COLORTERM=truecolor.

On Windows, the configured [general].shell is used first, then %COMSPEC%, then the system cmd.exe. The default config is %APPDATA%\vvmux\config.toml; owner-only runtime registries live below %LOCALAPPDATA%\vvmux\runtime. Pane shells receive an exact 127.0.0.1 virtual Vivid endpoint and VIVID_ANCHOR_TRANSPORT=conpty. Remote Unix applications may still need the supplied terminfo/vvmux.info installed on the remote host.

Commands

vvmux                              attach/create `default`
vvmux new [-s NAME] [-d]           create a session
vvmux attach [-t NAME] [--replace] attach exactly by name
vvmux list                         list live owner sessions
vvmux kill-session -t NAME         terminate a session and its process groups
vvmux msg [-t NAME] COMMAND        automate or inspect one pane directly
vvmux token create [--rotate]      create/rotate the VVWS bearer token
vvmux serve [OPTIONS]              run the loopback VVWS/1 session gateway
vvmux --config PATH ...            use an explicit strict TOML config

Only one client can be attached to a session. --replace sends a clean detach to the old client before the new client is admitted.

Pane automation

vvmux msg connects directly to the owner-only hidden session server. It does not type vvmux prefix keys and does not replace the foreground client, so an attached Vivido window keeps rendering normally while another shell controls or observes individual panes.

The session target is resolved from --target, then VVMUX_SESSION, then default. A pane target is resolved from --pane-id, then a same-session VVMUX_PANE_ID, then the focused pane in the active tab. close-pane deliberately has no focused fallback. Pane shells inherit the exact VVMUX_SESSION, VVMUX_TAB_ID, and VVMUX_PANE_ID values for their owner.

export VVMUX_SESSION=agent

right=$(vvmux msg split vertical --pane-id 1 | jq -r .new_pane_id)
bottom_left=$(vvmux msg split horizontal --pane-id 1 | jq -r .new_pane_id)
bottom_right=$(vvmux msg split horizontal --pane-id "$right" | jq -r .new_pane_id)

vvmux msg typing --pane-id "$right" 'echo hello from top-right'
vvmux msg key --pane-id "$right" Enter
vvmux msg wait text --pane-id "$right" 'hello from top-right'
vvmux msg get-text --pane-id "$right"

Available commands are:

capabilities
list-panes
inspect [--pane-id ID]
inspect-media [--pane-id ID]
split vertical|horizontal [--pane-id ID]
focus [--pane-id ID]
close-pane --pane-id ID
typing TEXT [--pane-id ID]
key KEY [--mods Shift,Alt,Ctrl,Super] [--repeat N] [--pane-id ID]
paste TEXT [--pane-id ID]
get-text [--rows N] [--pane-id ID]
get-grid [--start-line LINE --row-count N | --since-screen SEQ] [--pane-id ID]
wait text TEXT [--regex] [--after-screen SEQ] [--timeout DURATION] [--pane-id ID]
wait screen-change [--after-screen SEQ] [--timeout DURATION] [--pane-id ID]
wait screen-stable [--quiet DURATION] [--after-screen SEQ] [--timeout DURATION] [--pane-id ID]
wait rendered --after-session SEQ [--timeout DURATION]
wait exit [--timeout DURATION] [--pane-id ID]
wait media [--after-virtual REV] [--after-outer REV] [--timeout DURATION] [--pane-id ID]

typing, key, and paste bypass prefix and copy-mode handling and acknowledge only after the pane PTY writer flushes all generated bytes. paste honors bracketed-paste mode and prevents an embedded bracketed-paste terminator. Keys include Unicode scalars, navigation keys, F1 through F35, and keypad keys; cursor and keypad application modes are honored.

get-text writes exact Unicode without adding a newline. Its default view is the pane as vvmux is currently displaying it, including copy-mode scroll position. --rows N instead returns the newest N physical rows ending at the live bottom. Trailing unused cells are removed, soft-wrapped rows are joined, hard row boundaries remain newlines, and literal tabs, combining text, and wide characters are preserved.

get-grid prints JSON with pane/screen/session sequences, signed retained-grid line numbers, viewport rows, cursor and copy selection, active screen and modes, a deduplicated symbolic style table, and every physical cell. Colors are default, indexed, or RGB because the hidden server does not own the outer Vivido palette. Cells retain tabs, styled blanks, wide continuations, combining characters, hyperlinks, and wrap state. --since-screen returns current replacement rows when retained history is sufficient; full plus gap identifies an evicted or invalidated delta. Vivid media, pane frames, status lines, and other panes are intentionally excluded.

Structured observations and waits print JSON; split prints the new pane ID and committed session sequence. Input, focus, and close are silent on success. Waits default to 30 seconds and accept durations from 1 ms through 24 hours. wait rendered means the attached client wrote and acknowledged a composite terminal frame covering the requested session sequence; it does not claim that Vivido presented a GPU frame. Use vivido msg wait frame when GPU presentation matters.

inspect-media reports only pane-scoped, sanitized metadata: virtual scene/projection revisions, the independently acknowledged outer projection revision, source kind/lifecycle/revision/epoch, separate inner and outer attachment generations, visibility and milestones, bounded queue/credit utilization, and node geometry. It never includes capability tokens, media tickets, payload bytes, hashes, or derived keys. wait media waits for either requested revision domain to advance; when both --after-virtual and --after-outer are supplied, both predicates must become true.

Requests and input are limited to 1 MiB, decoded replies to 16 MiB, row requests and key repeats to 1,000, and regular expressions to 8 KiB. The server bounds connections, in-flight requests, waiters, response work, screen-delta history, and recent process-exit tombstones. VVMX 9 is a hard private-protocol cutover, so sessions created by older binaries must be restarted after upgrading.

Network session gateway

The optional default-enabled server-capability builds vvmux serve, a foreground loopback-only WebSocket gateway for xterm.js-style clients. It is one gateway for all owner sessions; individual hidden session servers remain private and continue to use same-user VVMX IPC.

Create a token, configure at least one exact browser origin, and start the gateway:

vvmux token create
vvmux serve --allow-origin http://127.0.0.1:3000

The default address is 127.0.0.1:7880. Non-loopback binds, missing/null/wildcard origins, and unauthenticated discovery are rejected. Use SSH port forwarding or a trusted TLS reverse proxy for remote access. Possession of the bearer token is equivalent to shell access to every vvmux session owned by that OS user. The raw token is printed once; only its hash is retained in an owner-only record.

The gateway lists, creates, and exclusively attaches to sessions. It serves no HTML or JavaScript and does not expose session kill operations. Plain xterm.js clients can attach text-only; Vivido.js's built-in connectVvmux coordinator also routes Vivid 1.1 over authenticated binary WebSockets, so images and timed media remain synchronized with the session. See VVWS-1.md for the normative wire contract and client integration shape.

Default keys

The prefix is Ctrl-b.

Key Action
Ctrl-b Ctrl-b Send a literal Ctrl-b
Ctrl-b % / Ctrl-b " Split left/right or top/bottom
Ctrl-b Arrow Focus direction
Ctrl-b Ctrl-Arrow Resize by one cell
Ctrl-b c, n, p, 09 Create/cycle/select tabs
Ctrl-b x, then y Close the focused pane
Ctrl-b z Toggle zoom
Ctrl-b f / Ctrl-b F Create a floating pane / show or hide ordinary floats
Ctrl-b P Pin or unpin the focused floating pane
Ctrl-b m / Ctrl-b r Enter floating move / resize mode
Ctrl-b d Detach
Ctrl-b [ / Ctrl-b ] Copy mode / paste copy buffer

Copy mode accepts arrows, Page Up/Down, Space to start selection, Enter to copy, and q or Escape to cancel. Copies are capped at 1 MiB and the client emits OSC 52. Paste honors the focused application's bracketed-paste mode and neutralizes embedded bracketed-paste terminators. On Windows, the outer terminal's bracketed-paste mode follows the focused pane, so Ctrl+V from Windows Terminal or Vivido is delivered with bracket markers only when that pane requested them.

In floating move or resize mode, arrows step by one cell and Shift-Arrow steps by five. Enter commits and Escape restores the rectangle captured on entry. Zoom hides every other pane, including pinned floats, without mutating the tiled tree, floating rectangles, visibility, pins, z-order, or focus.

Mouse clicks focus panes. Tiled border drags resize. On a floating pane, the top title frame moves the pane, while side/bottom frames and corners resize it; drag geometry is based on the press-time rectangle and total pointer delta. Mouse input is translated into pane-local SGR coordinates when the application requested mouse reporting; Shift cancels or prevents pane dragging and forces vvmux scrolling/copy behavior.

Strict floating defaults live under [floating]: default_width_percent and default_height_percent accept 10–100, and border_drag_margin accepts 1–4. New floats are centered at 60% by 60% by default, with a minimum 4-by-2 content area plus frame.

Vivid behavior

Every pane receives a distinct 256-bit VIVID_TOKEN and the session-wide virtual presenter endpoint. The server validates producer handshakes, single-use media tickets, request/object scope, source and scene quotas, monotonic packet/frame sequences, images, rasters, transactions, and authenticated anchor markers.

Static encoded images and the latest raster are retained within the configured aggregate budget. Timed audio/video continues to receive credits while detached but payloads are discarded. On a new projection, live video gets NEED_KEYFRAME; relay-only loss accepts the next keyframe in the current epoch, while decoder loss can demand a greater epoch. Only the requested keyframe and later packets are eligible for forwarding. Audio resumes with newly arriving packets. EOS video does not acquire a reconstructed poster.

The foreground client reconciles stable source and (producer, node, fragment) identities into the current Vivido session, reuses unchanged sources/media channels, resolves virtual anchors, and applies an exact negotiated clip rectangle for every pane. Higher pane outer rectangles are opaque media occluders, including their frames. A logical media node is split into at most eight exact signed-32.32 fragments that share one source; a projection contains at most 256 upstream nodes and omits lower-priority background media when that budget is exhausted. Source creation and scene control are pipelined and correlated; uncertain partial reconciliation reconnects once from the newest authoritative snapshot.

Each outer media connection has a bounded source-specific writer and credit ledger. A blocked video socket cannot stop linked audio, another pane, terminal rendering, or control. The virtual presenter's one-packet grant is returned after the outer record write succeeds, so linked audio pre-roll reaches Vivido before PLAY without inheriting an outer RTT stop-and-wait. The playing snapshot is ordered before the first post-PLAY keyframe. EOS marks the end of submission but leaves already-buffered outer video and linked audio playing until explicit stop or source teardown.

vvmux validates and forwards the Vivid portable profiles without transcoding, including canonical Opus, Vorbis, and FLAC initialization. Outer NEED_KEYFRAME and source loss are routed back to the matching inner source without replacing unrelated topology.

Control uses inherited VIVID_ENDPOINT. If inherited VIVID_ENDPOINT_BULK is present, only the foreground bridge uses it for non-control outer connections; the hidden server and pane processes never receive the outer endpoint or token. A bulk connection may fall back to the primary endpoint only before ATTACH_CHANNEL consumes its ticket.

If the outer terminal has no Vivid capability, or its presenter rejects node-clip-rect-v1, terminal use continues without media and the client emits a single status/title warning.

Current scope

Implemented: native Unix sockets and owner-restricted Windows named pipes; Unix PTYs and ConPTY/Job Object panes; Unix and Windows console clients; named detachable sessions, tabs, tiled and tab-scoped floating/pinned shell panes, zoom, scrollback/copy/paste, ordered overlap composition, mouse focus/move/resize/forwarding, status line, strict TOML, VVMX IPC, exact fragment-aware pane media occlusion, static rehydration, timed-media headless semantics, full-duplex outer control, linked A/V projection, and optional bulk-media endpoint discovery.

Intentionally absent: plugins, a bundled web UI, direct non-loopback/TLS serving, command panes, arbitrary action sockets, startup layout scripts, stacked panes, pane-class conversion, multi-pane selection, scrollback editing/search, source transcoding, mirrored multi-client sessions, WinPTY, MSI/service installs, machine-wide PATH changes, and arbitrary configured shell argument lists.

Windows troubleshooting

Host terminal Supported profile
Windows Terminal (current stable) VT input/output, Unicode, alternate screen, mouse, focus, OSC 52, title, paste, restoration
Visual Studio integrated terminal (current stable) Same profile; manual release certification required
Windows 10/11 conhost Same profile; pixel cell metrics may be zero
  • “ConPTY is required” means the OS is older than build 17763 or the ConPTY API is unavailable.
  • A named-pipe admission failure usually means the client and server run as different Windows users or one process is elevated under a different token. VVMX is deliberately same-user only.
  • Attachment requires real stdin/stdout console handles; new -d, list, and kill-session remain usable with redirected handles.
  • The RAII client restores console modes, code pages, title, cursor, alternate screen, mouse, focus, and bracketed-paste state on normal errors and unwinds.

About

Terminal Multiplexer for Vivido

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages