# libtmux for Elixir > The Elixir library (libtmux). These pages include its guides, tested examples and API documentation. - [Elixir API reference](https://libtmux.org/en/elixir/latest/reference/): every public symbol, generated from the source. Hosted on libtmux.org. --- # Concepts Source: https://libtmux.org/en/elixir/latest/concepts/ > Concepts for libtmux. Understand captured queries, object identities, and operations. - [Read values, execute explicitly](https://libtmux.org/en/elixir/latest/concepts/mental-model/): Hold one captured graph for several decisions and run a command when you need another. - [Captured tmux state](https://libtmux.org/en/elixir/latest/concepts/observations/): Capture an immutable snapshot of an existing server and read it without further commands. --- # Read values, execute explicitly Source: https://libtmux.org/en/elixir/latest/concepts/mental-model/ > Hold one captured graph for several decisions and run a command when you need another. Capture once when several decisions should use the same observed graph. Keep that snapshot while selecting and filtering. Execute a new command explicitly when you need another observation or an effect. Capture records an interval; fields can change during it. The resulting value stays fixed while live objects continue changing. ## The values you hold | Value | What it represents | I/O boundary | | --- | --- | --- | | [`LibTmux.Server`]() | Executable, socket and environment for an existing server | Pure descriptor construction | | [`LibTmux.Snapshot`]() | An indexed graph captured over one interval | [`LibTmux.Server.snapshot/2`]() captures a new value | | [`LibTmux.Selection`]() | Ordered items and their originating snapshot | Filtering and enumeration use captured values | | [`LibTmux.Ref`]() | An observed exact ID and server instance | Pure identity data; it reserves nothing | | [`LibTmux.Control`]() | A handle pinned to one connection epoch | Status reads are local; startup explicitly attaches a client | | [`LibTmux.Operation`]() | Access to one retained operation record | Result/status reads are local; awaiting registers a separate waiter | Windows are physical objects. [`LibTmux.WindowLink`]() supplies their membership in a session, index and active state. A linked window can appear more than once while its panes remain the same physical panes. Choose window links when membership matters; choose panes when an action should address each physical pane once. See [captured state](https://libtmux.org/en/elixir/latest/concepts/observations/). ## Coverage tells you which questions the graph can answer A loaded field may contain a value, an empty string or a documented [`nil`](). `:unloaded` means this snapshot does not contain the observation. `:unavailable` means the capability is absent. Neither means known absence. Relationship predicates need complete captured join domains. A missing pane relation cannot satisfy “no pane matches.” Validation raises a structured coverage error before filtering, including an empty selection. Obtain a new snapshot explicitly when more coverage is needed. Existing selections retain their original graph. See [filtering](https://libtmux.org/en/elixir/latest/guides/filtering/). ## Server identity and connection epochs have different lifetimes A server instance identifies one tmux process incarnation. Native object IDs can be reused after that process exits. References retain the observed instance so qualified control requests can reject known stale targets. One-shot commands retain a check/use race; an observed ID reserves nothing. An epoch identifies one control connection and its owned transport actors. [`LibTmux.Control.reconnect/2`]() obtains the ready epoch and returns it unchanged when it is already ready. Close first to require a replacement. Replacement waits for retirement and returns a new pinned handle. The server instance can stay the same while the epoch changes. Old connection handles cannot submit into the replacement. Reconnect never replays work. ## Waiting, command outcomes and retirement are separate | Result | What the caller can conclude | | --- | --- | | [`LibTmux.Control.submit/3`]() returns `{:ok, operation}` | Admission succeeded; inspect or await the retained operation | | [`LibTmux.Operation.await/2`]() returns an API timeout | This waiter ended; the operation may still advance | | A canonical outcome is `:completed` or `:failed` with `:acknowledged` certainty | tmux supplied a command reply; arbitrary programs started by that command have their own lifetimes | | A canonical outcome is `:skipped` with `:no_write` certainty | The operation was classified before dispatch | | A canonical outcome is `:unknown` | Available evidence cannot determine its effects | | Operation status reports `wire: :retired` | Reply attribution has retired; the retained record may still occupy capacity | | [`LibTmux.Control.close/2`]() returns `:ok` | The pinned connection epoch retired; its enclosing supervisor remains running | Each await call has its own budget. Timeout or caller death ends that waiter without cancelling the operation. For wire-active work, [`LibTmux.Operation.stop/1`]() requests closure of the entire pinned epoch and can affect siblings. An already retired operation returns `:ok` without closing its connection. Use [`LibTmux.Control.close/2`]() to wait for connection retirement. Inspect retained results instead of replaying an uncertain mutation. Once a canonical outcome exists, it stays unchanged. Bang operation APIs raise API/context errors and still return canonical failed, skipped and unknown outcomes as values. Wire retirement, record expiry and native process cleanup each have their own evidence. See [ownership](https://libtmux.org/en/elixir/latest/topics/ownership/) for deadlines, bounds and the remaining qualification limits. --- # Captured tmux state Source: https://libtmux.org/en/elixir/latest/concepts/observations/ > Capture an immutable snapshot of an existing server and read it without further commands. [`LibTmux.Server.new/1`]() constructs a descriptor with an explicit executable and socket. It performs no I/O. [`LibTmux.Server.snapshot/2`]() observes that existing server and returns an immutable [`LibTmux.Snapshot`](). Neither call attaches a control client or starts a missing server. Configured tmux hooks remain environmental effects of commands, including list commands. ## Read one captured graph [`Snapshot.items/2`]() returns a plain list for an entity atom or module. [`Snapshot.fetch/3`]() looks up an exact ID. [`Snapshot.related/4`]() follows captured indexes and raises a structured coverage error when a relation is incomplete. These functions perform no I/O. [`Snapshot.sessions/1`](), `windows/1`, `panes/1`, `window_links/1` and `clients/1` return materialized Enumerable selections. See [filtering](https://libtmux.org/en/elixir/latest/guides/filtering/) for local predicates and portable criteria. For common relationships, pass the snapshot and a session/window observation to [`Session.windows/2`](), [`Session.panes/2`](), [`Session.active_window/2`]() or [`Window.panes/2`](). The plural helpers return plain lists; `active_window/2` returns a window or [`nil`](). They validate the observation's kind, ID and server incarnation, then use the supplied graph's fields and coverage. An older observation from the same incarnation is valid; its other fields are ignored. Direct traversal validates the requested source. Looking up one session's active window does not fail because another session has ambiguous active links. Relationship filters validate their entire captured source domain before iteration, even when the selection is empty. Sessions, physical windows, panes and clients have separate maps. A pane belongs to one physical window. [`WindowLink`]() records session membership, window index and active state. Its key includes session ID, index and window ID; it does not prove a unique lifetime after unlink and relink. A window can appear at multiple indexes in the same session and in other sessions. Server-wide physical maps deduplicate identical rows and reject conflicting rows. Session pane traversal returns each physical pane once; window-link traversal preserves memberships. Top-level session/window/pane collections sort by numeric native ID. Session windows follow their first link's index; session and window panes sort by numeric pane ID. Links sort by numeric session ID and index; clients sort bytewise by their validated observed names. [`Session.attached_count`]() preserves tmux's raw client count. [`attached`]() is derived from that count. Explicit control clients remain counted even with `no-output` and `ignore-size`. A control client's height is known [`nil`]() when tmux reports no started tty; it is not an unavailable field. [`Pane.pid`]() is known [`nil`]() when tmux reports no PID, including empty and retained dead panes on tmux 3.8. Reported integers stay unchanged: older tmux versions can report zero for an empty pane or a historical PID for a dead pane. Neither a PID nor its absence proves process liveness or cleanup. ## Coverage and construction [`Snapshot.new/2`]() validates fixture or adapter observations without I/O. [`fields`]() declares loaded fields over the supplied entity IDs. `complete` declares complete server entity sets; omissions remain partial. `unavailable` declares absent capabilities separately. Declared names and values validate before indexes or relations become observable. Known empty strings, known nullable values, `:unloaded` and `:unavailable` remain distinct. Required coverage checks run even for empty entity sets. Derived attachment coverage follows its source count. A relation to many objects needs a complete join domain, so missing children never become an empty relation. Complete target sets reject dangling references. A partial target set can retain unresolved foreign IDs, but traversal fails explicitly. The [generated schema](https://libtmux.org/en/elixir/latest/topics/schema/) owns field types, explicit wire names, tmux variables and relation cardinalities. Physical windows carry no single session, session index or active membership flag. ## Capture consistency and bounds Capture reads topology, then named fields, then topology again. It checks the tmux PID against Linux boot identity and process start ticks before and after those reads. Replacement or changing references trigger finite read retries. A disappearing read target can retry; invalid encoding and other decoding errors remain visible. Two attempts are the default; one to three may be requested. All attempts share one deadline and the original budgets. Defaults are 1024 contextual entity rows, 512 commands, 8 MiB total response bytes, 64 KiB per response and 5000 ms. Both stdout and stderr, including failed-command output, consume the byte budget. Overload returns an error instead of a truncated success. Topology checks and retries also consume work. One-shot retirement has the separate allowance documented in [ownership](https://libtmux.org/en/elixir/latest/topics/ownership/). Free-form fields use the qualified versioned encoder. Numeric/ID topology columns validate their complete representation. Client rows combine one encoded identity with restricted scalar columns; tabs and newlines cannot escape the encoded identity. This is a fixed capture schema, not a general raw-format row parser. The capture interval records observation, not a transaction. Attributes can change within it; matching topology before and after does not prove nothing changed in between. Holding a snapshot reserves no objects. Its server instance is distinct from a future connection epoch, and one-shot checks retain a check/use race. Sending snapshots between BEAM processes can copy terms; retaining one can retain its graph. --- # API glossary Source: https://libtmux.org/en/elixir/latest/reference/glossary/ > Kinds, modifiers and package labels in the Elixir API reference. ## Symbol kinds Kinds identify declarations in the Elixir API reference. ### module An Elixir module, a named collection of functions, macros and types defined with defmodule. Module names are atoms, and a nested name such as LibTmux.Control.Request is one flat module rather than a child of LibTmux.Control. References: [Elixir modules and functions](https://hexdocs.pm/elixir/modules-and-functions.html). ### interface An Elixir behaviour, a module that declares callbacks other modules implement. A module implements a behaviour with the @behaviour attribute and defines each required callback. References: [Elixir behaviours](https://hexdocs.pm/elixir/typespecs.html#behaviours). ### struct An Elixir module that defines a struct: a map with a fixed set of keys and optional defaults. Fields hold values and are never reassigned. Functions on the module return a new struct rather than changing one. References: [Elixir structs](https://hexdocs.pm/elixir/structs.html). ### function An Elixir function identified by its module, name and arity. Functions of one name and different arities are different functions. A name ending in ! raises on failure, and a name ending in ? returns a boolean. References: [Elixir functions and arity](https://hexdocs.pm/elixir/modules-and-functions.html). ### method A callback that a behaviour declares and an implementing module defines. A callback is called through the implementing module, not through the behaviour that declares it. References: [Elixir behaviour callbacks](https://hexdocs.pm/elixir/typespecs.html#behaviours). ### property A field of an Elixir struct, read with dot access or pattern matching. A field has no accessor function; structs are plain maps, so the value is stored as the field holds it. References: [Elixir structs](https://hexdocs.pm/elixir/structs.html). ### typealias An Elixir type declared with @type or @opaque, named and given an arity. Types document values for readers and tools. They are not checked when a function runs. References: [Elixir user-defined types](https://hexdocs.pm/elixir/typespecs.html#user-defined-types). ### exception An Elixir struct declared with defexception, which can be raised and rescued. The struct also carries the fields of the error. A bang function raises it; the plain function returns it in an error tuple. References: [Elixir exceptions](https://hexdocs.pm/elixir/Kernel.html#defexception/1). ## Modifiers Modifiers add information about calling, implementing, or accessing a declaration. Their precise effect follows the language and the signature. ### opaque An Elixir type declared with @opaque, whose structure callers must not depend on. Other modules should build and inspect the value only through the declaring module’s functions. References: [Elixir user-defined types](https://hexdocs.pm/elixir/typespecs.html#user-defined-types). ### macro An Elixir macro, defined with defmacro and expanded at compile time. A caller must require or import the defining module before using the macro. References: [Elixir macros](https://hexdocs.pm/elixir/Kernel.html#defmacro/2). ### abstract A callback of a behaviour, declared without a body. References: [Elixir behaviour callbacks](https://hexdocs.pm/elixir/typespecs.html#behaviours). ### deprecated A declaration marked with the @deprecated attribute. The compiler warns at call sites of a deprecated function. The message names the replacement when the author gave one. References: [Elixir deprecations](https://hexdocs.pm/elixir/Module.html). ### overload A function with several @spec clauses, each describing one accepted combination of argument types. The clauses describe one function. Which clause applies depends on the arguments, not on a separate definition. References: [Elixir function specifications](https://hexdocs.pm/elixir/typespecs.html#defining-a-specification). ## Package context Package labels identify where an API is supplied. ### package The Hex package supplying this API. A package name and its application name are the same in a Mix dependency, and the module names it defines are independent of both. References: [Hex packages](https://hex.pm/docs/usage). --- # tmux MCP for Elixir is not published Source: https://libtmux.org/en/elixir/latest/mcp/ > The Elixir repository contains an MCP package scaffold, not a usable or published server. Elixir has no published libtmux MCP server. The repository's `packages/mcp` directory is a scaffold: its README says the package is under implementation and has no protocol server or executable yet. This page records availability so cross-language navigation does not turn a source placeholder into a product claim. There is no launch command, client configuration, tool reference, prompt catalog, or embedding reference to use. Use the [core Elixir library](https://libtmux.org/en/elixir/latest/guides/overview/) for direct tmux access. Choose another language's MCP server only as a separate process with that server's own package, policy, and compatibility requirements. --- # tmux workspace manager for Elixir is not published Source: https://libtmux.org/en/elixir/latest/workspace/ > The Elixir repository contains a workspace package scaffold, not a loader or supported builder API. Elixir has no published libtmux workspace manager. The repository's `packages/workspace` directory is a scaffold: its README says the package is under implementation and has no loader or executable yet. This page records availability without advertising placeholder code. There is no Elixir equivalent of `libtmux-workspace validate`, `plan`, or `load`, and no workspace reference tree is generated. Use the [core Elixir library](https://libtmux.org/en/elixir/latest/guides/overview/) to observe tmux and run commands on an existing server. A workspace tool from another language remains a separate application with its own configuration contract; it is not an Elixir feature. --- # Examples Source: https://libtmux.org/en/elixir/latest/examples/ > Examples for libtmux. Run complete programs with their documented setup and cleanup. - [Capture a pane and read it](https://libtmux.org/en/elixir/latest/examples/capture-and-read/): Capture a graph, select a pane and read it through one-shot and control commands. --- # Capture a pane and read it Source: https://libtmux.org/en/elixir/latest/examples/capture-and-read/ > Capture a graph, select a pane and read it through one-shot and control commands. Capture once, then query the immutable result without more tmux commands. This example works with the published `0.0.1-alpha.3` package. Install it in an Elixir project as shown in the [README](https://libtmux.org/en/elixir/latest/guides/overview/#installation). Save this program as `capture_and_filter.exs` in your application: ```elixir alias LibTmux.{Selection, Server, Snapshot} [executable, socket | _] = System.argv() {:ok, _} = Application.ensure_all_started(:libtmux) server = Server.new( executable: executable, socket: socket, env: Map.drop(System.get_env(), ["TMUX", "TMUX_PANE"]) ) {:ok, snapshot} = Server.snapshot(server) active_ids = snapshot |> Snapshot.panes() |> Selection.where(active: true) |> Enum.map(& &1.id) IO.puts(JSON.encode!(%{active_panes: active_ids})) ``` Set `LIBTMUX_TMUX` to the absolute tmux executable and `LIBTMUX_SOCKET` to the absolute socket of the existing server you intend to observe. Run: ```console $ mix run capture_and_filter.exs "$LIBTMUX_TMUX" "$LIBTMUX_SOCKET" ``` The JSON result lists active physical pane IDs in numeric order. A pane is active within its physical window; this is not a choice of one session's active window. Linked windows do not duplicate their panes. [`Server.new/1`]() builds a descriptor without I/O. [`Server.snapshot/2`]() runs bounded one-shot reads without attaching a control client or creating a missing server. [`Selection.where/2`]() and `Enum.map/2` use the captured graph. Capturing covers an interval, not an atomic tmux transaction. Capture again explicitly when fresh state is needed. The pattern match makes a capture error visible; production callers can handle `{:error, %LibTmux.Error{}}`. The default capture budgets are 5000ms, 512 commands, 1024 contextual rows, 64 KiB per response and 8 MiB total. A limit failure returns an error rather than a partial graph. For an isolated demonstration, use the [owned-server recipe](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/docs/owned-server.md). Next: [filter and select](https://libtmux.org/en/elixir/latest/guides/filtering/), [one-shot actions](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/docs/one-shot-actions.md), or [managed control](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/docs/managed-control.md). --- # Guides Source: https://libtmux.org/en/elixir/latest/guides/ > Guides for libtmux. Connect to tmux and work with its sessions, windows, and panes. - [Getting started](https://libtmux.org/en/elixir/latest/guides/overview/): Add the Hex package, check the Elixir, OTP and tmux requirements, and run an example. - [Attaching to tmux](https://libtmux.org/en/elixir/latest/guides/attaching-to-tmux/): Connect to an existing socket and leave its server running. - [Filter captured values](https://libtmux.org/en/elixir/latest/guides/filtering/): Select observed values with portable keyword and operator filters. --- # Getting started Source: https://libtmux.org/en/elixir/latest/guides/overview/ > Add the Hex package, check the Elixir, OTP and tmux requirements, and run an example.

