Capture server state
Use runtime:connect(options):await() to bind a borrowed tmux daemon, then
server:snapshot(options):await() to capture its state. Every live operation
returns a Request. The snapshot example prints
each unique pane and its window ID using the public API.
Pass an absolute binary to connect, and usually an absolute socket_path.
Optional config_path defaults to /dev/null. Connection options are copied
before dispatch. The library never starts a server. Importing the library and
inspecting captured records perform no I/O.
Without a socket_path, connect reads LIBTMUX_SOCKET_PATH, a socket path
(tmux -S), then LIBTMUX_SOCKET, a socket name (-L). The path wins when
both are set, and a variable that is empty or only whitespace counts as unset.
A socket_path option leaves both unread. With neither variable set, tmux gets
no socket and picks the server itself: the one in TMUX inside tmux, otherwise
the default socket under TMUX_TMPDIR. LIBTMUX_SOCKET_PATH must be absolute
and name a file.
connect starts no server. If tmux finds none running on the socket it picked,
or on the LIBTMUX_SOCKET name, it fails with invalid_endpoint. A socket
path, from the option or LIBTMUX_SOCKET_PATH, fails with invalid_endpoint
when no file is there, and with uncertain_generation when the file outlived
its server, which tmux leaves behind when it exits.
Connection setup reads the actual daemon’s version and identity and disables tmux autostart. Linux socket binding creates a private socket alias in the selected socket’s parent directory; that directory must permit creation and hard links. Each command checks the original socket and alias, and reads bounded daemon evidence. A socket name, or tmux’s own choice, has no path to alias, so nothing is created: the daemon’s pid, start time and release, which every command carries as a fence, are its only identity. A restart, replaced socket or uncertain connection invalidates the handle. Reconnect explicitly; old references do not bind to reused IDs. The compatibility matrix records Ubuntu CI coverage and local WSL2 checks; macOS remains outside the verified matrix.
server:close():await() closes the handle and its owned connections. It leaves
the borrowed daemon running. The runtime also closes handles when its root
scope finishes. Keep live work inside that scope; returning a handle from
adapter.run does not keep its runtime alive.
Collections and projections¶
Snapshots contain sessions, windows, panes, window_links, clients
and buffers as native selections. Canonical entity collections
deduplicate identity; snapshot.raw preserves contextual listing rows and
their order. A window linked twice has two link rows and one canonical window.
Relationships refer only to captured data. Clients are the attached clients
that tmux exposes through list-clients.
Window links own session_id, window_id, index and active context. Pane
and window IDs remain tmux strings; Lua positions are not tmux indexes.
A daemon with no sessions can still have buffers. Capture reads them without
creating a session.
By default, capture loads every supported field in the catalog.
The fields option maps collection names to nonempty field-name sequences;
omitted collections keep their default projection. Required identity and
relationship fields are added. requested_projections records the caller’s
selection; projections records effective fields, including for empty
collections. Both maps use singular entity names such as pane and
window_link. capabilities.fields records availability for the observed
daemon version; it does not claim every tmux command is supported.
Refresh by calling snapshot again. It returns fresh records. Tables remain
mutable, so do not edit them during traversal. Editing a record does not
refresh the server, another snapshot or its private identity index.
Create a handle with server:handle(snapshot, record). The record’s kind picks
the handle’s class: a SnapshotPane gives a libtmux.Pane, a
SnapshotSession a libtmux.Session, and so on, so LuaLS offers only the
methods tmux accepts for that kind. It copies the record’s
private identity, so edits to exposed id or ref fields cannot redirect it.
handle:reference() returns a separate reference table without I/O.
handle:snapshot():await() explicitly captures fresh state and returns the
matching record. A missing link/index, client/TTY or buffer name returns
target_missing; stale server generations fail before capture. Same-name
client or buffer reuse cannot establish continuous object identity.
Consistency and limits¶
Capture spans multiple commands. acquisition.started and finished are
monotonic milliseconds. Normal capture reports observed relationship races
in races and sets complete to false. Transport failures return an error.
Set strict = true for one additional identity/topology verification pass.
It compares membership and link context, ignoring listing order and volatile
scalar values. A mismatch returns inconsistent_snapshot with the original
snapshot in err.partial; capture never retries to manufacture consistency.
verification records the pass and its result. Equal observations do not
make capture atomic, prove continuous identity, or detect objects created
and removed between reads. Replacing a named buffer can preserve every
catalog field while changing its contents.
Each pass permits at most max_rows raw rows (default 65,536) across all
collections, before deduplication. max_bytes (default 16 MiB) limits both
total encoded output and retained scalar/key bytes per pass. Strict mode
adds one bounded pass. Accumulated rows and the graph copy also consume the
runtime’s byte budget; exhaustion returns queue_full. These counters bound
data, not exact Lua heap allocation.
timeout defaults to 750 milliseconds per listing command. Endpoint evidence
checks have a separate bounded 750-millisecond deadline. Whole capture has
a finite command count; cancel its Request for an earlier stop. Closing the
server during capture prevents a successful current-reference result.
Cancellation retires owned clients; it does not kill the daemon.
Run the example against an explicitly selected existing server:
$ TMUX_BIN=/usr/bin/tmux TMUX_SOCKET=/tmp/example-tmux.sock lua examples/snapshot.luaThe package gate runs this file from installed core and luv outside the checkout, checks exact output, and verifies cleanup. Core-only installation still has no luv dependency; this standalone example selects it explicitly.