Hex.pm · API reference · HexDocs · Documentation

Read tmux sessions, windows and panes from Elixir. Filter a snapshot, rename a window, split a pane, send input, or capture its screen. Queries use the captured snapshot; actions contact tmux explicitly. [Capture and query](https://libtmux.org/en/elixir/latest/guides/overview/#capture-and-query) · [Rename a window](https://libtmux.org/en/elixir/latest/guides/overview/#rename-a-window) · [Split and send input](https://libtmux.org/en/elixir/latest/guides/overview/#split-a-pane-and-send-input) · [Guides](https://libtmux.org/en/elixir/latest/guides/) ## Status and requirements **Alpha:** the API may change between releases. | Requirement | Version or dependency | | --- | --- | | Elixir / OTP | Elixir 1.20.4 or later 1.20 patch, compiled for OTP 29; OTP 29.1.2 or later 29 patch | | Runtime platform | Linux, with a C11 compiler and headers to build the external helper | | tmux | 3.3 minimum; tested with 3.3, 3.3a, 3.4 and 3.8 | | Dependencies | Zero third-party core runtime packages; no NIF | ## Installation Add the published alpha to your Mix dependencies: ```elixir {:libtmux, "== 0.0.1-alpha.3"} ``` Fetch dependencies: ```console $ mix deps.get ``` ## Run the examples The snippets below form one [executable program](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/examples/readme.exs). From this checkout, run it on a private tmux server: ```console $ python3 examples/owned_server.py \ --run "$(command -v tmux)" \ mix run examples/readme.exs ``` The [runner](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/docs/owned-server.md) creates a private server and cleans it up afterward. It supplies the executable, socket and session ID used below, leaving your existing tmux sessions alone. The runner needs Python 3.9+. Already have a server? Use the [capture-only program](https://libtmux.org/en/elixir/latest/examples/capture-and-read/), which takes its socket explicitly and leaves the server running. ## Capture and query [`Server.snapshot!/2`][snapshot] captures an immutable graph. [`Selection.where/2`][where] accepts predicate data; ordinary [`Enum`]() functions work on the selection. These queries make no further tmux calls. ```elixir alias LibTmux.{Selection, Server, Snapshot} [executable, socket, session_id] = System.argv() server = Server.new( executable: executable, socket: socket, env: Map.drop(System.get_env(), ["TMUX", "TMUX_PANE"]) ) snapshot = Server.snapshot!(server) active_ids = snapshot |> Snapshot.panes() |> Selection.where(active: true) |> Enum.map(& &1.id) ``` The fixture starts with one pane, so `active_ids` is `["%0"]`. To use an Elixir function as the predicate, choose [`Selection.filter/2`][filter]. Capture again explicitly when you need fresh observations. ## Rename a window Continue with the same [`snapshot`][snapshot-type] and [`server`][server-type]. [`Selection.one!/2`][one] requires exactly one match, and [`Session.active_window/2`][active-window] reads the captured relationship. [`Ref.from_observation/1`][ref] carries the observed identity into an action. ```elixir alias LibTmux.{Ref, Selection, Session, Snapshot, Window} session = snapshot |> Snapshot.sessions() |> Selection.one!(id: session_id) window = Session.active_window(snapshot, session) {:ok, session_ref} = Ref.from_observation(session) {:ok, window_ref} = Ref.from_observation(window) Window.rename!(server, window_ref, "editor", session: session_ref) ``` [`Window.rename!/4`][rename] returns `:ok` when tmux acknowledges completion. The `session` option chooses this window's session membership. The original snapshot still contains the name captured before the action. ## Split a pane and send input Continue with that [`window`][window-type] and [`session`][session-type]. [`Control.with_connection/2`][scope] owns connection startup and cleanup around the callback. Its returned value is available after connection retirement. ```elixir alias LibTmux.{Control, Pane, Ref, Selection, Snapshot} pane = snapshot |> Snapshot.panes() |> Selection.one!(window_id: window.id, active: true) {:ok, pane_ref} = Ref.from_observation(pane) {:ok, created_id} = Control.with_connection( [server: server, session: {:existing, session.id}, lane: :host_qualified], fn control -> %{id: id, ref: created} = Pane.split!(control, pane_ref, axis: :horizontal, command: {:argv, ["/bin/cat"]}) Pane.send_text!(control, created, "hello") Pane.send_keys!(control, created, ["Enter"]) id end ) ``` [`Pane.split!/3`][split] returns the new pane's ID and bound reference on this connection. [`Pane.send_text!/4`][text] sends literal text; [`Pane.send_keys!/4`][keys] sends named keys. `{:argv, ["/bin/cat"]}` requests a program directly; `{:shell, "command"}` requests shell interpretation. Acknowledgement does not mean the program has finished processing its input. The private runner qualifies this example's `lane: :host_qualified` declaration. That option does not configure or qualify an arbitrary server. Read the [managed-control guide](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/docs/managed-control.md) before using another host. For one-shot screen reads, use [`Pane.capture!/3`][capture]. The complete program prints the active pane `%0`, the new pane `%1`, and the window name `editor`. These examples run in CI. Each snippet shows its aliases; they shorten module names and are not inferred. ## Errors and recovery The domain actions shown above raise when they cannot complete. If a command times out, it may still have taken effect. The [recovery guide](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/docs/recovery.md) shows how to inspect the result before deciding what to do next. ## Concepts and reference - [Filtering](https://libtmux.org/en/elixir/latest/guides/filtering/): predicates, relationships and finding one match. - [One-shot actions](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/docs/one-shot-actions.md): rename, split, input and screen capture. - [Managed control](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/docs/managed-control.md): reuse a connection for multiple commands. - [Mental model](https://libtmux.org/en/elixir/latest/concepts/mental-model/): snapshots, references and command results. - [API reference](https://libtmux.org/en/elixir/latest/reference/): functions and types. ## Contributing See [Contributing](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/.github/CONTRIBUTING.md) for development setup and checks, and [Writing](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/.github/WRITING.md) for documentation conventions. Related projects: [libtmux for Python](https://github.com/tmux-python/libtmux), [libtmux ports](https://github.com/libtmux), and [tmux](https://github.com/tmux/tmux). [Artwork](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/docs/branding.md). Licensed under the [MIT License](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/LICENSE). [snapshot]: https://libtmux.org/en/elixir/latest/reference/libtmux-server-snapshot-bang-2/ [snapshot-type]: https://libtmux.org/en/elixir/latest/reference/libtmux-snapshot/ [server-type]: https://libtmux.org/en/elixir/latest/reference/libtmux-server/ [window-type]: https://libtmux.org/en/elixir/latest/reference/libtmux-window/ [session-type]: https://libtmux.org/en/elixir/latest/reference/libtmux-session/ [where]: https://libtmux.org/en/elixir/latest/reference/libtmux-selection-where-2/ [filter]: https://libtmux.org/en/elixir/latest/reference/libtmux-selection-filter-2/ [one]: https://libtmux.org/en/elixir/latest/reference/libtmux-selection-one-bang-2/ [active-window]: https://libtmux.org/en/elixir/latest/reference/libtmux-session-active_window-2/ [ref]: https://libtmux.org/en/elixir/latest/reference/libtmux-ref-from_observation-1/ [rename]: https://libtmux.org/en/elixir/latest/reference/libtmux-window-rename-bang-4/ [scope]: https://libtmux.org/en/elixir/latest/reference/libtmux-control-with_connection-2/ [split]: https://libtmux.org/en/elixir/latest/reference/libtmux-pane-split-bang-3/ [text]: https://libtmux.org/en/elixir/latest/reference/libtmux-pane-send_text-bang-4/ [keys]: https://libtmux.org/en/elixir/latest/reference/libtmux-pane-send_keys-bang-4/ [capture]: https://libtmux.org/en/elixir/latest/reference/libtmux-pane-capture-bang-3/ --- # Attaching to tmux Source: https://libtmux.org/en/elixir/latest/guides/attaching-to-tmux/ > Connect to an existing tmux server and find a session with Elixir. Select an explicit socket, capture the existing server, and find the `work` session. The program prints its name and leaves the server running. A missing server or session ends the program with an error. This controls tmux from Elixir. To open a session in your terminal, use [`tmux attach-session`](https://libtmux.org/en/tmux/latest/manual/attach-session/); the [shared guide](https://libtmux.org/en/tmux/guides/attaching-to-tmux/) covers interactive attachment and detaching. ## Connect to an existing server Save the complete program as `connect.exs`. The environment variable `LIBTMUX_SOCKET_PATH` selects the existing server. The launcher below sets it to a private socket for trying the example. The first line installs the published alpha from Hex. ```elixir title="connect.exs" Mix.install([{:libtmux, "== 0.0.1-alpha.1"}]) alias LibTmux.{Selection, Server, Snapshot} tmux = System.find_executable("tmux") || raise "tmux is not on PATH" socket = System.fetch_env!("LIBTMUX_SOCKET_PATH") server = Server.new(executable: tmux, socket: socket) snapshot = Server.snapshot!(server) session = snapshot |> Snapshot.sessions() |> Selection.one!(name: "work") IO.puts(session.name) ``` [`Server.new/1`](https://libtmux.org/en/elixir/latest/reference/libtmux-server-new-1/) describes a server that already exists and performs no I/O. [`Server.snapshot!/2`](https://libtmux.org/en/elixir/latest/reference/libtmux-server-snapshot-bang-2/) reads tmux and returns captured state, or raises the structured capture error. [`Snapshot.sessions/1`](https://libtmux.org/en/elixir/latest/reference/libtmux-snapshot-sessions-1/) selects sessions from that state without contacting tmux again, and [`Selection.one!/2`](https://libtmux.org/en/elixir/latest/reference/libtmux-selection-one-bang-2/) requires exactly one match. Reading or filtering captured values does not refresh them. The server value describes the daemon. It does not own a process to close, and the program does not destroy the caller's sessions. ## Setup and run Use an empty directory on Linux with Elixir 1.20.4 or newer, Erlang/OTP 29.1.2 or a newer 29.x release, a C11 compiler, tmux 3.3 or newer, a POSIX shell, and access to hex.pm. Save the launcher as `run.sh`. It starts an isolated tmux server, runs the program, checks that the session still exists, then stops only that server. Cleanup runs after failures too. A failed shutdown keeps its socket directory and prints its location for inspection. ```sh title="run.sh" #!/bin/sh set -eu binary=$(command -v tmux) directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-elixir-attach.XXXXXX") socket="$directory/tmux.sock" cleanup() { status=$? trap - 0 HUP INT TERM if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then printf 'Cannot stop tmux; kept %s\n' "$directory" >&2 exit 1 fi rm -rf "$directory" || exit 1 exit "$status" } trap cleanup 0 trap 'exit 1' HUP INT TERM unset TMUX TMUX_PANE export LIBTMUX_SOCKET_PATH="$socket" "$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat "$@" "$binary" -S "$socket" has-session -t '=work' ``` ```console $ sh run.sh elixir connect.exs ``` The program prints `work`. To use your own existing server, set `LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher. The launcher owns only the demonstration server. ## Find or create a session Reading a session does not reserve its name. If your application creates a session after an unsuccessful lookup, another client may create the same name between those operations. Handle the creation error as well as the lookup. [Mental model](https://libtmux.org/en/elixir/latest/concepts/mental-model/) and [Observations](https://libtmux.org/en/elixir/latest/concepts/observations/) explain what a snapshot holds. [Capture a pane and read it](https://libtmux.org/en/elixir/latest/examples/capture-and-read/) is a complete program that runs against an isolated server. The [library source revision](https://github.com/libtmux/libtmux-elixir/tree/b72452c198917433229bdc266a68c163541b6127) records the implementation used by this example. --- # Filter captured values Source: https://libtmux.org/en/elixir/latest/guides/filtering/ > Select observed values with portable keyword and operator filters. Obtain one [snapshot](https://libtmux.org/en/elixir/latest/concepts/observations/), then select its observed panes: ```elixir alias LibTmux.{Selection, Snapshot} editors = snapshot |> Snapshot.panes() |> Selection.where(active: true, current_command: [in: ["nvim", "vim"]]) Enum.map(editors, & &1.id) ``` Selections retain their immutable graph and order. Enumerable delegates to the materialized list, including suspension and early halt. `size/1` uses the cached count; ordinary [`Enum.member?/2`]() compares elements. Use [`Selection.member_id?/2`]() for native ID membership. Enumeration performs no I/O. [`Selection.filter/2`]() accepts only unary functions and uses Enum truthiness. Predicate exceptions propagate. `where/2` accepts validated data: keywords are shorthand for maps, with duplicate keys rejected at every nesting level. Both validate before scanning, including when no items remain. `one/2` returns `{:ok, item}` or a structured `:no_match` / `:multiple_matches` error. `one_or_nil/2` permits zero matches. Their bang variants raise the same error; both stop at the second match. `exists?/2` stops at its first match. `count/2` counts matching items; `size/1` returns the cached total. ## Predicate grammar A bare scalar means equality. Explicit `%{equals: value}` is equivalent. The [generated schema](https://libtmux.org/en/elixir/latest/topics/schema/) lists each field's type and operators. Numbers compare numerically, strings compare case-sensitively without normalization, and nullable numeric ordering against known nil is false. `not` negates equality; `not_in` negates membership, including known nil. Portable regex is not supported. Native predicates may use [`Regex`]() directly. Fields in one object conjoin. Boolean `and` and `or` accept lists of criteria; `not` accepts one criteria object. Empty `and` is true and empty `or` is false. Normalization never combines separate relation quantifiers: ```elixir alias LibTmux.{Selection, Snapshot} Selection.where(Snapshot.sessions(snapshot), %{ panes: %{some: %{active: true, current_command: "nvim"}} }) ``` That predicate requires one pane to satisfy both conditions. Two separate `some` clauses can match different panes. `every` and [`none`]() are true for a known-empty relation; `some` is false. [`count`]() counts that relation's identities: physical windows/panes deduplicate, window links retain memberships. To-one `is: criteria` is false for known absence and `is_not` negates it. `is: nil` means absent; `is_not: nil` means present. Session [`active_window`]() is derived from a complete link domain and loaded active flags. Zero active links means known absence; multiple active links raise `:ambiguous_relation`. An unresolved active target remains unloaded. Coverage checks run before evaluation, including fields behind a Boolean branch that cannot match. ## External filters [`Filter.new/3`]() returns a validated portable value, and `new!/3` raises its structured error. Applying that value retains its constructor validation; explicit new limits validate the normalized tree without generating extra criteria objects. Prepared predicates may contain sets and closures; portable filters contain data only. [`Filter.from_json/3`]() uses built-in JSON and rejects duplicate keys before map conversion, trailing data and unknown names. `from_wire/3` accepts an already decoded map; that map cannot reveal discarded duplicate keys. Neither decoder creates atoms from external keys. Encode through [`Filter.to_wire/1`](), then [`JSON.encode!/1`](). ```json {"version":1,"entity":"pane","where":{"active":true,"currentCommand":{"in":["nvim","vim"]}}} ``` Default ceilings are 65,536 bytes, depth 16, 1024 value nodes, 256 array elements and 8192 bytes per string. Wire depth starts at root zero and counts every member/element; the ceiling is inclusive. Positive integer options `max_bytes`, `max_depth`, `max_nodes`, `max_list` and `max_string_bytes` can narrow or widen the constructor budget. JSON object members have a fixed ceiling of 256. Native input depth counts criteria objects and also bounds the normalized tree. A normalized conjunction does not add relation/Boolean nesting; disjunctions, negations and relation children do. Wire budgets additionally cover the full versioned envelope. Errors carry the affected path, including Boolean and membership indexes; early parser-limit errors can identify the root before a path is available. Selections and filters operate on captured values. Server query planning and pushdown remain under implementation. --- # Third-party notices Source: https://libtmux.org/en/elixir/latest/meta/third-party-notices/ > Thanks to tmux's creator and contributors, with licences and attribution for the software behind libtmux.org. ## tmux First and foremost, thank you to **[Nicholas Marriott (nicm)](https://github.com/nicm)**, the creator of tmux, and to the many [tmux contributors](https://github.com/tmux/tmux/graphs/contributors). We are grateful for the work that makes tmux possible. libtmux is a separate project. Our libraries and supporting tools control a real tmux server; tmux itself provides the terminal multiplexer. This is the libtmux website. Visit the [official tmux website](https://github.com/tmux/tmux/wiki) for the upstream project and its documentation. See tmux's [COPYING file](https://github.com/tmux/tmux/blob/master/COPYING) and the copyright and permission notices in its source files for its licensing. ### tmux artwork The tmux logomark is by Jason Long. The site uses the unmodified [upstream SVG](https://github.com/tmux/tmux/blob/8f25579c5aef8d93924a20681f394e2a582fd3ad/logo/tmux-logomark.svg) under its [copyright and permission notice](https://libtmux.org/en/brand/tmux/LICENSE.txt). ## Programming-language artwork The homepage language selector uses local copies of the following artwork to identify the selected language. Each source record includes the download URL, retrieval time, file hash, copyright information and usage terms. These marks identify their respective languages and do not imply endorsement of libtmux. The SVG files are copied unchanged except for Scala, whose empty surrounding canvas is cropped. Its paths, gradients and colors are preserved, and the original SVG is retained beside the cropped copy. Rust's supplied SVG includes its own dark-theme colors. | Language | Credit and terms | Local source record | |---|---|---| | Python | Python Software Foundation. The [PSF logo terms](https://www.python.org/psf/trademarks/) permit the unaltered mark to identify Python. | [Python provenance](https://libtmux.org/en/brand/languages/py/provenance.json) | | Ruby | Copyright © 2006, Yukihiro Matsumoto. The [Ruby logo](https://www.ruby-lang.org/en/about/logo/) is licensed under [CC BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/). | [Ruby provenance](https://libtmux.org/en/brand/languages/ruby/provenance.json) | | Lua | Copyright © 1998 Lua.org; graphic design by Alexandre Nakonechnyj. The Devicon copy carries its [MIT notice](https://libtmux.org/en/brand/languages/lua/LICENSE.txt); [Lua's logo terms](https://www.lua.org/images/) also apply. Visit [Lua.org](https://www.lua.org/). | [Lua provenance](https://libtmux.org/en/brand/languages/lua/provenance.json) | | TypeScript | Microsoft. The [official branding terms](https://www.typescriptlang.org/branding/) govern the mark; the website repository licenses exclude logo and trademark rights. | [TypeScript provenance](https://libtmux.org/en/brand/languages/ts/provenance.json) | | Rust | The Rust Foundation. [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) and the [Rust trademark policy](https://rustfoundation.org/policy/rust-trademark-policy/) apply. | [Rust provenance](https://libtmux.org/en/brand/languages/rs/provenance.json) | | Go | The [Go gopher](https://go.dev/blog/gopher) is by Renee French, licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). | [Go provenance](https://libtmux.org/en/brand/languages/go/provenance.json) | | C++ | Created by Jeremy Kratz and licensed by the Standard C++ Foundation under its [logo-use terms](https://isocpp.org/home/terms-of-use). | [C++ provenance](https://libtmux.org/en/brand/languages/cxx/provenance.json) | | Swift | Apple Inc., under the [Swift Logo Guidelines](https://developer.apple.com/swift/downloads/swift-logo.zip). Swift and the Swift logo are trademarks of Apple Inc. | [Swift provenance](https://libtmux.org/en/brand/languages/swift/provenance.json) | | Java | Devicon collection copyright (c) 2015 konpa, with its [MIT notice](https://libtmux.org/en/brand/languages/java/LICENSE.txt). This does not establish unrestricted rights to the underlying Java logo; [Oracle's logo terms](https://www.oracle.com/legal/logos/) apply. | [Java provenance](https://libtmux.org/en/brand/languages/java/provenance.json) | | Kotlin | Kotlin Foundation brand guidelines preserve JetBrains copyrights. The [icon-use terms](https://kotlinfoundation.org/guidelines/) permit identifying Kotlin alongside other programming-language icons. | [Kotlin provenance](https://libtmux.org/en/brand/languages/kotlin/provenance.json) | | Scala | Copyright EPFL. [Historical permission](https://groups.google.com/g/scala-user/c/bCC-R0FQn1w) covers noncommercial Scala promotion; the [general artwork-license question](https://github.com/scala/scala-lang/issues/1040) remains unresolved. | [Scala provenance](https://libtmux.org/en/brand/languages/scala/provenance.json) | | C# | Copyright the .NET authors. [Brand-use permission](https://github.com/dotnet/brand/issues/10#issuecomment-669465301) allows the unmodified logo to represent .NET. The repository's CC0 statement covers illustrations; no blanket CC0 claim is made for the logo. | [.NET provenance](https://libtmux.org/en/brand/languages/csharp/provenance.json) | | F# | The F# Software Foundation. Its [logo terms](https://foundation.fsharp.org/logo) require unchanged shape, colors and proportions, without implying Foundation representation. | [F# provenance](https://libtmux.org/en/brand/languages/fsharp/provenance.json) | ## Terminal artwork The tmux CLI selector displays the Windows Terminal artwork by Microsoft Corporation, licensed under [CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0/). The SVG is unmodified. See the [source and provenance](https://libtmux.org/en/brand/tools/terminal/provenance.json) and [copyright and license](https://libtmux.org/en/brand/tools/terminal/LICENSE.txt). ## Documentation toolchain libtmux and this site are built with open-source software. The following tools have their own licences and attribution requirements. | Tool | Licence | Role | |---|---|---| | [Astro](https://astro.build/) | MIT | The site shell | | [Tailwind CSS](https://tailwindcss.com/) | MIT | Styling | | [Pagefind](https://pagefind.app/) | MIT | Site-wide search | | [Expressive Code](https://expressive-code.com/) | MIT | Code blocks | | [Sphinx](https://www.sphinx-doc.org/) | BSD-2-Clause | Python and C++ reference | | [Furo](https://github.com/pradyunsg/furo) | MIT | Sphinx theme | | [Breathe](https://github.com/breathe-doc/breathe) | BSD-3-Clause | Doxygen XML into Sphinx | | [Doxygen](https://www.doxygen.nl/) | GPL-2.0-only | Parses C++ headers to XML | | [API Extractor](https://api-extractor.com/) | MIT | TypeScript API model | | [IBM Plex](https://github.com/IBM/plex) | OFL-1.1 | Typeface | ### A note on Doxygen Doxygen is licensed GPL-2.0-only. It runs as a build step that reads libtmux's own headers and emits XML; that XML is rendered by Breathe and Sphinx, and no Doxygen-generated HTML is published. Running a GPL program over your own input does not place its licence on the output, and libtmux does not distribute Doxygen or any modified version of it. ## Reference hosting The [PyPI blocks logo](https://pypi.org/trademarks/) is a trademark of the Python Software Foundation and identifies links to the Python Package Index. Other package-host icons use [Simple Icons](https://simpleicons.org/) (CC0-1.0). The docs.rs mark is its [`docsdotrs` icon](https://github.com/simple-icons/simple-icons/blob/16.35.0/icons/docsdotrs.svg), inlined unchanged. The book beside javadoc.io links is original artwork for this site: the host publishes no vector logo, and the mark does not imitate its favicon. Some ports deep-link to the canonical host their ecosystem already uses, rather than duplicating it here: - Rust: [docs.rs](https://docs.rs/libtmux), for each published crate - Go: [pkg.go.dev](https://pkg.go.dev/github.com/libtmux/libtmux-go/tmux) - Java, Kotlin and Scala: [javadoc.io](https://javadoc.io/doc/io.github.libtmux/libtmux) - Swift: the [Swift Package Index](https://swiftpackageindex.com/libtmux/libtmux-swift/documentation/libtmux) copy of the DocC reference Those sites are operated independently of this project and carry their own terms. ## libtmux itself Each port is MIT licensed. See the `LICENSE` file in that port's repository for the authoritative text. --- # Topics Source: https://libtmux.org/en/elixir/latest/topics/ > Topics for libtmux. Understand object ownership, state, and operation behavior. - [Execution ownership](https://libtmux.org/en/elixir/latest/topics/ownership/): Run an executable without a shell, with exit status kept apart from output. - [Command and field encoding](https://libtmux.org/en/elixir/latest/topics/encoding/): Render commands and decode fields without a shell or a raw newline. - [Observation and filter schema](https://libtmux.org/en/elixir/latest/topics/schema/): Fields, wire names, value categories and operators of each observed entity. --- # Execution ownership Source: https://libtmux.org/en/elixir/latest/topics/ownership/ > Run an executable without a shell, with exit status kept apart from output. [`LibTmux.Transport.OneShot.run/3`]() runs an absolute executable and argv on Linux without a shell. It returns bytes, process exit status and cleanup evidence separately. A nonzero process exit is still a completed transport result; the tmux command layer interprets that status. Public domain actions and persistent control use these ownership boundaries. Start with [one-shot actions](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/docs/one-shot-actions.md) or [managed control](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/docs/managed-control.md); this guide explains the lower-level contracts. The native helper supplies receive credits that an ordinary OTP Port lacks. It reads each stdout/stderr pipe only against one bounded credit; its control pipe stays responsive while output or stdin is blocked. A capture limit after delivery cannot bound an upstream mailbox while its owner is descheduled. ## Limits and results Default ceilings are 1 MiB stdout, 64 KiB stderr and 4096 retained chunks. Byte limits may be 0–16 MiB per stream; chunk limits may be 1–65,536. `max_total_bytes` limits both streams together to 0–32 MiB; its default is the sum of the stream limits. The retained prefix obeys both ceilings. Encoded cwd, argv and environment together fit a 32 KiB initialization payload. Argv and environment each have at most 256 entries, including argv's executable. Target environment values preserve empty strings and non-NUL bytes; inherited tmux context is removed. Requested variables apply to child `execve`; the helper inherits the trusted host environment. The timeout defaults to 5000 ms and is at most 600,000 ms. A caller can pass an existing [`LibTmux.Deadline`]() instead. Admission, startup and capture share that budget. Cleanup has a separate allowance up to 750 ms. Expiry never turns partial output into success. Overload stops the owned producer and returns a structured error with a bounded prefix. `cleanup: :retired` requires private child-retirement evidence and helper exit. Missing proof stays `:unknown`. An exit status or retryable error never authorizes replay. The worker acknowledges terminal child-retirement proof before the helper exits. The helper keeps its input open while earlier cancellation or read credits drain; after acknowledgement the worker writes nothing further. This preserves the helper's separate exit-status evidence. Missing acknowledgement uses the existing 750 ms delivery allowance. The worker monitors its caller. Caller death initiates cleanup; worker/VM death closes the helper's parent pipe. Caller settlement and retirement remain separate. Closing the reply alias also removes an already-delivered result, releasing captured bytes from the caller's mailbox. ## Accounting and scope Per-call bounds cover output bytes, chunk metadata, encoded input and private frames. Captured chunks can coexist with an assembled result; account for both copies. The helper has fixed input, queue and write buffers and bounded initialization allocations. It requests 8 KiB child pipes and a 4 KiB signal pipe. Kernel page rounding can increase the actual capacities; a resize failure rejects startup. Parent pipes and Port delivery add buffers outside the capture byte count. Aggregate accounting must measure actual pipe capacities. See [the protocol](https://github.com/libtmux/libtmux-elixir/blob/66b9b1c3f3f3186428abdfda2c76f5cf87446710/c_src/README.md). This primitive has one request per worker and no shared request queue. Applications creating many independent callers need admission limits at their host/server boundary. A per-call ceiling is not a global memory ceiling. Ordinary finite descendants are killed and reaped. Uninterruptible tasks, helper SIGKILL, hostile escape and host compromise are outside that guarantee; incomplete cleanup retains uncertainty and ownership. Use explicit sockets and `-N` for borrowed tmux servers. Retiring a client must not stop a borrowed server. Intentional server creation and server-owned jobs need their own ownership boundaries. ## Building and distribution Mix builds the shipped C source with a C11 compiler and Linux headers. `CC` may name one compiler executable. Generated binaries are excluded from Hex archives and rebuilt during dependency compilation. No third-party runtime package or NIF is added. Packed-core checks must compile and execute outside the checkout. Workspace/MCP escript extraction and helper discovery still need their installed-consumer checks; the current path expects an unpacked application `priv` directory. Other platforms remain unqualified. ## Persistent connection components The internal Guardian owns one native helper and monitors its Manager and router independently. Startup grants one stdout credit after router demand. After activation it keeps one native read in flight and buffers at most 64 KiB or 128 stdout chunks while Router consumes its previous chunk. Each native read and delivered chunk fits 8 KiB. Exceeding either queue ceiling closes the epoch with `resource_limit`; it does not indefinitely stop reading from a still-attached client. These limits do not cap the borrowed tmux server's allocations or total process memory. Guardian caps stderr at 8 KiB and 64 chunks, and acknowledges one write fragment at a time. A fragment fits 8 KiB; a complete rendered command fits 64 KiB. The writer rechecks the consumed publication lease, original deadline and epoch. A partial line keeps its writer until the newline is acknowledged or the epoch closes. Completed slots retain their generation to reject replay. After orderly EOF or native exit, Guardian blocks further writes and drains already-received stdout through the existing Router credits. An end token follows pending native write acknowledgements; Router acknowledges it after processing earlier data and write messages. Only then does ordinary closure finalize remaining operations. Explicit closure, owner loss or protocol failure can abort that drain. Established results survive; unfinished effects remain uncertain. Drain and closure share the original 750 ms Guardian and 800 ms Manager cleanup allowances. Neither clock restarts after data delivery. Helper terminal acknowledgement remains independent of Router progress. Deadline accounting uses one 25 ms timer, rescheduled only after consumption. Busy delivery closes the epoch instead of suspending the owner or retrying. Retirement needs native terminal proof, its acknowledgement and actual helper exit; missing proof or Manager loss leaves the shared lifetime unclean. Supervisor shutdown without those proofs also leaves it unclean. Reserved closure revokes the exact lifetime generation without consuming a command or metadata slot. Guardian observes that word on its accounting timer even when Manager or Router makes no progress. A concurrent starting-to-ready transition cannot escape revocation; retries follow only the same generation. Revocation requests closure and blocks later checks. It does not prove cleanup or prevent a write that already passed its final check. Native and actor retirement remain separate required evidence. Startup uses [`attach-session -E`](https://libtmux.org/en/tmux/latest/manual/attach-session/) to preserve the borrowed session environment. Attachment still changes client counts and session activity. After reading the server PID and Linux process identity, a fresh same-connection challenge must return the exact nonce/PID and receive a complete native write acknowledgement before activation. It shares the original startup deadline. The server and inspected `/proc` must share a PID namespace. This requires host authority for completed, clean configuration throughout the epoch: no aliases overriding admitted command names, no executable effective hooks, and no unqualified messages or subscriptions that can write to the control stream. Other clients can inject multiline text through client-directed messages; parser limits cannot authenticate such output. Absence of a configuration-error notification cannot certify configuration. The library never silently disables hooks or changes configuration. The internal epoch router registers operation identity and FIFO position before sending fragments. A qualified reply after final-fragment dispatch can establish an immutable logical result before the native acknowledgement arrives. The writer still waits for native acknowledgement before advancing; wire retirement also requires it. Completed reply events survive a later parser error in the same chunk. Expired unsent work removes its exact queued token before capacity reuse. A retained reply observation and its logical result are separate copies in the eventual connection byte accounting. The lane admits encoded field reads, literal server user-option writes, split, rename and literal/named input. Option names are bounded ASCII and values do not expand. It rejects arbitrary command lines and screen capture output. Host qualification includes pane-mode key bindings, which can enqueue other commands in response to input. Reply numbers must increase when observed; gaps are normal because other clients and callbacks use the same server counter. This does not detect every possible counter wrap or authenticate an unqualified stream. Metadata requests use a separate fixed ETS inbox before any wake message. Its context binds the real table owner, capacity, allowed request kinds and atomics references. Publication creates the caller's reply alias and token; only a small ready notification enters its mailbox. Owner completion and caller receipt remain separate: a finished row stays occupied until alias closure and receipt acknowledgement, or caller death. A monotone receipt word cannot regress when a delayed old close meets a replacement generation. Wake hints also coalesce across slot reuse; accounting sweeps never clear a hint that a paused publisher still owns. Inbox outcomes copy every binary leaf, including map keys. Copying charges bytes and nodes before allocation, caps depth at 64 and nodes at 8192, and rejects functions, ports and unqualified references. Known context references come from explicitly identified, separately bounded resources. Metadata keeps the default 64 KiB outcome allowance; an independently configured Inbox may reserve 1–128 KiB so a 64 KiB reply plus its envelope fits. The 1 KiB minimum covers fixed fallback errors. Copying shares the original deadline, checked again before result publication. These limits do not yet establish complete connection-wide heap/work accounting. The selected waiter design retains bounded outcome copies in receipt-controlled Inbox rows, superseding the earlier record-reference-only proposal. This avoids cross-table pin/reclamation races at the cost of separately charged copies. The internal waiter owner settles accepted requests from the exact immutable terminal record before releasing it. A prior pending read cannot justify forgetting a later completed record. Closed or expired waiters affect no operation state; pre-acceptance await failures retain unknown effect certainty. Manager services its separate waiter Inbox before reconnect admission and startup continuations, and again after actor events. Both explicit forget and automatic record expiry use the exact-record release barrier. A finished waiter copy survives record reclamation and epoch replacement until receipt. Lost waiter storage closes an active epoch and blocks this Manager's reuse. If native retirement was already proved, that lifetime remains retired; the Manager's fault does not erase cleanup knowledge or preserve lost tables across its replacement. ### Managed execution [`Control.execute/3`]() uses the same operation slots and a collection request in the existing waiter Inbox. It waits for wire retirement, detaches the result for every accepted collector, releases the exact operation lease, then makes collector copies readable. Ordinary [`Operation.await/2`]() may still return an acknowledged result before wire retirement. It does not reclaim the record. Expired or closed collectors settle independently. Failure to prepare the remaining bounded result copies preserves the record. Expiry after release can still prevent delivery: [`Control.ExecutionError`]() then reports lost context. No timeout establishes no effects or authorizes replay. Already completed waiter copies stay in their fixed Inbox slots until receipt or caller death. An acknowledged completed row may be reused by a publisher through an exact-generation compare-and-swap, without waiting for a sweep; unfinished work cannot be recycled this way. A managed lease names its original publisher at admission. The Manager keeps at most one monitor per managed lease and reclaims a dead publisher's record only after wire retirement, including death before collection registration. Existing actor events and publisher DOWN signals service this work; no per-command cleanup process is created. Existing unpublished-lease accounting still fences a publication that never arrives before its deadline. After an execution error, a live caller can inspect its retained Operation until forget, expiry or Manager loss. Death of that original caller also permits reclamation, even if it passed the handle elsewhere. Use [`Control.submit/3`]() for retention independent of the submitting process. Accounting finalizes retained records for all observed dead destinations in one lease scan, matching each epoch and PID together. It preserves acknowledged results and dispatched uncertainty, and releases no live destination's record. [`LibTmux.Operation`]() pins its original record, lease and Inboxes. Retained status and result reads are local. Each await claims a bounded independent slot and releases its exact alias and monitor on return; timeout does not stop the operation. Metadata admission errors cannot label existing effects as unwritten. Canonical failed, skipped and unknown outcomes remain values, including in bang functions. Forget uses the original lease epoch after reconnect. Stop validates the authoritative Births owner, epoch and Router before revoking its generation through reserved capacity; an already-retired record needs no surviving Births table. Stop closes the whole epoch and may affect siblings. It is a request, never rollback or native retirement proof. The retained-record registry finalizes an epoch only after its exact Router PID has exited. It scans current admission leases, including a consumed lease whose Router died before storing its first record. Router stores its record and writing phase before sending, so an absent matching record proves no write under that ordering. Existing acknowledged results remain intact; dispatched work without evidence remains unknown. These outcomes retain their slots until explicit release. Clearing a released record uses its exact old value, preserving any replacement already published in that slot. Logical finalization does not prove Guardian/helper cleanup or authorize a new epoch; Manager must separately establish complete resource retirement. Startup registration uses one fixed row for three epoch actor roles and its gate. Each actor records its own PID before side effects. Closing the gate captures all accepted PIDs in the same CAS; a queued late startup cannot be omitted between a separate read and close. Router and Guardian bind the supplied registration to their manager, epoch and original deadline. Guardian also rechecks actor aliveness and its starting lifetime before opening the driver. An accepted PID remains owned through actual exit: registration or a pre-open check can race later closure. Gate closure alone is not native retirement. Manager keeps one asynchronous request to an ordinary Supervisor outstanding while it accounts for deadlines and metadata. A suspended Supervisor cannot park that accounting. Closing the reply alias does not cancel a queued Supervisor action; an abandoned unresolved start leaves cleanup unknown and blocks replacement. Captured actors remain monitored independently of the request alias. A Router born without a Guardian exits after the startup gate closes. A captured pair binds even during closure, including buffered native retirement events. Explicit reconnect carries its original deadline through old retirement and new bootstrap. Replacement requires native proof, actual Guardian and Router exit, the host's child-finalization acknowledgment and EpochSupervisor exit. Manager restarts preserve the external lifetime latch and do not reconnect automatically. The internal qualified cases retain outcomes across reconnect, forget exact completed leases, and expire retained records. Accepted metadata work remains owned after caller timeout or alias closure; neither cancels it. The internal connection Supervisor owns the view and starts EpochHost before Manager. Its external child-spec factory pins the lifetime across root restarts. Manager replacement invalidates its old tables; root replacement also invalidates the view. A surviving view never recovers old operation evidence. Finite Supervisor shutdown can leave descendants alive; only the shared unknown/unretired lifetime prevents a replacement helper in that case. An unsuccessful descriptor handoff after root startup kills the known root and reports cleanup uncertainty, even after observing root exit. The internal startup adapter passes the remaining initialization budget to OTP's generic server entry with its source-qualified Supervisor init shape. OTP stores the adapter as its callback; the adapter delegates the original module and arguments. A private alias captures the exact Root PID before delegation so cleanup drains only that Root's racing startup ACK or NACK. Descriptor publication rechecks the original deadline, and failed handoff cleanup uses only its remaining budget. OTP still waits for actual Root DOWN after an initialization timeout; this primitive alone does not prove a strict caller-return deadline or complete descendant retirement. The adapter is maintained against [OTP 29.1.2 supervisor startup](https://github.com/erlang/otp/blob/d199a49a01a2079afb6f2c5c9ae047cd4555e68d/lib/stdlib/src/supervisor.erl#L547-L548) and [proc_lib timeout cleanup](https://github.com/erlang/otp/blob/d199a49a01a2079afb6f2c5c9ae047cd4555e68d/lib/stdlib/src/proc_lib.erl#L438-L453). The public Supervisor API has no initialization timeout option. The adapter rejects runtime OTP majors other than 29, or a library build made with an Elixir compiled for another OTP major, as `unsupported_runtime` with no-write certainty before Root startup. Build identity is captured at compilation so this check cannot demand-load version parsing during a cold startup. This is a major-version guard, not patch qualification: the support manifest and reference/tracking runtime checks enforce the declared patch minima and verify the running Elixir's compiled OTP identity. Passing the guard alone does not qualify every OTP 29 patch. When updating OTP support, review the pinned Supervisor initialization shape, parent handling and `proc_lib` timeout/ACK cleanup. Run the direct adapter, connection-Supervisor, cold-loader and native startup/ownership checks on the reference and resolved tracking toolchains before changing the guard. Record their actual versions; identical resolutions qualify one toolchain pair. Public [`Supervisor.start_link`]() has no initialization timeout. A temporary `proc_lib` bridge changes the Root's recorded parent; transferring a link does not adopt a new parent. An external watchdog that kills a still-linked Root can kill a non-trapping caller. Replacing the adapter requires a qualified handoff design as well as a timer. The current isolated dependency remains private OTP coupling despite these compatibility checks. The initialization timer belongs to the caller. If that caller dies while the Supervisor callback is blocked, the Root can outlive its original deadline: the Supervisor traps the parent's exit until initialization returns. The adapter does not promise cleanup after untrappable startup-caller death. Both startup wrappers close their private reply aliases through local BIFs and zero-time receives. Timeout cleanup does not demand-load the shared reply helper after Root exit. Isolated loader-gate checks qualify this dependency; they do not bound OTP's actual-DOWN wait or explain earlier timing failures. Failed initial startup fences the fresh lifetime even before its first epoch reservation. If that reservation wins the CAS race, the fence closes only the first generation. Proven retirement remains intact, and later generations cannot be revoked by an old failure. Root cleanup uses its generation captured at startup entry. Manager can remain inert at generation zero with unknown cleanup through normal shutdown or Host loss, without creating epoch actors. This fence handles observed startup failures; it does not establish recovery from untrappable caller or VM death. One lifetime belongs to one connection Root; concurrent starts sharing that lifetime intentionally fail closed. [`Control.start_link/1`]() requires an explicit Server, exact existing session ID and host-qualified lane. It returns `{:ok, root, control}` only after fresh bootstrap and a bounded ready exchange. Its 1..600000ms timeout includes preparation, Root startup and handoff; every successful handoff rechecks the original deadline. The provisional handle stays internal. Accepted ready replies bind their exact endpoint without a later Births or latest-view lookup. If startup failed before ready registration, the error retains the original failure kind. Cleanup fences the lifetime, requests exact-Root termination, and reports actual Root exit only when observed. Native cleanup can remain unknown. Ordinary linked-process exits retain their OTP semantics. [`Control.child_spec/1`]() prepares detached bounded process bytes without I/O. One lifetime survives its permanent factory's invocations. Each invocation starts a new startup budget, but a nonfresh lifetime returns inert instead of automatically attaching. Explicit reconnect requires proven retirement; unknown cleanup, including generation zero, returns a structured error. [`Supervisor.start_child/2`]() and [`Supervisor.restart_child/2`]() return fresh handle info. After automatic restart, terminate and restart that exact child to obtain the new info; this recovery does not clear unknown cleanup. Retain a distinct child spec/lifetime per Root, and never use an old handle as a locator. [`Control.submit/3`]() pins a ready Root view, Manager and epoch. It detaches the request's binary leaves under a separate 20,000-byte owned-term limit before claiming a fixed operation slot. One deadline includes validation, admission, queueing, writing and replies. After claim, pure Operation binding preserves the handle even if context disappears. A final local gate check can suppress publication; the reserved lease stays owned. Its original deadline requests epoch closure; capacity still follows native/destination retirement and record retention or explicit forget rules. Router and Guardian independently validate before writing. [`Control.request_close/1`]() requests whole-epoch closure without claiming a slot, including when operation and metadata capacity are full. It does not acknowledge native retirement or roll back siblings. Repeated requests for the same closing or proven-retired generation succeed; old handles cannot follow replacement Managers or epochs. Status errors report unknown certainty about existing work; a rejected new, unadmitted submission reports no write. [`Control.close/2`]() waits for native and epoch-actor retirement plus the Host's epoch-child finalization acknowledgement under its original budget. An admitted metadata watcher retains its fixed Inbox cell through settlement, including after caller closure. Metadata settlement failure blocks replacement. Ready watches never start a failed or closed epoch. Accepted retirement watches settle before reconnect in the same batch, independent of slot order. Epoch reservation rechecks metadata and waiter storage; lost storage cannot authorize replacement. [`Control.reconnect/2`]() returns the acknowledged endpoint directly. A delayed caller receives that epoch even if a newer one already exists; its handle then reports stale. It does not reread the latest view or replay operations. The Root and retained records outlive ordinary epoch closure. Public lifecycle errors preserve unknown certainty about existing effects; direct retirement proof must still be observed within the original close budget. Strict caller-return startup bounds, connection-wide accounting and authoritative subscriptions remain unqualified. These component checks do not qualify those contracts. --- # Command and field encoding Source: https://libtmux.org/en/elixir/latest/topics/encoding/ > Render commands and decode fields without a shell or a raw newline. [`LibTmux.Command`]() is a value. Constructing it or rendering its bytes performs no I/O. Both one-shot execution and the qualified persistent control lane use these encoders. ## Separate parsers [`Command.argv/2`]() returns process arguments and protects terminal semicolons from tmux's argument-vector parser. It does not invoke a shell. [`Command.render/2`]() produces one tmux command line with every argument byte octal-escaped inside double quotes. The result contains no raw newline or carriage return. It excludes the framing newline the control writer adds. Both renderers validate the encoded byte budget before dispatch. Command quoting does not protect a value that a command interprets as a tmux format. [`LibTmux.Format.literal/1`]() constructs a literal format operand without evaluating embedded `#{...}`, shell jobs or shorthand variables. It accepts non-NUL bytes. The command still needs its own appropriate format flags; arbitrary commands have different expansion behavior. ## One field per response [`Format.field/1`]() encodes a named field with a `v1:` marker. It replaces backslashes first, then dollars and ASCII control bytes, using fixed tmux substitutions. [`LibTmux.Codec.decode_field/3`]() with `encoding: :escaped_v1` requires that marker, removes one framing newline and decodes escapes once. The conservative field format performs several substitutions. This encoding avoids an ambiguity in tmux 3.4: its command print path escapes some bytes while preserving literal backslashes. Removing arbitrary backslashes from that output would corrupt legitimate values. A versioned field encoding protects those values before printing. A separator byte alone cannot do that, because titles and paths may contain it. The default raw decoder removes only the final framing newline. Its source must independently guarantee unmodified bytes. It must not be applied to an unqualified tmux print path. Text decoding requires UTF-8; `:bytes` retains arbitrary bytes. Integer parsing consumes the whole value and can enforce a range; Boolean parsing accepts only `0` or `1`. ## Capture rows Snapshot capture batches each physical entity's free-form fields in one response. A row starts with `r1:N`, followed by N tab-separated `v1:` fields and one final newline. Each field uses the same conservative encoder above: tabs and newlines inside values become escapes, so they cannot become row delimiters. The decoder requires the expected version and field count, rejects malformed or truncated data, then validates each decoded field against its schema. Invalid UTF-8 remains an error for text fields. The response ceiling applies to the complete encoded row, including markers and separators. Multiple fields that each fit the ceiling individually can exceed it together; capture returns a resource-limit error, with no partial graph or alternate decoder. The default ceiling remains 64 KiB per response. One successful attempt uses `10 + S + W + P` commands, where S, W and P are the physical session, window and pane counts. Each command still launches one owned helper and one tmux client. Linked windows share their text read. The two topology scans, incarnation checks, contextual-row accounting and capture-wide deadline and byte budgets remain in force. This encoding does not qualify arbitrary pane output or hook output for a shared control lane. ## Bounded framing [`LibTmux.Protocol`]() is a pure control-mode parser with separate chunk, line, frame, line-count and emitted-event ceilings. It retains reply guard IDs; notification-looking lines inside a reply remain output. A rejected suffix preserves completed prefix events in the structured error. Retire a lane after a parse error. Lexical parsing cannot authenticate output that forges a matching guard. Commands, aliases, hooks and subscriptions require separate admission rules before sharing a persistent lane. Parser ceilings also do not bound upstream Port delivery or a connection owner's mailbox. Those transport contracts require independent qualification. --- # Observation and filter schema Source: https://libtmux.org/en/elixir/latest/topics/schema/ > Fields, wire names, value categories and operators of each observed entity. Generated from [`LibTmux.Schema`](); regenerate with `mix run scripts/generate_schema.exs`. All current fields use the tmux 3.3 capture baseline. Coverage must still be loaded. Only `nullable_integer` admits [`nil`](); other unavailable or unloaded fields raise. Link IDs are `{session_id, index, window_id}` locally and three-element arrays on the wire. See [filtering](https://libtmux.org/en/elixir/latest/guides/filtering/) for Boolean, relation and error semantics. ## `session` | Elixir field | Wire field | Value category | tmux format | Operators | | --- | --- | --- | --- | --- | | [`attached`]() | [`attached`]() | `:boolean` | Derived | `equals`, `not`, `in`, `not_in` | | [`attached_count`]() | `attachedCount` | `:integer` | [`session_attached`](https://libtmux.org/en/tmux/latest/catalog/formats/session_attached/) | `equals`, `not`, `in`, `not_in`, `lt`, `lte`, `gt`, `gte` | | [`id`]() | [`id`]() | `{:id, :session}` | [`session_id`](https://libtmux.org/en/tmux/latest/catalog/formats/session_id/) | `equals`, `not`, `in`, `not_in` | | `name` | `name` | `:string` | [`session_name`](https://libtmux.org/en/tmux/latest/catalog/formats/session_name/) | `equals`, `not`, `in`, `not_in`, `contains`, `starts_with`, `ends_with` | | Relation | Wire relation | Cardinality | Target | Operators | | --- | --- | --- | --- | --- | | [`active_window`]() | `activeWindow` | [`one`]() | `window` | `is`, `is_not` | | `clients` | `clients` | `many` | `client` | `some`, `every`, [`none`](), [`count`]() | | `panes` | `panes` | `many` | `pane` | `some`, `every`, [`none`](), [`count`]() | | `window_links` | `windowLinks` | `many` | `window_link` | `some`, `every`, [`none`](), [`count`]() | | `windows` | `windows` | `many` | `window` | `some`, `every`, [`none`](), [`count`]() | ## `window` | Elixir field | Wire field | Value category | tmux format | Operators | | --- | --- | --- | --- | --- | | `height` | `height` | `:integer` | [`window_height`](https://libtmux.org/en/tmux/latest/catalog/formats/window_height/) | `equals`, `not`, `in`, `not_in`, `lt`, `lte`, `gt`, `gte` | | [`id`]() | [`id`]() | `{:id, :window}` | [`window_id`](https://libtmux.org/en/tmux/latest/catalog/formats/window_id/) | `equals`, `not`, `in`, `not_in` | | [`layout`]() | [`layout`]() | `:string` | [`window_layout`](https://libtmux.org/en/tmux/latest/catalog/formats/window_layout/) | `equals`, `not`, `in`, `not_in`, `contains`, `starts_with`, `ends_with` | | `name` | `name` | `:string` | [`window_name`](https://libtmux.org/en/tmux/latest/catalog/formats/window_name/) | `equals`, `not`, `in`, `not_in`, `contains`, `starts_with`, `ends_with` | | `width` | `width` | `:integer` | [`window_width`](https://libtmux.org/en/tmux/latest/catalog/formats/window_width/) | `equals`, `not`, `in`, `not_in`, `lt`, `lte`, `gt`, `gte` | | [`zoomed`]() | [`zoomed`]() | `:boolean` | [`window_zoomed_flag`](https://libtmux.org/en/tmux/latest/catalog/formats/window_zoomed_flag/) | `equals`, `not`, `in`, `not_in` | | Relation | Wire relation | Cardinality | Target | Operators | | --- | --- | --- | --- | --- | | `panes` | `panes` | `many` | `pane` | `some`, `every`, [`none`](), [`count`]() | | `sessions` | `sessions` | `many` | `session` | `some`, `every`, [`none`](), [`count`]() | | `window_links` | `windowLinks` | `many` | `window_link` | `some`, `every`, [`none`](), [`count`]() | ## `window_link` | Elixir field | Wire field | Value category | tmux format | Operators | | --- | --- | --- | --- | --- | | `active` | `active` | `:boolean` | [`window_active`](https://libtmux.org/en/tmux/latest/catalog/formats/window_active/) | `equals`, `not`, `in`, `not_in` | | [`id`]() | [`id`]() | `{:id, :window_link}` | Derived | `equals`, `not`, `in`, `not_in` | | `index` | `index` | `:integer` | [`window_index`](https://libtmux.org/en/tmux/latest/catalog/formats/window_index/) | `equals`, `not`, `in`, `not_in`, `lt`, `lte`, `gt`, `gte` | | [`session_id`](https://libtmux.org/en/tmux/latest/catalog/formats/session_id/) | `sessionId` | `{:id, :session}` | [`session_id`](https://libtmux.org/en/tmux/latest/catalog/formats/session_id/) | `equals`, `not`, `in`, `not_in` | | [`window_id`](https://libtmux.org/en/tmux/latest/catalog/formats/window_id/) | `windowId` | `{:id, :window}` | [`window_id`](https://libtmux.org/en/tmux/latest/catalog/formats/window_id/) | `equals`, `not`, `in`, `not_in` | | Relation | Wire relation | Cardinality | Target | Operators | | --- | --- | --- | --- | --- | | `session` | `session` | [`one`]() | `session` | `is`, `is_not` | | `window` | `window` | [`one`]() | `window` | `is`, `is_not` | ## `pane` | Elixir field | Wire field | Value category | tmux format | Operators | | --- | --- | --- | --- | --- | | `active` | `active` | `:boolean` | [`pane_active`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_active/) | `equals`, `not`, `in`, `not_in` | | [`current_command`]() | `currentCommand` | `:string` | [`pane_current_command`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_current_command/) | `equals`, `not`, `in`, `not_in`, `contains`, `starts_with`, `ends_with` | | [`current_path`]() | `currentPath` | `:string` | [`pane_current_path`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_current_path/) | `equals`, `not`, `in`, `not_in`, `contains`, `starts_with`, `ends_with` | | [`dead`]() | [`dead`]() | `:boolean` | [`pane_dead`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_dead/) | `equals`, `not`, `in`, `not_in` | | [`exit_status`]() | `exitStatus` | `:nullable_integer` | [`pane_dead_status`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_dead_status/) | `equals`, `not`, `in`, `not_in`, `lt`, `lte`, `gt`, `gte` | | `height` | `height` | `:integer` | [`pane_height`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_height/) | `equals`, `not`, `in`, `not_in`, `lt`, `lte`, `gt`, `gte` | | [`id`]() | [`id`]() | `{:id, :pane}` | [`pane_id`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_id/) | `equals`, `not`, `in`, `not_in` | | `index` | `index` | `:integer` | [`pane_index`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_index/) | `equals`, `not`, `in`, `not_in`, `lt`, `lte`, `gt`, `gte` | | [`pid`]() | [`pid`]() | `:nullable_integer` | [`pane_pid`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_pid/) | `equals`, `not`, `in`, `not_in`, `lt`, `lte`, `gt`, `gte` | | [`title`]() | [`title`]() | `:string` | [`pane_title`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_title/) | `equals`, `not`, `in`, `not_in`, `contains`, `starts_with`, `ends_with` | | `width` | `width` | `:integer` | [`pane_width`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_width/) | `equals`, `not`, `in`, `not_in`, `lt`, `lte`, `gt`, `gte` | | [`window_id`](https://libtmux.org/en/tmux/latest/catalog/formats/window_id/) | `windowId` | `{:id, :window}` | [`window_id`](https://libtmux.org/en/tmux/latest/catalog/formats/window_id/) | `equals`, `not`, `in`, `not_in` | | Relation | Wire relation | Cardinality | Target | Operators | | --- | --- | --- | --- | --- | | `window` | `window` | [`one`]() | `window` | `is`, `is_not` | ## `client` | Elixir field | Wire field | Value category | tmux format | Operators | | --- | --- | --- | --- | --- | | [`control_mode`]() | `controlMode` | `:boolean` | [`client_control_mode`](https://libtmux.org/en/tmux/latest/catalog/formats/client_control_mode/) | `equals`, `not`, `in`, `not_in` | | `height` | `height` | `:nullable_integer` | [`client_height`](https://libtmux.org/en/tmux/latest/catalog/formats/client_height/) | `equals`, `not`, `in`, `not_in`, `lt`, `lte`, `gt`, `gte` | | [`id`]() | [`id`]() | `{:id, :client}` | [`client_name`](https://libtmux.org/en/tmux/latest/catalog/formats/client_name/) | `equals`, `not`, `in`, `not_in` | | [`pid`]() | [`pid`]() | `:integer` | [`client_pid`](https://libtmux.org/en/tmux/latest/catalog/formats/client_pid/) | `equals`, `not`, `in`, `not_in`, `lt`, `lte`, `gt`, `gte` | | [`session_id`](https://libtmux.org/en/tmux/latest/catalog/formats/session_id/) | `sessionId` | `{:id, :session}` | [`session_id`](https://libtmux.org/en/tmux/latest/catalog/formats/session_id/) | `equals`, `not`, `in`, `not_in` | | `width` | `width` | `:integer` | [`client_width`](https://libtmux.org/en/tmux/latest/catalog/formats/client_width/) | `equals`, `not`, `in`, `not_in`, `lt`, `lte`, `gt`, `gte` | | Relation | Wire relation | Cardinality | Target | Operators | | --- | --- | --- | --- | --- | | `session` | `session` | [`one`]() | `session` | `is`, `is_not` |