# libtmux for OCaml > The OCaml library (libtmux). These pages include its guides, tested examples and API documentation. - [OCaml API reference](https://libtmux.org/en/ocaml/latest/reference/): every public symbol, generated from the source. Hosted on libtmux.org. --- # OCaml concepts Source: https://libtmux.org/en/ocaml/latest/concepts/ > Understand immutable observations, window links and typed queries. Snapshots hold immutable observations. A window can have several session links; each link retains its own index and context. Process captured collections with [`Iarray`](), [`Seq`]() and ordinary functions. Typed expressions add inspectable query data without replacing those collections. - [Captured object graph](https://libtmux.org/en/ocaml/latest/guides/graph/): Navigate sessions, shared windows, contextual links and panes. - [Local queries](https://libtmux.org/en/ocaml/latest/guides/queries/): Compose native predicates and typed expressions over captured values. - [Package boundaries](https://libtmux.org/en/ocaml/latest/guides/packages/): Choose the pure core or optional transports and consumers. --- # tmux client library for OCaml Source: https://libtmux.org/en/ocaml/latest/core/ > tmux client library for OCaml. Control tmux, connect to and create servers, navigate sessions, windows, and panes, send keys, watch for changes via control mode, capture pane contents Use the native OCaml library to capture sessions, shared windows and panes. Process observations with [`Iarray`](), [`Seq`]() and ordinary predicates. Typed expressions add inspection and JSON interchange; client calls perform I/O. The core and workspace library depend on the standard library. Blocking Linux, Eio, JSON and the workspace CLI are separate packages. This port excludes MCP. [Installation and first use](https://libtmux.org/en/ocaml/latest/guides/getting-started/) · [Captured graph](https://libtmux.org/en/ocaml/latest/guides/graph/) · [API reference](https://libtmux.org/en/ocaml/latest/reference/)Control tmux, connect to and create servers, navigate sessions, windows, and panes, send keys, watch for changes via control mode, capture pane contents. ## At a glance | Type | Wraps | | ---- | ----- | | [`Libtmux_unix.Client.t`](https://libtmux.org/en/ocaml/latest/reference/libtmux_unix-client-t/) | A connection to one tmux server | | [`Libtmux.Session.t`](https://libtmux.org/en/ocaml/latest/reference/libtmux-session-t/) | A session, ID `$n` | | [`Libtmux.Window.t`](https://libtmux.org/en/ocaml/latest/reference/libtmux-window-t/) | A shared tmux window, ID `@n` | | [`Libtmux.Pane.t`](https://libtmux.org/en/ocaml/latest/reference/libtmux-pane-t/) | A pane in a window, ID `%n` | ## Typed API - Typed objects instead of parsed format strings: [`Libtmux.Pane`](https://libtmux.org/en/ocaml/latest/reference/libtmux-pane/) - Addressing by tmux ID: [`Libtmux.Pane.Id`](https://libtmux.org/en/ocaml/latest/reference/libtmux-pane-id-16to/) - Filtering and queries over tmux objects: [`Libtmux.Expr`](https://libtmux.org/en/ocaml/latest/reference/libtmux-expr/) - Typed errors instead of exit codes and stderr: [`Libtmux.Error`](https://libtmux.org/en/ocaml/latest/reference/libtmux-error/) ## What you get over the tmux CLI - Connect to or start a tmux server: [`Libtmux_unix.Client.connect`](https://libtmux.org/en/ocaml/latest/reference/libtmux_unix-client-connect/) - Traversal from a server down to its panes: [`Libtmux.Snapshot.panes`](https://libtmux.org/en/ocaml/latest/reference/libtmux-snapshot-panes/) - Send keys to a pane: [`Libtmux_unix.Client.send_keys`](https://libtmux.org/en/ocaml/latest/reference/libtmux_unix-client-send_keys/) - Watch for changes through tmux control mode: [`Libtmux_eio.Client.subscribe`](https://libtmux.org/en/ocaml/latest/reference/libtmux_eio-client-subscribe/) - Capture pane contents: [`Libtmux_unix.Client.capture_text`](https://libtmux.org/en/ocaml/latest/reference/libtmux_unix-client-capture_text/) - Arguments passed as a list, with no shell quoting: [`Libtmux.Command`](https://libtmux.org/en/ocaml/latest/reference/libtmux-command/) - A typed error when the running tmux lacks a feature: [`Libtmux.Error.Unsupported_version`](https://libtmux.org/en/ocaml/latest/reference/libtmux-error-t-unsupported_version/) - Async operations: [`Libtmux_eio.Client.capture`](https://libtmux.org/en/ocaml/latest/reference/libtmux_eio-client-capture/) - [Guides](https://libtmux.org/en/ocaml/latest/guides/): Capture, query, streaming and workspace guides. - [API reference](https://libtmux.org/en/ocaml/latest/reference/): Public modules, types and values. --- # OCaml examples Source: https://libtmux.org/en/ocaml/latest/examples/ > Run complete native OCaml programs against an isolated tmux server. Start with the [graph program](https://libtmux.org/en/ocaml/latest/guides/getting-started/), then run the [capability showcases](https://libtmux.org/en/ocaml/latest/guides/showcases/). Each complete program uses public packages and documents its inputs and ownership of tmux resources. - [Capability showcases](https://libtmux.org/en/ocaml/latest/guides/showcases/): Run installed programs with explicit setup, output checks and cleanup. - [Partial-failure recovery](https://libtmux.org/en/ocaml/latest/guides/recovery/): Inspect retained outcomes before deciding which action to take next. - [Workspaces](https://libtmux.org/en/ocaml/latest/workspace/source-guide/): Validate, plan and apply a complete JSON workspace. --- # Guides Source: https://libtmux.org/en/ocaml/latest/guides/ > Guides for libtmux. Connect to tmux and work with its sessions, windows, and panes. - [Install and run your first capture](https://libtmux.org/en/ocaml/latest/guides/getting-started/): Read about install and run your first capture. - [Start a private tmux server from OCaml](https://libtmux.org/en/ocaml/latest/guides/bootstrap/): Read about start a private tmux server from ocaml. - [Sessions, window links and panes](https://libtmux.org/en/ocaml/latest/guides/graph/): Read about sessions, window links and panes. - [Query captured data](https://libtmux.org/en/ocaml/latest/guides/queries/): Read about query captured data. - [Share a scalar pane query](https://libtmux.org/en/ocaml/latest/guides/query-interchange/): Read about share a scalar pane query. - [Submit actions and retain their outcomes](https://libtmux.org/en/ocaml/latest/guides/control/): Read about submit actions and retain their outcomes. - [Recover an operation after cancelling its waiter](https://libtmux.org/en/ocaml/latest/guides/recovery/): Read about recover an operation after cancelling its waiter. - [Observe output and wait for rendered text](https://libtmux.org/en/ocaml/latest/guides/streaming/): Read about observe output and wait for rendered text. - [Run a complete capability example](https://libtmux.org/en/ocaml/latest/guides/showcases/): Read about run a complete capability example. - [Choose the packages your application needs](https://libtmux.org/en/ocaml/latest/guides/packages/): Read about choose the packages your application needs. - [Compatibility](https://libtmux.org/en/ocaml/latest/guides/compatibility/): Read about compatibility. - [Testing and CI](https://libtmux.org/en/ocaml/latest/guides/testing/): Read about testing and ci. - [Measure capture, concurrency and output delivery](https://libtmux.org/en/ocaml/latest/guides/benchmarking/): Read about measure capture, concurrency and output delivery. - [Current capabilities and adoption gaps](https://libtmux.org/en/ocaml/latest/guides/status/): Read about current capabilities and adoption gaps. --- # Install and run your first capture Source: https://libtmux.org/en/ocaml/latest/guides/getting-started/ > Install and run your first capture: libtmux documentation. Use the [README installation](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/README.md#install-from-source) with OCaml 5.5.2, opam and tmux on Linux. The native clients compile a small Linux C helper and require a C compiler and system headers. This source checkout installs the libraries; no registry release is available yet. Activate the local switch in the current shell as shown in the README before changing to a separate project. This exports the switch choice for `opam exec` without changing shell startup files or the global default switch. ## Link an installed library For a blocking program, use a Dune executable with `libtmux` and `libtmux-unix`: ```dune (executable (name main) (libraries libtmux libtmux-unix)) ``` For the Eio examples, link the Eio backend and the application's runtime entry point instead: ```dune (executable (name main) (libraries libtmux libtmux-eio eio_main)) ``` Create `dune-project` with `(lang dune 3.24)`, save the [graph program](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/native_graph.ml) as [`main.ml`](), and build from that project. The installed-example check does this outside the repository with a separate install prefix; it does not depend on source-tree library resolution. ```console $ opam exec -- dune build ``` ## Select an endpoint explicitly [`Endpoint.socket_path`]() accepts an absolute socket path; [`Endpoint.named_socket`]() describes tmux's `-L` name. Constructing either performs no I/O. Ordinary `Client.connect` attaches to an existing daemon and does not start one. The [private demo setup](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/README.md#read-a-real-tmux-graph) creates an owned socket and a `cat` pane. Run the graph program with the tmux binary and socket path, or copy its complete source into your project. It captures the graph, closes its connection and prints retained observations. A connection identifies the daemon PID twice around pidfd acquisition. The client and daemon must share a PID namespace and local scratch filesystem; an accessible socket by itself does not establish those conditions. A stale source is an error, never permission to reconnect a mutation automatically. ## Run a concurrent application The [complete Eio example](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/native_watch.ml) calls `Eio_main.run` and `Eio.Switch.run`, then supplies `process_mgr` and `mono_clock` from the application environment. Use that outer shape in your application. Capturing and querying values does not require adopting Eio in the pure core. Expected failures return `result` and print with [`Error.pp`](). Backend cancellation propagates separately. Clients and subscriptions belong to their connecting domain; immutable observations can be shared. Close owned resources with [`Fun.protect`]() or the supplied Eio switch. [Object graph](https://libtmux.org/en/ocaml/latest/guides/graph/) · [Queries](https://libtmux.org/en/ocaml/latest/guides/queries/) · [Control](https://libtmux.org/en/ocaml/latest/guides/control/) · [API](https://libtmux.org/en/ocaml/latest/reference/) · [Compatibility](https://libtmux.org/en/ocaml/latest/guides/compatibility/) --- # Start a private tmux server from OCaml Source: https://libtmux.org/en/ocaml/latest/guides/bootstrap/ > Start a private tmux server from OCaml: libtmux documentation. Use `Workspace.submit_bootstrap` when the endpoint is known to be missing. It creates the first session and binds a client through the same control connection. The native workspace builder needs no JSON package; `Workspace.await` performs startup. For an existing daemon, use `Client.connect`, capture its graph, and create a session through that client. See [control and recovery](https://libtmux.org/en/ocaml/latest/guides/control/). ## Run the complete program Complete the [source installation](https://libtmux.org/en/ocaml/latest/guides/getting-started/), then run from the checkout: ```console $ opam exec -- dune exec examples/showcase_bootstrap.exe -- tmux ``` The program prints its temporary socket path followed by `bootstrapped: 1 session, 1 window, 1 pane`. It creates a `bootstrap-demo` session whose `editor` window runs `/bin/cat -u`, captures the graph, and reads that snapshot after closing its client. The [complete source](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/showcase_bootstrap.ml) includes its private directory, tmux configuration, local version discovery and cleanup. To use the whole program outside this checkout, save it as `main.ml` in the Dune project from [first use](https://libtmux.org/en/ocaml/latest/guides/getting-started/#link-an-installed-library) with: ```dune (executable (name main) (libraries libtmux libtmux-workspace libtmux-unix unix)) ``` CI compiles that complete file against installed packages and checks its exact outcome line, graph assertions, directory cleanup and owned process exit on tmux 3.3a and 3.8. The [executed inventory](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/showcase.json) records its command and workflow. ## Build, plan, await and capture The central function receives the binary's discovered version and a new private socket path. [`Config.make`](), [`Plan.make`]() and submission perform no I/O; the function returns a retained execution handle: ```ocaml let bootstrap ~binary ~version ~directory endpoint = let spawn = Spawn.make (Exec { program = "/bin/cat"; args = [ "-u" ] }) in let config = Config.make ~directory ~session:"bootstrap-demo" [ Config.window ~name:"editor" [ Config.pane ~spawn () ] ] |> checked Config.pp_error in let plan = Plan.make ~endpoint ~version ~observed:None config |> checked Plan.pp_error in get (Workspace.submit_bootstrap ~binary plan) ``` Passing [`None`](https://ocaml.org/manual/5.5/api/Option.html#TYPEELTt.None) as the `observed` argument means the endpoint is known to be missing. The full program uses a newly created private directory to establish that precondition. A failed capture must remain an error; treating it as a missing observation discards that failure and produces the wrong plan. A plan remains advisory; another process can still change the endpoint before execution. Keep that handle outside the cleanup scope. Await it, check the receipt, then borrow its client for the capture: ```ocaml let snapshot = protect ~finally:(fun () -> get (Workspace.close execution)) (fun () -> let receipt = get (Workspace.await execution) in require_success receipt; let client = Option.get (Workspace.client execution) in get (Client.capture client)) ``` `require_success` reports failed steps and compensation before raising. The [`protect` helper](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/showcase_bootstrap.ml#L14) keeps that primary exception if closing also fails, reporting the cleanup failure separately. Both helpers are in the complete source. `Workspace.inspect execution` reads the retained journal after closure; inspect reported creations and uncertainty before choosing another action. The program stops without retrying. The [workspace guide](https://libtmux.org/en/ocaml/latest/workspace/source-guide/#collisions-and-recovery) explains those receipts. `Workspace.client` borrows the bootstrapped client. Close the workspace handle after using that client; its snapshot stays readable. ## Choose the daemon's lifetime Bootstrapping owns the new client, not the tmux daemon. Closing the handle releases that client. The example explicitly configures its private daemon with [`exit-unattached on`](https://libtmux.org/en/tmux/latest/catalog/options/exit-unattached/), supplied through a small shell exec wrapper. The daemon and its demonstration pane exit after the final client detaches. It then removes only its own temporary files. For an application that keeps the server, choose its normal tmux lifetime policy and retain the workspace handle while using the borrowed client. An existing or newly visible daemon does not become owned merely because this application connected to it. Cancellation and a partial bootstrap can leave effects; use [retained operation recovery](https://libtmux.org/en/ocaml/latest/guides/recovery/) and the workspace receipt rather than assuming closure undoes them. [Object graph](https://libtmux.org/en/ocaml/latest/guides/graph/) · [Workspaces](https://libtmux.org/en/ocaml/latest/workspace/source-guide/) · [Guide](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/README.md) --- # Sessions, window links and panes Source: https://libtmux.org/en/ocaml/latest/guides/graph/ > Sessions, window links and panes: libtmux documentation. A snapshot contains sessions, global windows, session-specific window links, panes and captured clients. Entity values are abstract immutable observations. They remain readable after their client closes. ```text Session ── Window_link ── Window ── Pane index │ another link can share this window ``` A window can be linked to multiple sessions, or linked more than once within one session. [`Session.window_links`]() retains each context and index. [`Session.windows`]() returns distinct global windows in first-link order. [`Window.panes`]() contains that global window's captured panes. Local traversal performs no I/O, lazy refresh or per-object subprocess calls. Captured client rows and attached-client counts include the library's control connection when it is present. The [graph program](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/native_graph.ml) deliberately walks links. To see the same window in two sessions, run the [README setup](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/README.md#read-a-real-tmux-graph), then create another session: ```console $ env -u TMUX -u TMUX_PANE tmux \ -S "$LIBTMUX_DEMO_DIR/server" \ new-session \ -d \ -s linked \ -- "exec cat" ``` Link the demo's window at index 7 in the other session: ```console $ env -u TMUX -u TMUX_PANE tmux \ -S "$LIBTMUX_DEMO_DIR/server" \ link-window \ -d \ -s @0 \ -t linked:7 ``` ```console $ opam exec -- dune exec examples/native_graph.exe -- \ tmux "$LIBTMUX_DEMO_DIR/server" ``` The same window ID and pane ID appear under both sessions, at their respective link indexes. The installed-example check asserts both appearances. Remove only the additional session when finished: ```console $ env -u TMUX -u TMUX_PANE tmux \ -S "$LIBTMUX_DEMO_DIR/server" \ kill-session -t '=linked' ``` An entity ID is scoped to a connection source. `same_entity` requires the same bound source generation as well as the same entity ID; separate connections compare unequal even when they observe the same daemon. `same_observation` also distinguishes captures. Use those operations or explicit `Id.equal`, rather than polymorphic equality over observations. A valid target does not make captured names, geometry or membership current. A link context is an observed session/index/window tuple, not a stable link incarnation across unlinking or reindexing. Capture provenance records an acquisition interval. Several tmux commands do not form an atomic server transaction. Disappearing or conflicting objects produce a capture error rather than a fabricated empty relationship. Each entity retains the complete snapshot graph so its relationships remain available. Retaining one pane from every refresh therefore retains every one of those captures. Keep scalar summaries when navigation is unnecessary. `Pane.target` omits the graph but remains bound to its original connection generation; it is not a durable target for a future connection. Relationship traversal can allocate wrapper arrays even when a query stops testing early. The [retained-heap measurements](https://libtmux.org/en/ocaml/latest/guides/benchmarking/#heap-retained-across-captures) compare these choices across repeated real captures. [Queries](https://libtmux.org/en/ocaml/latest/guides/queries/) · [Recovery](https://libtmux.org/en/ocaml/latest/guides/control/) · [API](https://libtmux.org/en/ocaml/latest/reference/) --- # Query captured data Source: https://libtmux.org/en/ocaml/latest/guides/queries/ > Query captured data: libtmux documentation. Capture once, then use standard collections. The [complete program](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/native_query.ml) closes its blocking client before evaluating any of the functions below. Follow the [README setup](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/README.md#read-a-real-tmux-graph) first. ## Use a native predicate ```ocaml let active_ids snapshot = Snapshot.panes snapshot |> Iarray.to_seq |> Seq.filter Pane.active |> Seq.map Pane.id |> List.of_seq ``` [`Snapshot.panes`]() returns an immutable array of captured panes. Converting it to a sequence does not acquire anything. Repeated traversal sees captured values; stateful callbacks supplied by an application can still behave differently. ## Describe a typed query ```ocaml let roomy_terminals : Pane.t Expr.t = Expr.all [ Expr.Bool.is_true Pane.Fields.active; Expr.Text.is_in Pane.Fields.current_command [ "cat"; "bash"; "zsh"; "nvim" ]; Expr.Int.ge Pane.Fields.width 80; ] let roomy_ids snapshot = Snapshot.panes snapshot |> Iarray.to_seq |> Query.matching roomy_terminals |> Seq.map Pane.id |> List.of_seq ``` [`Expr.compile`]() returns an ordinary predicate. [`Query.matching`]() compiles once when constructed and preserves order and multiplicity. Field descriptors carry both entity and value types, so invalid entity/operator combinations fail in the compiler. Boolean `Expr.all []` is true and `Expr.any []` is false. ## Follow relationships without fetching ```ocaml let sessions_with_room snapshot = let expression = Expr.Rel.any Session.Relations.windows (Expr.Rel.any Window.Relations.panes roomy_terminals) in Snapshot.sessions snapshot |> Iarray.to_seq |> Query.matching expression |> Seq.map Session.name |> List.of_seq ``` Relationships traverse the complete captured graph. [`Expr.Rel.any`]() over no children is false; `all` and [`none`]() over no children are true. Put conditions inside one `any` when the same child must satisfy both. Two independent existentials can match different children. ## Establish cardinality before acting [`Query.exactly_one`]() returns [`No_match`]() or `Multiple_matches {at_least=2}` instead of choosing the first element. It stops once it finds a second match; proving uniqueness exhausts the remaining finite sequence. [`one_or_none`]() returns an option while retaining an ambiguity error. Duplicate entries count separately. Select first, inspect the result, then submit a mutation. Selection does not lock tmux; a target can disappear between observation and action. The [control guide](https://libtmux.org/en/ocaml/latest/guides/control/) explains that failure boundary. ## Decode portable query data explicitly `Libtmux_json.Expr.of_string Model.pane` validates the shared scalar pane subset of [`libtmux.filter/1`](https://libtmux.org/en/ocaml/latest/guides/query-interchange/). It rejects duplicate/unknown keys, wrong operand types, invalid UTF-8, oversized/deep documents and Java regex. Native closures stay in [`Seq.filter`](); they have no portable wire identity. [`Libtmux.Plan.panes`]() compiles a source filter and retains the residual expression without I/O. Inspect [`source_filter`]() and use [`Require_exact`]() to reject unsupported lowering. Current client capture operations acquire the complete graph; they do not execute these plans. The planner is a separate seam for acquisition adapters. Conjunctions, disjunctions, negation and relationships require different lowering rules; local sequence processing never issues a tmux query implicitly. The [native reference](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/lib/index.mld) executes a pure planning example with MDX. The [architecture capability table](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/dev/adr/0001-native-ocaml-suite.md#compose-ordinary-predicates-with-typed-query-data) distinguishes observation accessors, native expressions, JSON, planning and client acquisition. An available accessor or format name need not be a portable expression field. [Field reference](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/fields.md) · [API](https://libtmux.org/en/ocaml/latest/reference/) · [Guide](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/README.md) --- # Share a scalar pane query Source: https://libtmux.org/en/ocaml/latest/guides/query-interchange/ > Share a scalar pane query: libtmux documentation. [`Libtmux_json.Expr`]() reads and writes the following subset of [`libtmux.filter/1`][java-wire]. Use [`Model.pane`]() as the model witness. This document defines the OCaml interoperability boundary; the [query guide](https://libtmux.org/en/ocaml/latest/guides/queries/) shows local evaluation and the [complete JSON program](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/query_json.ml) demonstrates decoding, encoding and structured errors. ## Document and nodes A document has exactly `"schema"`, `"model"` and `"expr"` properties. The supported values are `"libtmux.filter/1"` and `"pane"`. An expression is one of these nodes: | `"node"` | Other required properties | Meaning | | --- | --- | --- | | `"compare"` | `"field"`, `"op"`, `"value"` | Compare one canonical scalar field | | `"and"` | `"operands"`: expression array | Every operand matches; empty is true | | `"or"` | `"operands"`: expression array | Any operand matches; empty is false | | `"not"` | `"operand"`: expression | Negate one expression | Property order is irrelevant. Duplicate, missing and unknown properties are errors. Encoders preserve query meaning, not the original spelling: a `"not_equals"` comparison can encode as `"not"` around `"equals"`. ## Fields and operators Field names are case-sensitive canonical identities, not arbitrary accessors. | Value type | Fields | Operators | | --- | --- | --- | | String | [`"pane_id"`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_id/), [`"pane_current_command"`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_current_command/), [`"pane_title"`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_title/), [`"pane_current_path"`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_current_path/) | `"equals"`, `"not_equals"`, `"starts_with"`, `"ends_with"`, `"contains"`, `"in"` | | Integer | [`"pane_index"`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_index/), [`"pane_width"`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_width/), [`"pane_height"`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_height/), [`"pane_left"`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_left/), [`"pane_top"`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_top/) | `"equals"`, `"not_equals"`, `"less_than"`, `"at_most"`, `"greater_than"`, `"at_least"` | | Boolean | [`"pane_active"`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_active/) | `"equals"`, `"not_equals"` | `"value"` has the field's JSON type. String `"in"` takes an array of strings and matches equality with any member; an empty array matches nothing. Integers must be integral JSON numbers in the signed 32-bit range, from −2,147,483,648 through 2,147,483,647. Floating-point values are rejected. Text is valid UTF-8 with exact, case-sensitive matching. The codec performs no normalization or case folding. Native OCaml predicates can process arbitrary bytes, but invalid UTF-8 has no representation in this query schema. Relationships, other models, native extension fields such as [`"pane_dead"`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_dead/), optional fields such as [`"pane_pid"`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_pid/), integer membership and Java's `"matches"` regex operator are outside this subset. Use native expressions or ordinary [`Seq.filter`]() for local behavior outside it. ## Bounds and errors Both decoding and encoding enforce these limits: | Resource | Limit | | --- | --- | | JSON document | 65,536 bytes | | JSON nesting | 32 levels | | Expression nodes | 1,024 | | String membership values | 256 | [`Libtmux_json.Expr.of_string`]() checks input bytes and nesting before parsing. [`Libtmux_json.Expr.decode`]() validates the supplied Yojson tree, including duplicate properties and the size of its compact JSON representation. Encoding applies the same subset and bounds. Failures return [`Decode_error.t`](): a JSON pointer, category and message. For example, a string supplied to [`"pane_width"`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_width/) reports [`Wrong_type`]() at `/expr/value`. The decoder returns the first detected error, not an aggregate validation report. It does not include the whole input in the error. ## Reproduce the compatibility check The [conformance procedure](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/test/conformance/README.md) pins the Java source, verifies prepared artifacts, rebuilds the OCaml peer and exchanges queries in both directions over the same fixture. The current 66 cases establish the tested scalar behavior. They do not imply support for the rest of Java's query language or a Java runtime dependency for OCaml users. [java-wire]: https://github.com/libtmux/libtmux-java/blob/e8a39689d8d60968ac978a1dd2a89c6112333896/docs/guide/filtering.md --- # Submit actions and retain their outcomes Source: https://libtmux.org/en/ocaml/latest/guides/control/ > Submit actions and retain their outcomes: libtmux documentation. Observations are local values. Client calls perform explicit I/O, and mutations use targets bound to the source that produced them. Closing a client releases its own transports. tmux then applies its configured lifetime: for example, [the standalone programs](https://libtmux.org/en/ocaml/latest/guides/showcases/) enable [`exit-unattached`](https://libtmux.org/en/tmux/latest/catalog/options/exit-unattached/) on their private daemons so they exit after their final client detaches. The [layout program](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/native_build.ml) creates a session, splits its first pane and adds a logs window. It passes argv through [`Spawn.Exec`](), a working directory and an environment value. [`Spawn.Shell`]() explicitly requests shell code. `send_text` is literal input; `send_keys [Key.Enter]` is a separate action. A creation response returns the reported session, window and pane IDs without an extra graph capture. Only the resource requested by that command is newly created. A split's returned session and window are pre-existing containers. ## Choose a backend Use [`Libtmux_unix.Client`]() in a blocking script. Use [`Libtmux_eio.Client`]() inside an application-owned Eio switch, supplying the process manager and monotonic clock. Clients are affine to their connecting domain. Immutable captured values can be read from other domains; live client handles cannot move between them. The Eio backend has client-owned workers for command draining and stream reading. A cancelled waiter does not abandon a written request. Local scratch filesystem work runs on a system thread. The blocking backend supports scoped cancellation tokens signalled from another domain or systhread. | Recovery concern | Eio | Blocking Unix | | --- | --- | --- | | Cancel one caller | Eio cancellation scope around `Client.await` | [`Cancellation.with_`]() token passed to `Client.await` | | Return after cancellation | The waiter raises while a client-owned fiber drains admitted work | The call drains before raising; one exchange timeout after detection bounds transport work | | Inspect an outcome | `Operation.inspect`, on the connecting domain | `Operation.inspect`, on the connecting domain | | Release resources | Close the client inside its application-owned switch | Close the client after the call returns | The blocking bound covers transport draining. Local scratch I/O must still finish before the call returns. Captures, reads and subscriptions share the client's payload budget and process slots. The [capacity guide](https://libtmux.org/en/ocaml/latest/guides/streaming/#plan-shared-capacity) shows their combined reservations and how to leave headroom for service operations. Eio `Client.close` rejects new command and subscription admission as soon as closing starts. Concurrent closes wait for the same teardown result. A later close retains an earlier teardown failure; the closed state alone does not prove resources were released. Inspect the client's `Cleanup` report for secondary failures. Internal connection teardown also retains one completion, so a concurrent failure path cannot skip cleanup already in progress. The [complete recovery example](https://libtmux.org/en/ocaml/latest/guides/recovery/) cancels an active waiter, awaits the same retained operation and checks server state before its next mutation. ## Distinguish acknowledgement from completion `submit` validates and reserves capacity without writing. Retain the operation handle before awaiting it. `Operation.inspect` reads its state without I/O: queued, not started, written, acknowledged, finished, or outcome unknown. A reply can acknowledge a command while hooks or its completion marker remain outstanding. Sending keys confirms tmux accepted input; it does not prove a terminal program consumed it or completed work. A detach request does not prove the peer exited. Use the relevant postcondition, rendered wait or external exit signal for the task. Expected failures use `result` and [`Error.t`](). Backend cancellation propagates as cancellation. An uncertain result retains reported creation IDs and unresolved owned buffers; it never becomes an instruction to retry the mutation. When Eio's daemon watcher closes a connection, an in-flight operation retains [`Error.Disconnected`]() as its cause. An idle-reader failure retains its library error. Explicit client closure uses [`Error.Closed`](). Inspect the retained receipt to distinguish these reasons; each can leave an admitted mutation uncertain. `Client.cleanup` retains process and descriptor cleanup diagnostics after closure. Supply a [`Cleanup.create ()`]() report through `~cleanup` before connecting when diagnostics must survive failed setup or cancellation. The same option is available on terminal attachment and workspace bootstrap. [`Cleanup.inspect`]() returns the cleanup phase, typed library error when available, original exception and backtrace without I/O. The report retains at most 64 entries and counts further issues; exception payloads can still retain substantial memory. Reports and their diagnostics belong to the creating domain. Cleanup attempts independent releases even when one fails. A primary exception keeps its identity and backtrace; secondary failures go to the report. If there is no primary failure, the first unrecovered cleanup failure is returned or raised normally. A recovered fallback can still leave a diagnostic after a successful return: failure to open a child pidfd skips TERM grace, forces termination and reaps, while retaining the [`Open_pidfd`]() issue. Failed forced termination produces an [`Unreaped_child`]() issue when exit cannot be established without blocking. That issue does not prove resource release. The [recovery example](https://libtmux.org/en/ocaml/latest/guides/recovery/) keeps its report outside connection lifetime. ## Recover deliberately A replacement daemon retires the connection generation. Reconnect explicitly, capture again and establish cardinality before choosing a fresh target. Old captured values remain readable, but their targets cannot silently move to the new server. Partial effects can remain after a later hook or command failure. Inspect the retained receipt and current server state. Compensate only resources whose creation is proven. A snapshot is an observation over an interval, and a selection followed by mutation is not an atomic transaction. Use the [recovery walkthrough](https://libtmux.org/en/ocaml/latest/guides/recovery/) to follow this sequence through a complete installed program, including cleanup of its own pane. See [streaming](https://libtmux.org/en/ocaml/latest/guides/streaming/) for Data/Gap/End and bounded queues, and [compatibility](https://libtmux.org/en/ocaml/latest/guides/compatibility/) for hook, PID-namespace and scratch-file constraints. [Guide](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/README.md) · [API](https://libtmux.org/en/ocaml/latest/reference/) --- # Recover an operation after cancelling its waiter Source: https://libtmux.org/en/ocaml/latest/guides/recovery/ > Recover an operation after cancelling its waiter: libtmux documentation. For a creation followed by a failing tmux hook, see [retained creation evidence](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/hook-recovery.md). That walkthrough inspects the reported IDs after closing its client. Retain the operation handle before awaiting it. Cancelling one Eio waiter leaves admitted work under the client's ownership. Inspect that handle, await its outcome and check the relevant server state before choosing another mutation. This program uses a tmux [`wait-for`](https://libtmux.org/en/tmux/latest/manual/wait-for/) barrier to keep an operation pending while its waiter is cancelled. It then signals the barrier once and awaits the same handle. A fresh capture checks the window's name before renaming it. The barrier coordinates this example; it does not establish completion of a terminal program. [Control](https://libtmux.org/en/ocaml/latest/guides/control/) compares Eio and blocking cancellation timing. ## Run the complete program Complete the [source installation](https://libtmux.org/en/ocaml/latest/guides/getting-started/) and run from the cloned repository. Start an empty private server: ```console $ export LIBTMUX_RECOVERY_DIR="$(mktemp -d -t libtmux-ocaml-recovery.XXXXXX)" ``` ```console $ env -u TMUX -u TMUX_PANE tmux \ -S "$LIBTMUX_RECOVERY_DIR/server" \ -f /dev/null \ new-session \ -d \ -s recovery-demo \ -- "exec cat" ``` The fresh server's session ID is `$0`; quote it to preserve the literal ID: ```console $ opam exec -- dune exec examples/native_recovery.exe -- \ tmux "$LIBTMUX_RECOVERY_DIR/server" '$0' ``` Expected output is `waiter=cancelled; operation=retained`, followed by `operation=finished; postcondition=recovery-complete` and `cleanup-issues=0; omitted=0`. The program creates a window and removes its own pane afterward. It preserves the seed session and other panes, and closes its client on success or failure. Remove the private seed session and directory: ```console $ env -u TMUX -u TMUX_PANE tmux \ -S "$LIBTMUX_RECOVERY_DIR/server" \ kill-session \ -t '=recovery-demo' ``` ```console $ rm -f -- "$LIBTMUX_RECOVERY_DIR/server" ``` ```console $ rmdir "$LIBTMUX_RECOVERY_DIR" ``` ## Inspect before acting The [complete source](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/native_recovery.ml) uses installed public libraries. Its Eio cancellation scope covers only the waiter. Client ownership and cleanup remain outside that scope. A caller-owned `Cleanup` report is created before connecting and inspected after closing. It remains accessible if setup fails or cancellation prevents a client from being returned. ```ocaml open Libtmux module Client = Libtmux_eio.Client module Operation = Libtmux_eio.Operation module Cleanup = Libtmux_eio.Cleanup let get = function | Ok value -> value | Error error -> failwith (Format.asprintf "%a" Error.pp error) let protect ~finally use = match use () with | value -> finally (); value | exception error -> let backtrace = Printexc.get_raw_backtrace () in (try finally () with cleanup -> Format.eprintf "cleanup failed: %s@." (Printexc.to_string cleanup)); Printexc.raise_with_backtrace error backtrace let cancel_waiter operation = let entered, enter = Eio.Promise.create () in let context = ref None in Eio.Fiber.pair (fun () -> try Eio.Cancel.sub (fun current -> context := Some current; Eio.Promise.resolve enter (); ignore (get (Client.await operation)); failwith "wait completed before its release signal") with Eio.Cancel.Cancelled Exit -> ()) (fun () -> Eio.Promise.await entered; Eio.Cancel.cancel (Option.get !context) Exit) |> ignore let recover client session = let created = get (Client.create_window client session ~name:"recovery-pending" ~spawn:(Spawn.make (Exec { program = "/bin/cat"; args = [ "-u" ] }))) in let channel = "libtmux-recovery-" ^ Generation.to_string (Source.generation (Client.source client)) in let pending = ref None and release_attempted = ref false in let release () = if not !release_attempted then ( release_attempted := true; get (Client.signal client channel)) in protect ~finally:(fun () -> Eio.Cancel.protect (fun () -> protect ~finally:(fun () -> get (Client.kill_pane client created.pane)) (fun () -> match !pending with | None -> () | Some operation -> ( match get (Operation.inspect operation) with | Operation.Queued -> get (Operation.cancel operation) | Operation.Written | Operation.Acknowledged -> release (); get (Client.await operation) | Operation.Not_started | Operation.Finished _ | Operation.Outcome_unknown _ -> ())))) (fun () -> let operation = get (Client.submit client (Operations.wait_for channel)) in pending := Some operation; ignore (get (Operation.await_stage operation `Acknowledged)); cancel_waiter operation; (match get (Operation.inspect operation) with | Operation.Written | Operation.Acknowledged -> () | Operation.Outcome_unknown receipt -> failwith (Format.asprintf "outcome unknown: %a" Error.pp receipt.cause) | _ -> failwith "expected a retained in-flight operation"); print_endline "waiter=cancelled; operation=retained"; release (); get (Client.await operation); (match get (Operation.inspect operation) with | Operation.Finished (Ok ()) -> () | _ -> failwith "retained operation did not finish"); let window = Snapshot.find_window (get (Client.capture client)) (Window.Target.id created.window) |> Option.get in if Window.name window <> "recovery-pending" then failwith "postcondition changed; leave the next action to the caller"; get (Client.rename_window client (Window.target window) ~name:"recovery-complete"); let after = Snapshot.find_window (get (Client.capture client)) (Window.id window) |> Option.get in assert (Window.name after = "recovery-complete"); Printf.printf "operation=finished; postcondition=%s\n" (Window.name after)) let () = if Array.length Sys.argv <> 4 then ( prerr_endline "usage: native_recovery TMUX_BINARY SOCKET_PATH SESSION_ID"; exit 2); Eio_main.run @@ fun env -> Eio.Switch.run @@ fun sw -> let cleanup = Cleanup.create () in protect ~finally:(fun () -> let report = get (Cleanup.inspect cleanup) in Iarray.iter (fun issue -> match issue.Cleanup.error with | Some error -> Format.eprintf "cleanup: %a@." Error.pp error | None -> Format.eprintf "cleanup: %s@." (Printexc.to_string issue.exception_)) report.issues; Printf.printf "cleanup-issues=%d; omitted=%d\n" (Iarray.length report.issues) report.omitted) (fun () -> let client = get (Client.connect ~cleanup ~sw ~binary:Sys.argv.(1) ~process_mgr:(Eio.Stdenv.process_mgr env) ~mono_clock:(Eio.Stdenv.mono_clock env) (Endpoint.socket_path Sys.argv.(2))) in protect ~finally:(fun () -> Eio.Cancel.protect (fun () -> get (Client.close client))) (fun () -> let id = get (Session.Id.of_string Sys.argv.(3)) in let session = Snapshot.find_session (get (Client.capture client)) id |> Option.get in recover client (Session.target session))) ``` The release attempt is recorded before its I/O so cleanup cannot retry an uncertain signal. The finally block cancels queued work, releases and drains a live barrier, and removes only the pane whose creation was reported. Existing panes remain outside that cleanup. A primary failure keeps its original exception and backtrace; a later cleanup failure is reported separately. Both clients and operations stay on the connecting domain. ## Interpret a retained state [`Libtmux_eio.Operation.inspect`](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/eio/libtmux_eio.mli) performs no I/O and remains readable after client closure. A state describes this operation; it does not prove a terminal program consumed input or exited. | State | Next decision | | --- | --- | | `Queued` | Cancel before writing, or continue awaiting the same handle. | | `Not_started` | The operation made no admitted effect; establish fresh preconditions before a new action. | | `Written` or `Acknowledged` | Keep the handle. The client still owns completion; cancelling another waiter does not undo the write. | | `Finished (Ok value)` | Use the result and check the task's relevant postcondition. | | `Finished (Error error)` | Examine the error and current state; a later command or hook failure can follow an earlier effect. | | `Outcome_unknown receipt` | Preserve the cause and reported creations. Reacquire observations explicitly and resolve uncertainty before another mutation. | The example exercises active-waiter cancellation and retained completion. Its unknown-outcome branch reports the cause and stops the next action. Transport loss, a replacement daemon and uncertain mutation recovery require the retained receipt and fresh observation; this program does not simulate those failures. Closing a client does not terminate a borrowed tmux daemon. For workspace automation, inspect the [execution receipt](https://libtmux.org/en/ocaml/latest/workspace/source-guide/#collisions-and-recovery) and compensate only proven creations. For stream continuity loss, handle the [Gap or End event](https://libtmux.org/en/ocaml/latest/guides/streaming/) before choosing a fresh subscription. [First use](https://libtmux.org/en/ocaml/latest/guides/getting-started/) · [Control](https://libtmux.org/en/ocaml/latest/guides/control/) · [Guide](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/README.md) --- # Observe output and wait for rendered text Source: https://libtmux.org/en/ocaml/latest/guides/streaming/ > Observe output and wait for rendered text: libtmux documentation. Use [native_watch.ml](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/native_watch.ml) for a complete concurrent literal-send, future-byte match and rendered-screen check. Its application owns the Eio scheduler and switch. Use [native_wait.ml](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/native_wait.ml) to capture exactly one pane and wait until its ordinary screen contains the supplied text. The example imports the installed Eio client, owns its switch, uses an explicit socket, closes its client and prints the matched pane ID. It requires an existing daemon and one pane; absence and ambiguity remain errors. `Client.wait_for_text` first subscribes, then captures the screen immediately. Output invalidates that observation. Queued Data events are coalesced before the next bounded capture; a queued Gap stops that coalescing. Membership and overflow gaps cause fresh graph acquisition and subscription in the same connection binding. A disconnected daemon or stream fails the wait. Success returns a captured screen containing the text. Text that appears and disappears between captures may be missed. Deadlines are checked between acquisitions, whose in-flight I/O keeps the client's exchange timeout. A timed out wait does not prove that text was absent in the past. The wait releases its temporary subscription on every exit. An earlier error or cancellation remains the primary result; `Client.cleanup` retains secondary cleanup failures. If cleanup fails after a match or during gap recovery, the wait fails instead of reporting success or opening another subscription. Use `Client.subscribe` and `Subscription.next` for future output bytes. Data retains every byte, including NUL and invalid UTF-8. Its cursor records a local subscription generation and an observed byte offset. A cursor is neither a snapshot timestamp nor a server-side history position. `poll` only removes a queued event and performs no I/O. `Subscription.wait_for_bytes` consumes those future events and matches across event boundaries. Its nonempty pattern is limited to 64 KiB. It returns the match's end cursor, possibly inside the last consumed Data event. A deadline leaves the subscription active. A Gap returns `Output.Ended reason`; later matching requires an explicit fresh capture and new subscription. The defaults are 1 MiB and 1024 queued events per subscriber, at most 16 active subscribers, and eight total client lanes including command/wait lanes. Reuse one verified lane per covered session. Admission reserves bytes against the shared 32 MiB budget. A full queue produces Gap then End rather than blocking another subscriber or an independent command. Unknown loss has no byte count. These limits apply together; [plan shared capacity](https://libtmux.org/en/ocaml/latest/guides/streaming/#plan-shared-capacity) before combining many observers with captures and reads. Coverage uses an observed session/window context and revalidates it during admission. Layout or membership changes end continuity conservatively. Detaching a stream leaves the primary client usable; a daemon replacement retires the whole binding. New coverage always has a new subscription generation. Eio readers belong to the client, use no application callbacks and keep running when a subscriber stalls. The blocking backend advances all stream lanes during `Subscription.next`. Cancellation closes the affected subscription and propagates through the backend's cancellation mechanism. Client close releases all owned lanes and retains terminal events; it leaves the daemon alive. When a subscription releases the last user of a lane, close waits for its owned helper to be reaped. Concurrent Eio subscription closes join that same cleanup. Repeated closes preserve the first terminal reason and any cleanup failure; unconsumed terminal events remain readable through `Subscription.poll`. The [recovery program](https://libtmux.org/en/ocaml/latest/guides/recovery/) shows how to cancel one operation waiter while keeping its retained outcome available. Cancelling a subscription closes that subscription; it does not cancel an already admitted command. Neither interface establishes an atomic capture/stream barrier or reconstructs terminal history. An immutable snapshot remains a separate captured value. ## Plan shared capacity Every client has its own bounded payload budget and process-slot pool. The blocking and Eio clients use the same accounting. An individually valid queue or command can still return [`Error.Capacity`]() when another reservation consumes the available headroom: | Resource | Current limit or reservation | | --- | --- | | Aggregate client payload | 32 MiB shared by queues, commands and operation scratch space | | Admitted commands | 64; encoded payload accounting remains charged until completion or cancellation before writing | | Admitted command bytes | 1 MiB shared across admitted operations; submission also temporarily reserves 2 MiB | | Client process slots | 8, including the primary control client; each auxiliary slot reserves 128 KiB | | Subscriptions | 16 active per client, sharing a verified stream lane per covered session | | Subscriber queue | 1 MiB / 1024 events by default; reserve the full configured byte capacity even when empty | | Graph or rendered capture | 16 MiB operation scratch reservation plus an 8 MiB control exchange | | Option, environment or hook read | 16 MiB scratch reservation plus a temporary process slot | The payload budget tracks library reservations. Caller-retained snapshots, completed operation handles, receipts and exception payloads have their own lifetimes, alongside runtime overhead, child-process memory and tmux's allocations. See [observation retention](https://libtmux.org/en/ocaml/latest/guides/graph/). For example, sixteen default queues on one covered session reserve 16 MiB plus that session's auxiliary slot. A subsequent capture or read cannot obtain its scratch reservation. Even eight default queues leave insufficient room for the capture's nested control exchange. Closing subscriptions releases their reservations; the client remains usable after a capacity error. | Empty queues, one covered session | Capture | Option read | | --- | --- | --- | | 16 × 1 MiB | [`Capacity`]() | [`Capacity`]() | | 8 × 1 MiB | [`Capacity`]() | Fits the reservation budget | | 7 × 1 MiB | Fits the reservation budget | Fits the reservation budget | | 16 × 256 KiB | Fits the reservation budget | Fits the reservation budget | These examples describe reservation accounting on a fixed one-pane graph. Additional lanes, admitted commands and concurrent work consume more headroom. A budget that fits still needs valid targets, available data and a responsive daemon. The [complete watcher](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/native_watch.ml) chooses its queue explicitly: ```ocaml let subscription = get (Client.subscribe ~capacity_bytes:(256 * 1024) ~capacity_events:256 client pane) ``` Its full program includes the client, pane acquisition, result handling, Eio switch and cleanup. Choose a capacity for the burst your application can consume; a smaller queue reaches `Gap Overflow` sooner when its reader stalls. Slots need their own headroom. The primary client and seven distinct covered sessions fill all eight slots, leaving none for a read process or `wait_for`. If an application needs those operations, it can cap distinct covered sessions at six while keeping one slot for service, accounting for any waits already using slots. The library currently reserves no slot exclusively for service. Several panes in the same session can share its stream lane. Reducing queue capacities does not free process slots. Closing the last active subscriber for a covered session releases its lane after owned-helper cleanup. A separate client has its own budget and slots, but also its own source generation: acquire observations on that client before using its targets. ## Recover after a gap Close the ended subscription, capture the graph again, and establish fresh coverage for the observed pane. This recovers future observation. Bytes lost before the new subscription are still unavailable; a screen capture cannot reconstruct their history. The [complete recovery program](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/native_stream_recovery.ml) creates a window running `cat`, observes a reply, then splits the window to trigger a membership gap. It handles that gap, removes the extra pane and closes the old subscription. It captures the original pane again, subscribes, and observes new input with a different cursor generation: ```ocaml let recover_coverage client id (previous : Output.cursor) = let fresh = capture_pane client id in let after = with_subscription client fresh (fun subscription -> echo client fresh subscription "after-gap") in if Generation.equal previous.generation after.generation then failwith "new subscription reused the old cursor generation"; print_endline "coverage=fresh; cursor-generation=new; future=after-gap" ``` The source contains the imports, scheduler, cleanup and matching helpers. `capture_pane` fails if the pane disappeared. `with_subscription` admits coverage before `echo` sends input and awaits its reply concurrently. The example never reconnects, reuses an old cursor or retries an uncertain mutation. It removes only panes whose creation returned successfully, leaving any foreign panes untouched. Primary exceptions survive secondary cleanup failures. Creation here uses convenience calls. Cancellation before one returns can leave a creation whose ID the caller never received. For creation outcomes that must survive waiter cancellation, retain `Client.submit`'s handle before `Client.await`, as shown in [operation recovery](https://libtmux.org/en/ocaml/latest/guides/recovery/). An uncertain outcome does not authorize a retry or deletion of an unproven resource. After [installing from source](https://libtmux.org/en/ocaml/latest/guides/getting-started/), run this walkthrough from the clone. Create a private seed session: ```console $ export LIBTMUX_STREAM_RECOVERY_DIR="$(mktemp -d -t libtmux-ocaml-stream-recovery.XXXXXX)" ``` ```console $ env -u TMUX -u TMUX_PANE tmux \ -S "$LIBTMUX_STREAM_RECOVERY_DIR/server" \ -f /dev/null \ new-session \ -d \ -s stream-demo \ -- "exec cat" ``` Run the program against the seed session's literal `$0` ID: ```console $ opam exec -- dune exec examples/native_stream_recovery.exe -- \ tmux "$LIBTMUX_STREAM_RECOVERY_DIR/server" '$0' ``` Expected output is `gap=membership-changed; history=unavailable`, followed by `coverage=fresh; cursor-generation=new; future=after-gap`. The seed session remains, with no additional panes or clients. Remove that session and directory: ```console $ env -u TMUX -u TMUX_PANE tmux \ -S "$LIBTMUX_STREAM_RECOVERY_DIR/server" \ kill-session \ -t '=stream-demo' ``` ```console $ rm -f -- "$LIBTMUX_STREAM_RECOVERY_DIR/server" ``` ```console $ rmdir "$LIBTMUX_STREAM_RECOVERY_DIR" ``` This exercise uses a stable private daemon and a deliberate layout change. Transport loss, repeated membership changes or a missing pane can prevent fresh coverage. Preserve those failures and choose the next action explicitly. [First use](https://libtmux.org/en/ocaml/latest/guides/getting-started/) · [Control and recovery](https://libtmux.org/en/ocaml/latest/guides/control/) · [API](https://libtmux.org/en/ocaml/latest/reference/) · [Guide](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/README.md) --- # Run a complete capability example Source: https://libtmux.org/en/ocaml/latest/guides/showcases/ > Run a complete capability example: libtmux documentation. Each program creates a private tmux workspace, performs one task, verifies the result and closes its clients. Copy the whole file into a Dune project or run it from this checkout after [installation](https://libtmux.org/en/ocaml/latest/guides/getting-started/). The programs use a minimal tmux configuration that makes their private daemon exit when its last client detaches. They accept an optional tmux binary, not a borrowed server socket. Their setup uses the public workspace API; the task uses the client API. The Eio example starts its scheduler after setup and subscribes before sending the bytes it observes. For a separate project, save a program as `main.ml` and use these Dune libraries: ```dune (executable (name main) (libraries libtmux libtmux-workspace libtmux-unix unix)) ``` The watch program also needs `libtmux-eio` and `eio_main`. The [installation guide](https://libtmux.org/en/ocaml/latest/guides/getting-started/) supplies the project and switch setup. | Task | Complete program | Expected outcome line | | --- | --- | --- | | Start a private server and its first session | [Bootstrap](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/showcase_bootstrap.ml) | `bootstrapped: 1 session, 1 window, 1 pane` | | Navigate sessions, shared windows and panes | [Graph](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/showcase_graph.ml) | `graph: 2 sessions, 2 windows, 3 links, 2 panes` | | Observe future bytes concurrently | [Watch](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/showcase_watch.ml) | `observed: changed` | | Send literal input and Enter | [Keys](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/showcase_send_keys.ml) | [`sent: literal: $HOME; #{pane_id}`](https://libtmux.org/en/tmux/latest/catalog/formats/pane_id/) | | Rename a window and retain the old observation | [Rename](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/showcase_rename_window.ml) | `renamed: editor-main` | | Create a window and recapture its name | [Create](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/showcase_create_window.ml) | `created: logs` | | Move a window to another session's index | [Move](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/showcase_move_window.ml) | `moved: build at 7` | | Apply and verify horizontal pane geometry | [Layout](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/showcase_layout.ml) | `layout: 3 horizontal panes` | All programs print their temporary socket path before the outcome. The [bootstrap walkthrough](https://libtmux.org/en/ocaml/latest/guides/bootstrap/) explains the missing-endpoint precondition and ownership of the resulting client: ```console $ opam exec -- dune exec examples/showcase_bootstrap.exe -- tmux ``` Run the graph example; it traverses the snapshot after the client and daemon close: ```console $ opam exec -- dune exec examples/showcase_graph.exe -- tmux ``` Observe future output with an application-owned Eio switch: ```console $ opam exec -- dune exec examples/showcase_watch.exe -- tmux ``` Send a literal string containing shell and tmux format characters: ```console $ opam exec -- dune exec examples/showcase_send_keys.exe -- tmux ``` Rename the editor window: ```console $ opam exec -- dune exec examples/showcase_rename_window.exe -- tmux ``` Create a separate logs window: ```console $ opam exec -- dune exec examples/showcase_create_window.exe -- tmux ``` Move a build window to index 7 in another session: ```console $ opam exec -- dune exec examples/showcase_move_window.exe -- tmux ``` Split a window and apply an even horizontal layout: ```console $ opam exec -- dune exec examples/showcase_layout.exe -- tmux ``` These examples support tmux 3.3a–3.8 on the qualified Linux environment. The native CI job executes their real entry points on 3.3a and 3.8, using a separate installation and Dune project. A nonzero exit, missing exact outcome line, leftover directory or surviving owned process fails the run. [`showcase.json`](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/showcase.json) records the whole files, commands, expected lines, tmux floor, workflow, job and runner. Sending keys confirms tmux accepted input. The rendered-text example observes the resulting screen; it does not wait for an arbitrary application's exit code. A future-byte subscription has no historical capture barrier. See [control outcomes](https://libtmux.org/en/ocaml/latest/guides/control/) and [streaming](https://libtmux.org/en/ocaml/latest/guides/streaming/) for those contracts. --- # Choose the packages your application needs Source: https://libtmux.org/en/ocaml/latest/guides/packages/ > Choose the packages your application needs: libtmux documentation. This repository is one Dune project with separately installable opam packages. Each package has an authored manifest. Internal dependencies require the same package version, so a client or workspace adapter uses its matching core. The core and pure workspace library have no third-party runtime dependencies. They use OCaml 5.5.2's standard library, including [`Iarray`](), [`Seq`](), [`List`]() and `result`. Test and documentation tools remain development dependencies. | Package | Role | Runtime dependencies | | --- | --- | --- | | `libtmux` | Immutable graph, typed queries, commands and observation values | OCaml standard library | | `libtmux-workspace` | Pure configuration validation, plans and execution journals | `libtmux` | | `libtmux-unix` | Blocking Linux transport and workspace executor | Core, workspace, standard [`Unix`](), private Linux bindings | | `libtmux-eio` | Concurrent transport and workspace executor | Core, workspace, blocking transport, Eio | | `libtmux-json` | Validated query and workspace JSON codecs | Core, workspace, Yojson | | `libtmux-workspace-cli` | Installed `tmux-workspace` command | The libraries above, Eio's entry point and Cmdliner | Pure workspace planning depends on captured observations and an explicit tmux version. It does not start a scheduler, read files or contact tmux. Execution uses the adapter supplied by [`Libtmux_unix.Workspace`]() or [`Libtmux_eio.Workspace`](). The CLI combines file loading, JSON decoding, planning and the Eio executor. An application querying a captured graph needs only `libtmux`. A blocking script chooses `libtmux-unix`; an Eio application chooses `libtmux-eio` and owns its scheduler and switch. Add `libtmux-json` for external query documents or JSON workspaces. These integrations do not add dependencies to the pure core. See [installation](https://libtmux.org/en/ocaml/latest/guides/getting-started/), [workspace examples](https://libtmux.org/en/ocaml/latest/workspace/source-guide/) and [public interfaces](https://libtmux.org/en/ocaml/latest/reference/). The source-install walkthrough installs all current packages; a consuming Dune project lists only the libraries it uses. ## Scope policy Prefer standard-library types and ordinary functions. Keep transport, JSON and CLI dependencies at their integration boundaries. Use the current stable OCaml compiler in 2026; older-compiler shims and compatibility matrices are excluded. MCP integration is outside this port's scope. The project does not ship or vendor an MCP SDK or server. Native OCaml APIs and the workspace CLI are its automation interfaces; MCP discovery and conformance are excluded from the project's adoption and completion criteria. --- # Compatibility Source: https://libtmux.org/en/ocaml/latest/guides/compatibility/ > Compatibility: libtmux documentation. The compiler target is **OCaml 5.5.2**, exactly pinned. Older compilers are outside this implementation's scope. The development tools are Dune 3.24.2 and opam 2.6.1. The implementation supports Linux and **tmux 3.3a–3.8**, including every intermediate release. CI tests only the two endpoints, 3.3a and 3.8. Option and format catalogs cover the entire range. Current evidence covers specific components; final suite qualification has a separate acceptance gate. The table records the exercised fixtures, not every possible cleanup path. | Component | 3.3a | 3.8 | | --- | --- | --- | | Owned fixture teardown | PASS | PASS | | Exact Boolean pane filters | PASS | PASS | | Argument and metadata byte codecs | PASS | PASS | | Split control framing and command groups | PASS | PASS | | Bound graph capture and ordinary client teardown | PASS | PASS | | Replacement-daemon rejection | PASS | PASS | | Private-file capture and failed-save callbacks | PASS | PASS | | Eio waiter cancellation and retained outcomes | PASS | PASS | | Blocking cancellation and bounded drain | PASS | PASS | | Command and lane admission limits | PASS | PASS | | Environment bytes, scopes and read-process lifecycle | PASS | PASS | | Scoped options and built-in hook execution | PASS | PASS | | Current client names and detach-request semantics | PASS | PASS | | Interactive attachment, cancellation and child exit | PASS | PASS | | Versioned format availability and generated APIs | PASS | PASS | | Native operation inventory | PASS | PASS | | Data/Gap/End, stream limits and recovery | PASS | PASS | | Installed stream-gap walkthrough | FAIL | FAIL | | Rendered waits and future-byte matching | PASS | PASS | | Blocking/Eio workspace preconditions and compensation | PASS | PASS | | Public workspace bootstrap on fresh/existing daemons | PASS | PASS | | Installed workspace CLI, receipts, interruption and output faults | PASS | PASS | | Literal workspace guide and owned teardown | PASS | PASS | | Native workspace interruption caller and guide | PARTIAL | PASS | | Final suite qualification | UNVERIFIED | UNVERIFIED | The [stream-gap walkthrough](https://libtmux.org/en/ocaml/latest/guides/streaming/#recover-after-a-gap) reported an unknown operation outcome after its recovery output on tmux 3.3a in [hosted run 42516374421](https://github.com/libtmux/libtmux-ocaml/actions/runs/42516374421). A bounded diagnostic reproduced the failure with the owned daemon no longer alive. Its exit cause remains unresolved. Earlier component passes do not establish that the affected workload is safe. On tmux 3.8, the same walkthrough exited with status 2 in [hosted run 42533145236](https://github.com/libtmux/libtmux-ocaml/actions/runs/42533145236). Its harness captured but did not include the program's output in the raised exception, so the failure's cause is unestablished. That run passed the ordinary native component suite before failing the installed example. Keep this result separate from the earlier 3.3a daemon-exit evidence; it does not prove the same cause. The example runner now includes captured output and the original cause when a program fails or times out. Later passing runs do not close either finding. The [workspace interruption caller](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/workspace-recovery.md) passes installed compensation and foreign-link preservation checks on both endpoints. Its literal guide also passes. An earlier tmux 3.3a consumer-harness readiness command exited with status 1 before releasing the creation hook. That failure did not retain the command's captured output, so its cause remains unestablished. The current runner retains command, consumer and cleanup diagnostics. Later passing cases do not close the earlier readiness finding; the 3.3a cell remains partial. Installed checks verify compensated pane exit, seed topology and client counts. The foreign-link control renames the created session and requires the ID-based postcondition to report failure while preserving foreign content. The current matrix uses only OCaml 5.5.2 and tmux 3.3a/3.8. Pure assertions check version rejection and catalog availability across the supported range. Intermediate releases remain supported without separate runtime CI cells; earlier runtime results remain evidence for their tested workloads. Development tmux 3.8a does not replace the published 3.8 cell. PID-acquisition races and malformed replies after writing pass deterministic fault checks with real Linux child processes. Cancellation at each capture transfer step and unresolved-buffer receipts also pass on the recorded release fixtures. Ready I/O still checks its deadline; busy pipes cannot bypass the timeout. These results remain evidence for those tested cases. MCP integration is outside this port's scope. Owned-child cleanup sends TERM, allows a 200 ms cooperative grace, then sends KILL if necessary and reaps the child. The policy covers control, read-process and terminal helpers; it never sends these signals to the daemon. Eio waits allow other fibers to progress. The grace applies per helper, not to the whole client, and cannot bound uninterruptible kernel work or scratch-file completion. Caller-owned `Cleanup` reports retain secondary process and descriptor failures without replacing native cancellation or the primary exception. Independent releases are still attempted. A failed KILL only permits a nonblocking exit check; an unconfirmed exit is reported as [`Unreaped_child`](). Concurrent Eio client closes await one retained teardown result. The Eio runtime owns its pipe ends explicitly and joins their readiness waiters before closing them. Process I/O rechecks closure after yielding, preventing access to reused descriptors. A private Eio process owner keeps its reaper alive through cancellation and switch release; callers join its completed teardown. Shutdown regressions cover both Eio's Linux and POSIX backends on Linux. This does not add platform support. New commands and subscriptions are rejected as soon as client close starts. Connection and subscription teardown also retain one completion: overlapping Eio callers join it, and later calls preserve its failure. The shutdown regression checks stopped helpers and helpers that survive TERM and input closure, while keeping the parent and its Eio switch alive to verify reaping. These are injected helper failures, not naturally reproduced hangs in a supported tmux daemon. Blocking readiness uses `poll`; ready reads, ready writes and deadlines work above descriptor 1100. Terminal attachment and cancellation also have high-descriptor evidence on the previously tested 3.3/3.3a/3.4/3.8 fixtures. See the [architecture's cleanup boundary](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/dev/adr/0001-native-ocaml-suite.md#share-protocol-semantics-while-preserving-each-runtimes-cancellation). The near-limit [mixed workload](https://libtmux.org/en/ocaml/latest/guides/benchmarking/#mixed-commands-stream-pressure-and-membership-changes) reproduces a tmux 3.4 daemon crash in the control-client detach notification path. The exact 3.8 source includes the upstream null-state guard. An earlier 3.3 observer-command failure remains unattributed. Passing ordinary client checks does not close either mixed-workload failure. The I/O packages require Linux pidfds, the same PID namespace as the daemon, and shared local scratch storage. A socket pathname does not prove those conditions. Each connection has its own generation; a replacement daemon never inherits old targets. Closing a client releases its own processes and leaves the daemon running. The blocking client accepts a scoped [`Cancellation`]() token that another domain or systhread can cancel. It drains admitted work before raising [`Cancellation.Cancelled`](). The drain has at most one exchange timeout from detection; subsequent owned-child cleanup has its separate cooperative grace. The operation handle retains success, failure or uncertainty. Eio cancellation releases the waiter immediately while a client-owned fiber drains the work. Local scratch I/O must finish in either case; Eio performs it on a system thread. Filesystem failures have a distinct error from transport disconnects. Pane PIDs and client terminal heights can be absent. The model exposes these as options; a control client without a started terminal has no reported height. tmux itself sanitizes names on older releases and rejects some names on newer releases. Captures preserve the observed value rather than reconstructing the argument originally submitted by another client. Control commands use ASCII escapes for non-ASCII bytes because tmux 3.3's command-string lexer can confuse a signed `0xff` byte with EOF. Metadata uses tmux's `q` format modifier and preserves physical newlines. A fixed read probe detects the additional visual escaping used by tmux 3.4 before decoding it. Control clients must request UTF-8 mode so locale-dependent sanitization cannot alter captured bytes. An end guard acknowledges a command. Completion requires the operation's postcondition or a following marker on its lane. A control client may exit with status 1 after an earlier command failed, even when later commands and shutdown succeeded. Tests retain that distinction. The optional JSON codec supports the scalar pane registry and Boolean nodes from Java's [`libtmux.filter/1`](https://libtmux.org/en/ocaml/latest/guides/query-interchange/) at revision `e8a39689d8d60968ac978a1dd2a89c6112333896`. Both implementations agree on 66 captured-value cases and read each other's encoded queries. Numeric operands use signed 32-bit bounds. Regex, relationship documents, numeric membership and native extension fields remain unsupported in this wire schema. The [conformance procedure](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/test/conformance/README.md) rebuilds the peers from identified inputs and retains both codec directions and artifact digests. It is a separate qualification command, not part of ordinary hosted OCaml CI. Reproduce native checks with the commands in [CONTRIBUTING.md](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/.github/CONTRIBUTING.md). The fixture builders use official release archives and retain archive and binary digests under `scratch/tmux/`. Environment reads use a bounded read-only process on the same daemon. The client checks its reported PID and the original pidfd after collecting the complete response. Values may contain control-marker text without entering the control parser. Replacement, malformed data, truncated output and capacity failures never become a successful empty table. This route does not execute mutations or reconnect the primary lane. Scoped options preserve scalar types, sparse array indexes and local versus inherited entries. Bulk inherited enumeration follows tmux and includes only built-ins; `read_option` resolves a named inherited user option. Descriptors are recorded for every supported release in the [option catalog](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/options.md). tmux fills the lowest unused indexes when appending array elements. Hook commands keep names and arguments separate. Built-in hook arrays report synchronous failures; earlier effects remain after a later command fails. Named `@` hooks can be configured, read and removed, but `run_hook` rejects them because tmux may hide parse failures in the server log. Hooks that print arbitrary output remain outside the supported control transport contract. Captured client names belong to a connection generation but may be reused within it. Operations address the client currently bearing that name. A detach request confirms command processing, not peer exit; callers need separate exit evidence. Switching an existing client does not create a new terminal. `Terminal.attach_current` opens an interactive terminal against an explicit endpoint and session ID. This separate utility is unbound: an ID can refer to a different daemon if the endpoint changes. It accepts no captured target and never retries or starts a daemon. Both backends wait for the terminal client to exit and reap it on cancellation. Supplied terminal streams remain caller-owned. The generated [format catalog](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/formats.md) records exact-release availability and callback contexts. Local [`Formats.lookup`]() distinguishes captured values, missing values, unacquired fields and fields unsupported by that release. It does not perform arbitrary format acquisition. Generated [operation conveniences](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/operations.md) share the same command constructors between Unix and Eio; the installed-consumer test exercises both adapters. --- # Testing and CI Source: https://libtmux.org/en/ocaml/latest/guides/testing/ > Testing and CI: libtmux documentation. Use Alcotest for typed values and lifecycle invariants, `ppx_expect` for readable output contracts, and QCheck for generated semantic cases. All three run through Dune. The pure runtime library uses the standard library; the PPX is a test-only dependency. The [cardinality tests](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/test/core_test.ml) compare typed results using an [`Alcotest.testable`](https://mirage.github.io/alcotest/alcotest/Alcotest/index.html#type-testable) with [`Cardinality.pp_error`](). A failure displays the expected and actual values, including the error variant's meaning. Comparing a Boolean expression would only report `true` versus `false`. Keep testable printers small and show the values needed to understand the failure. The [expect tests](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/test/diagnostics_expect.ml) place expected diagnostic text beside the code that prints it. A mismatch produces a diff and a `.corrected` file. Inspect the changed meaning before promoting an expectation. Stable query explanations, error messages and CLI output fit this style. Avoid snapshotting timestamps, random IDs or whole server captures to test a small invariant. [QCheck cases](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/test/query_test.ml) compare typed-expression evaluation against native predicates with generated inputs. A fixed seed makes the current suite reproducible; shrinking helps explain counterexamples. Properties supplement the small authored edge cases and live tmux checks. Run the unit, expect, property and compile-failure suite: ```console $ opam exec -- dune runtest --force ``` Cold PPX driver compilation belongs to setup or an outer build. Time the warm test command separately and retain the existing inner/mid budgets. Native and installed-consumer checks belong to the outer loop because they launch processes and own real tmux servers. Run the implemented native contract suite against one explicit release binary: ```console $ opam exec -- python3 test/native_suite.py /path/to/tmux ``` The [CI workflow](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/.github/workflows/ocaml.yml) uses exactly OCaml 5.5.2, Alcotest 1.9.1, `ppx_expect` v0.17.3 and QCheck 0.91. It checks authored manifests, builds/odoc, generated files, formatting, unit/expect/property cases, and two native release cells: tmux 3.3a (oldest supported) and 3.8 (newest stable). Every intermediate release remains supported. Pure assertions check version boundaries and catalog availability throughout the range. Each native cell installs the packages into a temporary prefix and runs a consumer outside the checkout. Consumer features still in development need their own later acceptance gates. The [Java/OCaml conformance procedure](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/test/conformance/README.md) prepares a pinned Java peer separately, verifies artifact hashes and runs both evaluators and codec directions. Its fixtures and failure diagnostics are retained per run. This qualification is outside ordinary OCaml CI and the fast test loops. The CLI document check exercises regular files and piped input on both Eio backends, including the inclusive 64 KiB boundary. Pass `--directory` to exercise a mounted filesystem using an owned temporary directory: ```console $ opam exec -- python3 test/workspace_document_test.py ``` `ocaml/setup-ocaml` caches opam and the switch. The tmux composite action caches only an installed prefix, keyed by source, runner image/version, architecture, compiler, package versions and build/probe recipes. Dependencies are installed before validating either hits or misses. A restored binary must match its hash, report the exact release and pass an owned-daemon probe with verified teardown. Rejected caches rebuild; no broad restore key reuses a binary from another environment. Save a validated installation before running library tests. The cache design adapts the [Go port's pinned implementation](https://github.com/libtmux/libtmux-go/blob/d8ea6f77ede9f75e96af78719c8c1707fc47bdfb/.github/scripts/tmux_build.py). The APIs and testing styles follow the primary documentation for [setup-ocaml](https://github.com/ocaml/setup-ocaml/tree/ff66428f72b64472e749b22e0ad4dded4b2ddca6), [Alcotest](https://github.com/mirage/alcotest/tree/1.9.1), [ppx_expect](https://github.com/janestreet/ppx_expect/tree/v0.17.3), and [Dune tests](https://dune.readthedocs.io/en/stable/tests.html). ## Executed documentation README and guide snippets name regions in the compiled programs under [`examples/`](); JSON includes use the complete shipped configuration. The source is authoritative; this check rejects drift: ```console $ python3 tools/docs_snippets.py ``` The installed-consumer test installs libraries into a separate prefix, builds every shipped example in an independent Dune project and runs it against owned tmux servers. It checks linked-window contexts, typed/native query results, created layout geometry, options, pane titles, working directories, literal input, future-byte matches, rendered-screen waits and zero remaining clients. Owned fixture teardown verifies pane and daemon exit. The [standalone capability programs](https://libtmux.org/en/ocaml/latest/guides/showcases/) include their own private server setup. CI copies their whole source files into an independent Dune project and executes the recorded `opam exec -- dune exec` commands against installed packages. Exact outcome lines and owned process/directory teardown are checked. The [inventory](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/showcase.json) records each source and command with its native workflow/job and tmux floor. Each native CI cell also runs the [workspace guide](https://libtmux.org/en/ocaml/latest/workspace/source-guide/) through real `opam exec` and an installed `tmux-workspace`. It executes the displayed setup, validation, plan, apply and cleanup commands, then checks receipt fields, pane directories, layout output, client closure and owned process exit. CLI fault tests pin their output pipe capacity so a larger host pipe cannot turn a stalled-consumer test into successful buffered delivery. These are correctness checks and whole-command timings, not throughput benchmarks. Do not derive performance comparisons from their durations. [Guide](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/README.md) · [API](https://libtmux.org/en/ocaml/latest/reference/) · [Compatibility](https://libtmux.org/en/ocaml/latest/guides/compatibility/) --- # Measure capture, concurrency and output delivery Source: https://libtmux.org/en/ocaml/latest/guides/benchmarking/ > Measure capture, concurrency and output delivery: libtmux documentation. The [capture benchmark](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/capture.py) compares serial blocking and Eio capture on one owned graph: one session, four windows and sixteen panes. Each sample captures the graph 100 times by default and selects active panes using both a compiled expression and a native predicate over each same snapshot. The harness checks exact ordered IDs across captures and modes, one observed tmux client and one child process per mode, and zero attached clients afterward. Owned fixture teardown verifies pane and daemon exit. Both modes reuse one bound connection for their captures; they perform no per-object acquisition. ## Reproduce Prepare the [exact release fixture](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/.github/CONTRIBUTING.md#owned-tmux-fixtures), then build the benchmark outside normal test loops: ```console $ opam exec -- dune build bench/capture.exe ``` Run it with Python 3.11 or newer. Choose a new output file each time: ```console $ opam exec -- python3 bench/capture.py scratch/tmux/tmux-3.8/tmux \ --iterations 100 \ --repeat 5 \ --output scratch/capture-baseline.json ``` The output records compiler and tmux versions, binary and behavior-source hashes, OS/architecture, CPU availability, starting load average, whole-command time, per-sample process time, client and daemon CPU usage, and OCaml heap allocation during the capture loop. Samples include program startup, binding, the capture loop and client closure. Fixture creation and compilation are outside the per-sample interval. Mode order alternates across repetitions. ## Capture and graph size These observations use OCaml 5.5.2 on one shared Linux host. Each table entry is median milliseconds for 50 captures, followed by its min–max across three samples. The raw files retain the source commit, behavior hash and host load. | tmux | Blocking, ms | Eio, ms | Evidence | | --- | --- | --- | --- | | 3.3 | 49.7 (49.0–52.6) | 54.6 (53.9–55.2) | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/capture-3.3-4x4.json) | | 3.3a | 76.3 (76.3–76.3) | 86.6 (72.1–90.1) | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/capture-3.3a-4x4.json) | | 3.4 | 74.1 (72.6–76.0) | 75.8 (61.8–76.9) | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/capture-3.4-4x4.json) | | 3.8 | 52.0 (48.7–66.6) | 67.1 (52.4–69.0) | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/capture-3.8-4x4.json) | Change `--windows` and `--panes-per-window` to measure larger graphs. The same tmux 3.8 workload, with 50 captures and three samples per mode, produced: | Windows × panes per window | Blocking median, ms | Eio median, ms | Evidence | | --- | --- | --- | --- | | 1 × 1 | 28.5 | 28.0 | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/capture-3.8-1x1.json) | | 4 × 4 | 52.0 | 67.1 | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/capture-3.8-4x4.json) | | 8 × 8 | 113.8 | 114.9 | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/capture-3.8-8x8.json) | | 16 × 16 | 402.2 | 408.5 | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/capture-3.8-16x16.json) | The largest graph has 256 panes. Each observation still uses batched graph acquisition. Allocation counters cover the OCaml heap during the capture loop; they do not measure process RSS or tmux's allocations. ## Heap retained across captures Keeping one pane keeps its captured graph reachable. The [retention benchmark](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/retention.py) measures this with a blocking client and one session containing one pane per window. Each sample runs in a fresh process, warms the connection, then captures the same graph 100 times. It measures live OCaml heap after full major collection before capture, while retained values remain reachable, and after releasing them. On OCaml 5.5.2 and tmux 3.8, six samples per mode produced these live-heap increases in KiB. Every sample had the same retained-byte count: | Windows | Every snapshot | One pane per capture | Latest pane only | Evidence | | --- | --- | --- | --- | --- | | 1 | 115.6 | 117.9 | 1.1 | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/retention-3.8-1x1.json) | | 16 | 982.8 | 985.1 | 9.8 | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/retention-3.8-16x1.json) | | 64 | 3757.8 | 3760.1 | 37.6 | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/retention-3.8-64x1.json) | Retaining 100 detached `(id, command, path)` summaries used 13.2 KiB in each graph shape; 100 `Pane.target` values used 6.2 KiB. With 64 windows and [25 captures](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/retention-3.8-64x1-25-captures.json), retaining one pane per capture used 940.0 KiB, while retaining only the latest pane still used 37.6 KiB. Targets remain connection-bound; retaining one does not authorize a future connection. These are total live-heap differences, including list cells and projected values, not exact arena sizes. The discard control and every release phase returned 40 bytes below the warm baseline, a small baseline-overhead offset. This measurement does not cover process RSS, tmux memory, relationship-array allocation or a general leak guarantee. Mode order rotates across samples. The harness verifies topology, retained value counts, detached clients and owned daemon/pane teardown. Sample durations include the client-exit check; whole-command duration includes fixture setup and teardown. Build the benchmark: ```console $ opam exec -- dune build bench/retention.exe ``` Run the 64-window case with a new output path: ```console $ opam exec -- python3 bench/retention.py scratch/tmux/tmux-3.8/tmux \ --windows 64 \ --captures 100 \ --repeat 6 \ --output scratch/retention.json ``` ## Completed control-lane mutations The [control benchmark](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/control.py) measures typed [`Libtmux.Operations.set_option`]() mutations through one persistent Eio control client. Each sample starts a fresh one-session, one-window, one-pane daemon, warms the connection, then completes 128 distinct option writes with 1, 8 or 32 concurrent callers. Six samples per cell rotate the order of those caller counts. Every handle must finish successfully. After the measured phase, an independent read checks every option name and value. Launch logs and process/client counts verify one persistent helper, with no subprocess per mutation. Owned teardown checks helper, client, pane and daemon exit. Build the executable outside normal test loops: ```console $ opam exec -- dune build bench/control.exe ``` Run the complete matrix on one endpoint fixture: ```console $ opam exec -- python3 bench/control.py scratch/tmux/tmux-3.8/tmux \ --requests 128 \ --concurrency 1 8 32 \ --repeat 6 \ --output scratch/control-baseline.json ``` OCaml 5.5.2 results on the two supported endpoints: | tmux | Callers | Median ops/s (min–max) | Submit-to-await p95, ms | Loop allocation, MiB | | --- | --- | --- | --- | --- | | 3.3a | 1 | 6068.2 (5016.1–7190.8) | 0.292 | 12.64 | | 3.3a | 8 | 6287.1 (4645.9–7017.5) | 2.020 | 13.30 | | 3.3a | 32 | 4961.3 (3640.1–7007.8) | 9.103 | 17.43 | | 3.8 | 1 | 3969.2 (3533.3–4966.2) | 0.435 | 12.64 | | 3.8 | 8 | 4100.4 (2949.3–5087.1) | 3.077 | 13.29 | | 3.8 | 32 | 3421.2 (2744.9–4300.8) | 13.175 | 17.42 | Raw [3.3a](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/control-3.3a-128-1-8-32.json) and [3.8](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/control-3.8-128-1-8-32.json) records retain all 768 operation observations per cell, six sample durations, source/executable/binary hashes and platform details. One-minute host load started at 2.12 for 3.3a and 9.14 for 3.8, with 12 available CPUs. Other host work was active. These samples do not establish a release-speed comparison or a recommended caller count. Increasing callers does not add lanes: the engine still serializes this client. The native phase includes command construction, completion checks and result-row allocation. It excludes connection, warmup, observer probes, independent verification and teardown. Per-operation time starts immediately before [`Libtmux_eio.Client.submit`](), after a benchmark worker becomes available; it includes admitted lane queueing and [`Libtmux_eio.Client.await`](), but excludes waiting for that worker slot. The pooled p95 uses nearest rank over the 768 observations. Allocation includes the benchmark's own command and result values. The "sample_seconds" field includes setup and teardown; "consumer_and_topology_cpu_seconds" covers reaped children through consumer exit, including the topology probe, rather than daemon or whole-sample CPU. The raw "whole_command_seconds" field times the collector's entry point. Timing the outer command separately also includes Python and opam startup. These results qualify this fixed mutation workload. They do not qualify the stream-gap or mixed-load failures in [compatibility](https://libtmux.org/en/ocaml/latest/guides/compatibility/). ## Concurrent reads, streaming and cancellation The [Eio benchmark](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/concurrency.py) runs bounded concurrent option reads with distinct expected values. It checks response attribution and input order, then measures future output from an owned `cat` process with terminal echo disabled. Output cursors must advance. Finally it cancels active byte waits and checks subscription cleanup. One primary client becomes two while a stream lane is active; both observed client and child-process counts are checked. No clients remain after closure, and owned pane/daemon exit is verified. Each option read starts a read-only tmux process inside the client's pump. The request rates below include process launch and are separate from the control-lane mutation measurements above. Build the executable outside normal test loops: ```console $ opam exec -- dune build bench/concurrency.exe ``` Run the three phases against an owned release fixture: ```console $ opam exec -- python3 bench/concurrency.py scratch/tmux/tmux-3.8/tmux \ --requests 128 \ --concurrency 8 \ --streams 10 \ --cancels 10 \ --repeat 3 \ --output scratch/concurrency-baseline.json ``` Request time includes queueing until the attributed response. Stream time runs from sending literal input through observing the process's output. Cancellation time runs from the cancellation request through the observer receiving it, including subscription cleanup. The p95 uses nearest rank across all samples. On tmux 3.8, each row contains 384 requests, 30 stream samples and 30 cancellations: | Concurrent requests | Median requests/s | Request p95, ms | Stream p95, ms | Cancel p95, ms | Evidence | | --- | --- | --- | --- | --- | --- | | 1 | 556.6 | 2.517 | 0.598 | 0.258 | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/concurrency-3.8-c1.json) | | 8 | 487.9 | 19.955 | 0.863 | 0.368 | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/concurrency-3.8-c8.json) | | 16 | 561.5 | 29.746 | 0.569 | 0.220 | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/concurrency-3.8-c16.json) | | 64 | 423.5 | 159.039 | 0.667 | 0.406 | [Raw data](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/concurrency-3.8-c64.json) | The eight-request workload also passes on [3.3](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/concurrency-3.3-c8.json), [3.3a](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/concurrency-3.3a-c8.json) and [3.4](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/concurrency-3.4-c8.json). Higher concurrency increases queueing on the retained connection. These measurements come from a shared host running other work; compare raw sample ranges and load before choosing a workload or drawing performance conclusions. ## Mixed commands, stream pressure and membership changes The [mixed-load benchmark](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/mixed.py) overlaps retained command submissions, seven fast stream readers, seven deliberately stalled readers, membership changes and cancellation. Its default runs three waves with eight command workers and four attributed output frames per stream lane. It adds no work to the unit-test loops. Build the executable separately: ```console $ opam exec -- dune build bench/mixed.exe ``` Run the bounded workload against one owned release fixture: ```console $ opam exec -- python3 bench/mixed.py scratch/tmux/tmux-3.8/tmux \ --output scratch/mixed-baseline.json ``` Each wave submits 64 commands without yielding and requires the 65th to reject with the declared admission limit. Eight queued operations are cancelled before writing; their handles retain `Not_started`. Sixteen subscribers occupy seven shared session lanes. Together with the load connection's primary lane this reaches its eight-lane limit; a separate controller connection sends output and changes membership. The seventeenth subscriber and an additional session lane must reject with their specific capacity errors. These expected rejections are recorded separately from correctness failures. Commands set unique user-option names and values. The receipt retains operation IDs, worker IDs, submission indexes and the actual order in which awaiters observe results. It verifies every resulting option value, including absence for cancelled operations. Ordinary mutations use the retained control lane; `read_option` verification runs afterward through its temporary subprocess route and has a separate phase duration. Each verified option emits its operation ID, completed count and read duration before the observer handshake. The validation phase includes those handshakes; individual read durations exclude them. The observer acknowledgements and resource queries add measured overhead, so these are instrumented workload timings. Fast streams must deliver exact ordered bytes with continuous generation and byte offsets. Tiny unconsumed queues must report `Gap Overflow`, then [`End`](). Splitting and removing a temporary pane changes a watched window's membership; affected readers must report `Gap Membership_changed`, then [`End`](). Explicit readmission uses a fresh observation and generation. Unchanged lanes continue independently; the receipt requires observed stream progress while commands remain pending in each wave. Each wave also cancels an active observer after its retained wait reaches `Acknowledged`, releases the wait through the controller, and checks the eventual result before and after client closure. Native event callbacks synchronously sample owned Linux process trees before yielding to telemetry, retaining their occurrence-time pending-command counts. Progress handshakes separately sample process trees and attached tmux clients, including while the churn pane is alive. Emission timestamps and counts stay separate from event occurrence; queued telemetry cannot manufacture overlap. Raw records distinguish the Python harness, OCaml process, daemon, command-client children and pane children. RSS/FD/process/client maxima are maxima of these observations; per-process RSS high-water marks come from the kernel. They do not establish continuous aggregate resource peaks. The controller contributes a primary client and, during its retained wait, an auxiliary client in addition to the load connection's eight clients. The sampler's own retained pidfds affect its FD count. Per-worker receipts report completed quotas, cancellation counts, latency samples and first/last observation times. They characterize this bounded workload rather than proving scheduler fairness, a service-level objective or general capacity. The 1 MiB queued-byte and 32 MiB payload limits are recorded source constraints; this workload pressures admission counts, subscriber counts and lane counts, not those byte maxima. No automatic retries or timeout increases hide a failure. Exercise teardown after interruption with a new output file: ```console $ opam exec -- python3 bench/mixed.py scratch/tmux/tmux-3.8/tmux \ --rounds 1 \ --frames 2 \ --inject-failure \ --output scratch/mixed-interrupted.json ``` `EXPECTED_FAILURE` means the harness intentionally interrupted the native process while a temporary pane was alive. It still requires owned daemon, pane and command-client exit, reaping, zero remaining children and removal of the private fixture directory. Unexpected errors or failed cleanup produce `FAIL`; an interruption is not a successful workload measurement. The [summary](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/mixed-20261108.json) records each outcome and source identity. The linked gzip archives contain the complete JSON receipts; the summary includes their compressed and uncompressed SHA-256 digests. Raw results retain exact source and executable hashes, fixture-helper hash, versions, command arguments, workload bounds, host load, resource observations and cleanup receipts. Keep failed receipts beside passing measurements when assessing a release. Current collector qualification is **PARTIAL** across the supported releases: | tmux | Mixed workload disposition | Raw evidence | | --- | --- | --- | | 3.3 | Current workload passed; an earlier observer-command failure remains unattributed. | [Current](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/mixed-3.3-current-20261108.json.gz), [earlier failure](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/mixed-3.3-qualified-20261108.json.gz) | | 3.3a | Current workload passed. | [Current](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/mixed-3.3a-current-20261108.json.gz) | | 3.4 | Reproduced daemon SIGSEGV: a null control state is dereferenced while notifying control clients of a detach. | [Failure](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/mixed-3.4-qualified-20261108.json.gz), [core analysis](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/mixed-3.4-crash-analysis-20261108.json) | | 3.8 | Current default workload and intentional interruption cleanup passed. | [Default](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/mixed-3.8-current-20261108.json.gz), [interruption](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/bench/results/mixed-3.8-current-interrupted-20261108.json.gz) | Earlier records predate collector corrections; use their recorded source hashes. A later passing sample does not resolve an unexplained failure. These outcomes qualify this mixed benchmark separately from the existing native runtime tests. The three 3.4 cores identify the same upstream write path. A later [tmux correction](https://github.com/tmux/tmux/commit/93169e2e6231853c61c33432bceebf4ffc91015b) guards null control state; that guard is present in the exact 3.8 source. The precise triggering sequence remains unproved. The supported range is 3.3a–3.8; the 3.3 measurements above are historical evidence. The 3.4 mixed workload remains a failed qualification. Benchmarks are a separate tier. Correctness gates and test-loop timings are listed in [testing](https://libtmux.org/en/ocaml/latest/guides/testing/), not used as throughput measurements. Keep a single benchmark run under ten minutes and a complete sweep under one hour. [Compatibility](https://libtmux.org/en/ocaml/latest/guides/compatibility/) · [Control](https://libtmux.org/en/ocaml/latest/guides/control/) · [Guide](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/README.md) --- # Current capabilities and adoption gaps Source: https://libtmux.org/en/ocaml/latest/guides/status/ > Current capabilities and adoption gaps: libtmux documentation. The immutable model, typed queries, native Linux clients and output observation have real-tmux evidence at the active tmux endpoints, 3.3a and 3.8. Intermediate releases remain supported throughout 3.3a–3.8. The installed workspace CLI also passes those cells. The final suite review remains unfinished. No package or hosted-documentation release has been published. ## What an application can use Capture a complete graph, retain it after closing the client, traverse shared windows through session-specific links, filter with native predicates or typed expressions, and establish cardinality before an explicit mutation. Native operations cover sessions, windows, panes, captured clients, layouts, environment, options and hooks. Both blocking and Eio transports retain operation outcomes and expose bounded subscriptions. The [compatibility table](https://libtmux.org/en/ocaml/latest/guides/compatibility/) identifies qualified components. The [examples](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/README.md) are installed, compiled and executed against owned servers; they cover graph links, query results, created resources, literal input, streaming, rendered waits and client teardown. ## What is still missing | Area | Required evidence before complete-suite adoption | | --- | --- | | Documentation site | Qualify the final hosted reference and verify its deployed links; public `.cmti` export and local rendering are implemented | | Known runtime findings | Account for the [recorded daemon-exit and observer failures](https://libtmux.org/en/ocaml/latest/guides/compatibility/) when choosing a deployment workload | | Final qualification | Rerun every required component and exact release cell on the final source revision | | Package adoption | Obtain an independent caller-task review and resolve material findings | [Recorded benchmarks](https://libtmux.org/en/ocaml/latest/guides/benchmarking/) cover blocking/Eio capture, graphs up to 256 panes, concurrent requests, OCaml heap allocation, client/daemon CPU, future-output latency and cancellation cleanup. Their raw data preserves the workload, source identities and host conditions. The workspace model, JSON loader, blocking/Eio executors and installed CLI are implemented. The [workspace guide](https://libtmux.org/en/ocaml/latest/workspace/source-guide/) executes the installed command through validation, planning, apply, receipt inspection and owned cleanup. Live tests cover partial effects, compensation, append isolation, stale plans, cancellation journals and bootstrap on fresh and existing daemons. Cleanup targets only proven newly created panes; foreign panes moved into a new window remain. Versioned advisory plans and execution receipts preserve errors, creation IDs and cleanup outcomes; output documents cannot be replayed as operational input. CLI checks also cover malformed input, collisions, missing endpoints, interrupted apply, changed documents and stalled or broken stdout. ## Engineering assessment For a Linux application needing immutable tmux observations and explicit native control, the model and clients are candidates for adoption with the documented constraints. Suite adoption still needs the documentation and qualification above. Maintainer count and alpha status do not decide that assessment; observable behavior and recovery do. An adopting engineer should expect explicit endpoint selection, client lifetime, command uncertainty and stream gaps. The package should keep these visible. Implicit reconnects, callback-driven property acquisition, a mandatory query wrapper or a required runtime for pure filtering would make the API harder to reason about. A 9.5/10 whole-package rating is not established. Hosted documentation and final qualification have open gates. Documentation quality cannot substitute for those results. [Testing](https://libtmux.org/en/ocaml/latest/guides/testing/) describes current evidence and how examples are kept in sync with source. MCP integration is excluded by project policy. Its SDK, conformance and host integration are outside the adoption criteria. The supported automation surfaces are native OCaml APIs and the workspace CLI; see [package boundaries](https://libtmux.org/en/ocaml/latest/guides/packages/). --- # Linking and consuming our docs Source: https://libtmux.org/en/ocaml/latest/meta/linking/ > Find documentation exports for search engines, bots, LLMs and other documentation sites. We welcome bots, search engines, SEO tools, LLMs and other clients to use our documentation. Read a page in your browser, fetch its Markdown, discover pages through a manifest, or link to documented objects from another site. ## Choose an export The page footer links to the exports for its documentation tree. Use those links to select a language, version or preview. | File or format | Use it for | | --- | --- | | [llms.txt](https://libtmux.org/en/llms.txt) | Discover guides, API references and agent prompts through a Markdown index. | | [llms-full.txt](https://libtmux.org/en/llms-full.txt) | Fetch guides and MCP contracts together as Markdown. API references are linked separately. | | [docs.json](https://libtmux.org/en/docs.json) | Discover page titles, URLs, descriptions, headings and Markdown URLs in a structured manifest. | | Markdown | Read one page as text using its footer's **Markdown** link. | | Raw source | Inspect the source file using its footer's **Raw source** link, where available. | | [objects-full.inv](https://libtmux.org/en/objects-full.inv) | Resolve Sphinx references across shared docs, each port's default version, or its latest until the default has published a catalog, and tmux reference versions. | | objects.inv | Resolve Sphinx references within one port and version, or one tmux documentation tree. | | [targets.json](https://libtmux.org/en/targets.json) | Browse inventory keys, display names and destination URLs as JSON. A scoped catalog sits beside each root inventory. The root one lists fallbacks and ports left out. | The root exports above cover the assembled site. Port exports describe the selected port and version. For a single API declaration, start with `docs.json` and follow its reference and symbol-index links. ## Link from Sphinx The [Intersphinx guide](https://libtmux.org/en/meta/intersphinx/) explains how to configure inventories, choose native roles or portable labels, and preserve references when a page moves or changes its title. Browse [Intersphinx examples](https://libtmux.org/en/meta/intersphinx/examples/) for copyable RST and MyST references to library APIs, tmux C declarations, hooks, format variables, CLI commands and schema fields, paired with their destinations. ## Discover and index pages Use the [sitemap](https://libtmux.org/en/sitemap-index.xml) to discover indexable pages. Follow [robots.txt](https://libtmux.org/robots.txt) and each page's robots metadata: pull-request previews, component demos and search-index shards are excluded from crawling, and alternate versions can carry `noindex`. Choose a versioned documentation tree when a reference needs a particular release. The full inventory follows each port's advertised default; a port's `objects.inv` describes the selected version. --- # Link to libtmux from Sphinx Source: https://libtmux.org/en/ocaml/latest/meta/intersphinx/ > Use Intersphinx inventories to reference libtmux APIs and documentation. Sphinx can resolve references to libtmux APIs, guides, headings, MCP objects, schema fields, and other documented targets. The footer links to the full inventory and the inventory for the page's port or tmux version. Browse [Intersphinx examples](https://libtmux.org/en/meta/intersphinx/examples/) for copyable RST and MyST references paired with their destinations. For Markdown exports, manifests and crawler discovery, see [Linking and consuming our docs](https://libtmux.org/en/meta/linking/). ## Configure Intersphinx Enable the built-in extension in your project's `conf.py`: Python's [`None`]() discovers `objects.inv` at the configured base URL. The port mapping above selects the Python `latest` tree. Copy the base URL of another port or version to select its inventory. The full inventory includes shared documentation, the configured default version of each assembled port, and distinct tmux reference versions. It is generated after site assembly. Each port inventory includes the rendered targets in that exact version. The tmux inventory includes the shared tmux guides and the latest CLI manual, options, hooks, formats, and C source reference. A version mapping such as `tmux-3-7c` includes only that release's manual, catalog, and C declarations. Manual and C reference pages link to the same version inventory in the footer. Use mapping names without dots when writing MyST reference roles. ## Choose a reference Every exported target has a portable label that works with `std:ref`, without installing a language or MCP domain. Open `targets.json` beside the inventory and copy its `key`. Its `href` and `display` identify the destination. For example, the Python [`libtmux.Pane`]() declaration has this reStructuredText reference: ```rst :external+libtmux:ref:`Pane ` ``` With MyST enabled in your project, the same label works in Markdown: ```markdown {external+libtmux:ref}`Pane ` ``` Native Python, C++, JavaScript, and C objects also have aliases for their built-in roles. A scoped Python inventory supports: ```rst :external+libtmux-python:py:class:`libtmux.Pane` ``` The tmux mapping provides native C roles and portable manual references: ```rst :external+tmux:c:struct:`cmd` :external+tmux:ref:`capture-pane ` ``` Command options also work with Sphinx's program context: ```rst .. program:: tmuxp load :external+libtmux-python:option:`--append` ``` The full inventory qualifies native names by port, such as [`py:libtmux.Pane`](). A fully qualified alias includes the product too: [`py:core:libtmux.Pane`](). Short aliases are offered only when they identify one destination. Configuration values, command options, environment variables, and glossary terms use standard-domain types where their renderers identify those semantics. ## Stable labels A portable key contains `scope:product:category:identity`. Its encoding preserves uppercase letters, punctuation, and Unicode, because `std:ref` lowercases targets. `%50ane` and [`pane`]() therefore remain distinct. Copy the key from the catalog without decoding its percent escapes. Keys are independent of display titles and destination URLs. API public IDs, protocol wire identities, JSON Pointers, named parameters, and native method identities supply their names. Authored documents can retain `inventoryId` and explicit HTML fragment IDs when their titles or routes change. Contributors preserve renamed keys in the inventory alias catalog and use the existing page redirect mechanism for retired routes. ### Preserve published links If `meta/intersphinx.md` moves to `meta/linking.md`, retain its logical ID and list the retired route in the new page's frontmatter: ```yaml inventoryId: intersphinx aliases: - meta/intersphinx ``` The document key remains `site:core:doc:intersphinx`, and the old route redirects to the new page. Retain existing heading fragment IDs when their text changes. When an identity or fragment changes intentionally, map the former keys to current targets in `site/src/data/inventory-aliases.json`. For example, choosing `inventoryId: linking` and changing `stable-labels` to `stable-references` needs these entries: ```json [ { "scope": "site", "from": "site:core:doc:intersphinx", "to": "site:core:doc:linking" }, { "scope": "site", "from": "site:core:heading:intersphinx%23stable-labels", "to": "site:core:heading:linking%23stable-references", "fragment": "stable-labels" } ] ``` The document entry preserves `std:ref` and `std:doc` references. The heading entry preserves `std:ref`; `fragment` also installs the retired HTML anchor at the new destination. Keep the route alias too: inventory aliases do not redirect retired URLs. Copy `to` from the current target catalog. Retain earlier `from` entries and update their destinations when renaming again. Former native role names, such as a Python class name, need preservation in the native metadata. Existing `objects.inv` and `reference/objects.inv` endpoints remain compatibility exports. New integrations should use the full inventory, a port's root inventory, or a tmux root inventory to obtain the complete target catalog and case-safe labels. See the [Sphinx Intersphinx documentation](https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html) for named inventories and cross-reference syntax. --- # Intersphinx examples Source: https://libtmux.org/en/ocaml/latest/meta/intersphinx/examples/ > Compare Sphinx and MyST references to APIs, tmux C declarations, hooks, format variables, CLI commands and schema fields. import { referenceHref } from '../../../../lib/api-models' Copy a reference and compare its destination below. These examples cover native language roles, portable labels, full and scoped inventories, and links into tmux C declarations, hooks, format variables and CLI commands. ## Configure the inventories Add these mappings to your Sphinx project's `conf.py`: Python's [`None`]() discovers `objects.inv` at the mapping's base URL. The full inventory uses each port's advertised default version. The `py` mapping explicitly selects `latest`; `tmux` selects the latest tmux manual and C reference. Use the root URL from an inventory's footer link when selecting a preview or another release. ## Native Python roles A named inventory selects the Python tree: ```rst :external+py:py:class:`libtmux.Pane` ``` Destination: [Pane](https://libtmux.org/en/py/latest/reference/libtmux-pane/#libtmux.Pane). The full inventory qualifies native names by port: ```rst :external+full:py:class:`py:libtmux.Pane` ``` Destination: Pane in the Python default version. For automatic lookup, use the ordinary role. A local definition takes precedence over Intersphinx: ```rst :py:class:`libtmux.Pane` ``` Destination: [Pane](https://libtmux.org/en/py/latest/reference/libtmux-pane/#libtmux.Pane). ## Portable labels Portable labels work with the standard reference role, including languages that have no built-in Sphinx domain. Copy the key from `targets.json`. ```rst :external+full:ref:`Pane ` :external+py:ref:`Pane ` ``` Destinations: Full inventory: Pane, [Scoped inventory: Pane](https://libtmux.org/en/py/latest/reference/libtmux-pane/#libtmux.Pane). The external role searches the configured inventories without selecting one. This native name is unqualified only in the scoped Python inventory: ```rst :external:py:class:`libtmux.Pane` ``` Destination: [Pane](https://libtmux.org/en/py/latest/reference/libtmux-pane/#libtmux.Pane). ## MyST roles and inventory URLs Enable `myst_parser` alongside [`sphinx.ext.intersphinx`](https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html) for Markdown sources. Named roles accept the same targets as reStructuredText: ```markdown {external+py:py:class}`libtmux.Pane` ``` Destination: [Pane](https://libtmux.org/en/py/latest/reference/libtmux-pane/#libtmux.Pane). Portable labels retain their percent escapes inside reference roles: ```markdown {external+full:ref}`Pane ` ``` Destination: Pane in the Python default version. MyST's inventory URL syntax also supports native aliases: ```markdown [Pane](inv:py:py:class#libtmux.Pane) ``` Destination: [Pane](https://libtmux.org/en/py/latest/reference/libtmux-pane/#libtmux.Pane). Use role syntax for percent-encoded portable keys: MyST normalizes escapes in `inv:` URLs. Keep mapping names free of dots, as in `tmux-3-7c`, so MyST can parse the role name. When using an inventory URL, specify its domain and object type if a name is both a document and a label. ## tmux C declarations The tmux mapping provides Sphinx's built-in C roles: ```rst :external+tmux:c:struct:`cmd` :external+tmux:c:func:`cmd_find_target` ``` Destinations: [cmd](https://libtmux.org/en/tmux/latest/reference/c-struct-cmd/#c%3Astruct%3Acmd), [cmd_find_target](https://libtmux.org/en/tmux/latest/reference/c-function-cmd_find_target/#c%3Afunction%3Acmd_find_target). Select a release with its own mapping. The full inventory also offers version-qualified native aliases: ```rst :external+tmux-3-7c:c:func:`cmd_find_target` :external+full:c:func:`tmux@3.7c:cmd_find_target` :external+full:c:func:`tmux@latest:cmd_find_target` ``` Destinations: [Scoped: cmd_find_target in 3.7c](https://libtmux.org/en/tmux/3.7c/reference/c-function-cmd_find_target/#c%3Afunction%3Acmd_find_target), [Full: cmd_find_target in 3.7c](https://libtmux.org/en/tmux/3.7c/reference/c-function-cmd_find_target/#c%3Afunction%3Acmd_find_target), [Full: cmd_find_target in latest](https://libtmux.org/en/tmux/latest/reference/c-function-cmd_find_target/#c%3Afunction%3Acmd_find_target). MyST supports the same C roles and a typed inventory URL: ```markdown {external+tmux:c:struct}`cmd` {external+tmux:c:func}`cmd_find_target` {external+full:c:func}`tmux@3.7c:cmd_find_target` [cmd_find_target](inv:tmux:c:function#cmd_find_target) ``` Destinations: [MyST: cmd](https://libtmux.org/en/tmux/latest/reference/c-struct-cmd/#c%3Astruct%3Acmd), [MyST: cmd_find_target](https://libtmux.org/en/tmux/latest/reference/c-function-cmd_find_target/#c%3Afunction%3Acmd_find_target), [MyST full: cmd_find_target in 3.7c](https://libtmux.org/en/tmux/3.7c/reference/c-function-cmd_find_target/#c%3Afunction%3Acmd_find_target), [Inventory URL: cmd_find_target](https://libtmux.org/en/tmux/latest/reference/c-function-cmd_find_target/#c%3Afunction%3Acmd_find_target). The C reference links each declaration to its source file and revision. Portable labels also address its signatures, parameters, and sections. ## tmux hooks Link a hook name to its catalog page with a portable label. The same key works in the scoped tmux inventory and the full inventory: ```rst :external+tmux:ref:`after-new-session ` :external+full:ref:`after-new-session ` ``` Destinations: [Scoped: after-new-session](https://libtmux.org/en/tmux/latest/catalog/hooks/after-new-session/), [Full: after-new-session](https://libtmux.org/en/tmux/latest/catalog/hooks/after-new-session/). In MyST, use the same key inside a reference role. Preserve its percent escapes: ```markdown {external+tmux:ref}`after-new-session ` {external+full:ref}`after-new-session ` ``` Destinations: [MyST scoped: after-new-session](https://libtmux.org/en/tmux/latest/catalog/hooks/after-new-session/), [MyST full: after-new-session](https://libtmux.org/en/tmux/latest/catalog/hooks/after-new-session/). ## tmux formats Link a format variable such as [`#{session_name}`](https://libtmux.org/en/tmux/latest/catalog/formats/session_name/) to its catalog entry: ```rst :external+tmux:ref:`session_name ` :external+full:ref:`session_name ` ``` Destinations: [Scoped: session_name](https://libtmux.org/en/tmux/latest/catalog/formats/session_name/), [Full: session_name](https://libtmux.org/en/tmux/latest/catalog/formats/session_name/). The MyST references select the same scoped or full inventory: ```markdown {external+tmux:ref}`session_name ` {external+full:ref}`session_name ` ``` Destinations: [MyST scoped: session_name](https://libtmux.org/en/tmux/latest/catalog/formats/session_name/), [MyST full: session_name](https://libtmux.org/en/tmux/latest/catalog/formats/session_name/). ## tmux CLI manual The same tmux inventory covers command pages: ```rst :external+tmux:ref:`capture-pane ` ``` Destination: [capture-pane](https://libtmux.org/en/tmux/latest/manual/capture-pane/). The document role works when the target is a whole page: ```rst :external+tmux:doc:`Full manual ` ``` Destination: [Full tmux manual](https://libtmux.org/en/tmux/latest/manual/full/). Select a command's manual page with the document role. Its portable key also works in the full inventory: ```rst :external+tmux:doc:`tmux new-session ` :external+full:doc:`tmux new-session ` ``` Destinations: [Scoped: new-session](https://libtmux.org/en/tmux/latest/manual/new-session/), [Full: new-session](https://libtmux.org/en/tmux/latest/manual/new-session/). Use the same document role in MyST: ```markdown {external+tmux:doc}`tmux new-session ` {external+full:doc}`tmux new-session ` ``` Destinations: [MyST scoped: new-session](https://libtmux.org/en/tmux/latest/manual/new-session/), [MyST full: new-session](https://libtmux.org/en/tmux/latest/manual/new-session/). ## Command options, environment variables, and terms The Python port's workspace documentation exports standard-domain objects. Set the program context before referencing an option: ```rst .. program:: tmuxp load :external+py:option:`--append` ``` Destination: [tmuxp load --append](https://libtmux.org/en/py/latest/workspace/cli/load/#arguments-and-flags-a-append). Environment variables and glossary terms use their own roles. Qualify the product when a term appears in more than one glossary: ```rst :external+py:envvar:`COLUMNS` :external+py:term:`async ` ``` Destinations: [COLUMNS](https://libtmux.org/en/py/latest/workspace/configuration/environment/#cli-and-runtime-variables-columns-lines-rows), [async](https://libtmux.org/en/py/latest/reference/glossary/#async). ## MCP tools and schema fields Portable labels also address protocol objects and individual JSON Schema fields. No custom MCP domain is required: ```markdown {external+py:ref}`Read tools batch ` {external+py:ref}`on_error ` ``` Destinations: [Read tools batch](https://libtmux.org/en/py/latest/mcp/tools/call_read_tools_batch/#mcp.tool.call_read_tools_batch), [on_error](https://libtmux.org/en/py/latest/mcp/tools/call_read_tools_batch/#mcp.tool.call_read_tools_batch.input%2Fproperties%2Fon_error). ## Shared documentation and headings The full inventory includes the shared documentation: ```rst :external+full:doc:`Inventory guide ` :external+full:ref:`Configure Intersphinx ` ``` Destinations: [Inventory guide](https://libtmux.org/en/meta/intersphinx/), [Configure Intersphinx](https://libtmux.org/en/meta/intersphinx/#configure-intersphinx). ## Local files and fallback locations Download an inventory and pass its filename as the second mapping value. The first value remains the documentation URL used in generated links: An ordered list tries a local copy before the HTTP inventory: See the [inventory guide](https://libtmux.org/en/meta/intersphinx/) for key stability and the [Sphinx Intersphinx reference](https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html) for configuration options. --- # AI policy Source: https://libtmux.org/en/ocaml/latest/meta/ai-policy/ > Use the libtmux packages with AI tools. When contributing, check what you submit and wait for a maintainer's invitation before preparing a pull request. Last revised October 25, 2026 Use these packages with AI tools as you like. When contributing, check what you submit and wait for a maintainer's invitation before preparing a pull request. This policy covers the libraries, ports, and documentation in the [libtmux](https://github.com/libtmux) and [tmux-python](https://github.com/tmux-python) organizations, including their issues, comments, discussions, reviews, and pull requests. ## 1. For users: About these packages We encourage you to use AI tools with our libraries and documentation, including to generate code that calls the libraries. We want these projects to be useful in the way you work. Some of what we publish supports that workflow: - [MCP integrations](https://libtmux.org/en/py/latest/mcp/) that let assistants interact with tmux. - Markdown documentation and `llms.txt` and `llms-full.txt` resources linked from documentation pages. - [`AGENTS.md`]() and other repository instructions for tools working on the codebase. Use the documentation for the package, language, and version you are working with. ### How the ports are made The ports build on [tmuxp](https://tmuxp.git-pull.com/), started in 2013, and [libtmux](https://libtmux.git-pull.com/), started in 2016. Both projects predate today's LLM coding tools. Current work on the original projects and the ports uses AI assistance. Maintainers guide the design, review changes, test behavior against real tmux, and decide what ships. They remain responsible for what the projects include and maintain. ### Stability Packages in the [libtmux organization](https://github.com/libtmux) are experimental unless their README says otherwise. Names and APIs may change between releases. Pin an exact version and review changes before upgrading. ## 2. For contributors: How to report issues, ideas, suggestions, and security issues A useful issue is a contribution in its own right. Your experience helps us see what breaks, what feels awkward, and what is missing. You do not need to arrive with a patch. Open bugs, ideas, and suggestions in the repository of the package they concern. Report suspected security vulnerabilities privately using the route below. ### Bugs Tell us what you tried, what happened, and what you expected. Include a small example we can run, the library and tmux versions, and the actual output. Include relevant logs and error messages as they appeared, and distinguish what you observed from possible explanations. Bug reports are welcome whether the code you were running was handwritten or generated. Include the code that ran. A conversation transcript is usually unnecessary; include relevant prompts or tool calls when they are needed to reproduce the problem. ### Ideas and suggestions Explain what you wanted to accomplish and where the current design gets in your way. We welcome feedback on: - Usability: what was hard to find or get right, and where the documentation misled you. - Language conventions: whether a port feels natural in the language you use. - Aesthetics: how an API, a page, or an error message reads. - Missing capabilities: what you could not accomplish with the current API. If an API you expected is missing, describe the task and why that API would help. ### Security issues Report suspected security vulnerabilities privately. Use **Report a vulnerability** on the affected repository's security page where available. Include the affected package and version, the tmux version, what you observed, and reproduction steps or relevant code references if available. Distinguish verified behavior from suspected impact. Please coordinate public disclosure with us so we can investigate and, where needed, prepare a fix. Command execution explicitly requested through documented features is expected behavior and does not, by itself, establish a vulnerability. Explain any additional failure, such as data being interpreted as commands unexpectedly or an intended security boundary being crossed. ### Writing your report Short reports, typos, and imperfect English are fine. Translation and writing assistance are welcome; check that the result says what you mean and preserves the facts you observed. Your experience matters more than a polished report. ### Using AI tools If AI materially helped prepare your issue, comment, discussion, review, or pull request, say which tools you used, what they did, and what you checked yourself. Using these tools does not count against a contribution. You remain responsible for what you submit. Do not send generated code, reports, or explanations you have not checked. ### Pull requests Before preparing a pull request, open an issue or comment on an existing issue about the proposed change. Wait for a maintainer to invite a PR for an agreed scope. Opening an issue alone is not an invitation. PRs submitted without that agreement may be closed without review. Arrange security fixes through the private reporting route. Even when we agree that something should change, we may prefer to write the implementation ourselves. Review must consider correctness, supported tmux versions, language conventions, documentation, and ongoing maintenance. Checking those can take more time than writing the change, whether it was handwritten or generated. If we invite a PR, understand the changes, test them, and be able to explain them. Follow the project's contribution instructions, and describe the tests you ran and the versions you used. Generated code and tests can share the same mistake, so passing tests alone do not show that a change fits the project. Submit only material you have the right to contribute on terms compatible with the project's license. Identify outside code, tests, or text incorporated into the contribution, including material adapted with AI assistance, and provide its source and license. Keep required notices, and raise unclear permissions before including the material. These source and licensing requirements apply to maintainers too.

Acknowledgments: Informed by Pallets’ LLM and AI Policy, tmux’s AI contribution guidelines, and OpenJDK’s Interim Policy on Generative AI. Licensing references: the GNU GPL FAQ on translating code and license compatibility. All sources consulted .

--- # Third-party notices Source: https://libtmux.org/en/ocaml/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 and Rust. Scala's 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 turns its mark white when the device is in dark mode. A browser does not reliably apply that rule to this site's color setting when the file is shown as an image. The rule is removed, its paths are unchanged, and the page lightens the mark on a dark theme instead. | 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. --- # API glossary Source: https://libtmux.org/en/ocaml/latest/reference/glossary/ > Kinds, modifiers and package labels in the OCaml API reference. ## Symbol kinds Kinds identify declarations in the OCaml API reference. ### module An OCaml module exposing values, types and nested modules through a signature. References: [OCaml module types](https://ocaml.org/manual/5.5/modtypes.html#s:modtypes). ### interface An OCaml module type specifying the declarations a module must provide. References: [OCaml module types](https://ocaml.org/manual/5.5/modtypes.html#s:modtypes). ### struct An OCaml record type with named fields. References: [OCaml type and exception definitions](https://ocaml.org/manual/5.5/typedecl.html#s:tydef). ### enum An OCaml variant type with named constructors, which can carry values. References: [OCaml type and exception definitions](https://ocaml.org/manual/5.5/typedecl.html#s:tydef). ### function An OCaml value with a function type. Arguments are curried and can carry labels or be optional. References: [OCaml expressions](https://ocaml.org/manual/5.5/expr.html#s:value-expr). ### attribute A field of an OCaml record. Its module supplies the qualified name; the reference groups it under its declaring type. References: [OCaml type and exception definitions](https://ocaml.org/manual/5.5/typedecl.html#s:tydef). ### constant A named OCaml value or variant constructor. The signature does not promise a compile-time constant. References: [OCaml module types](https://ocaml.org/manual/5.5/modtypes.html#s:modtypes). ### typealias An OCaml type declaration. A declaration without an equation or representation is abstract. References: [OCaml type and exception definitions](https://ocaml.org/manual/5.5/typedecl.html#s:tydef). ### exception An OCaml exception constructor. References: [OCaml type and exception definitions](https://ocaml.org/manual/5.5/typedecl.html#s:tydef). ## Modifiers Modifiers add information about calling, implementing, or accessing a declaration. Their precise effect follows the language and the signature. ### opaque An abstract OCaml type whose representation is hidden by its public signature. References: [OCaml module types](https://ocaml.org/manual/5.5/modtypes.html#s:modtypes). ## Package context Package labels identify where an API is supplied. ### package An installable OCaml library package. Its public modules supply the declarations in this reference. A package name and an OCaml module path are distinct: libtmux-unix provides Libtmux_unix. References: [OCaml libraries with Dune](https://ocaml.org/docs/libraries-dune). --- # OCaml-based tmux workspace manager Source: https://libtmux.org/en/ocaml/latest/workspace/ > Validate, preview and apply JSON workspaces through the native OCaml library. **Workspace Manager concept example** A working example of the core library. It can change without notice. Build `tmux-workspace` from the source revision in the installation guide. The `libtmux-workspace-cli` package supplies this command. Follow the workspace guide for its configuration format. Install `libtmux-workspace-cli` from the source checkout to run `tmux-workspace`. The CLI uses the JSON decoder, pure workspace plan and native client packages. It retains a receipt for recovery after partial or uncertain execution. [Run a complete workspace](https://libtmux.org/en/ocaml/latest/workspace/source-guide/) · [Workspace API](https://libtmux.org/en/ocaml/latest/workspace/reference/) - [Workspace guide](https://libtmux.org/en/ocaml/latest/workspace/source-guide/): Validate, plan and apply JSON configuration, then inspect its receipt. - [API reference](https://libtmux.org/en/ocaml/latest/workspace/reference/): Browse pure validation and planning alongside explicit execution. --- # OCaml workspace API reference Source: https://libtmux.org/en/ocaml/latest/workspace/reference/ > Public OCaml workspace validation, planning and execution APIs. **Workspace Manager concept example** A working example of the core library. It can change without notice. Use [`Libtmux_workspace`]() to validate configuration and build a plan without I/O. Execute the plan through an explicit native client and retain its journal for recovery. [`Libtmux_json`]() decodes the versioned workspace JSON format. The declarations below document the workspace library at this site's source revision. The [workspace guide](https://libtmux.org/en/ocaml/latest/workspace/source-guide/) shows installation, planning, execution and receipt inspection with `tmux-workspace`. ## API declarations - [Libtmux_workspace](https://libtmux.org/en/ocaml/latest/workspace/reference/libtmux_workspace/) - [Libtmux_eio.Workspace](https://libtmux.org/en/ocaml/latest/workspace/reference/libtmux_eio-workspace/) - [Libtmux_json.Workspace](https://libtmux.org/en/ocaml/latest/workspace/reference/libtmux_json-workspace/) - [Libtmux_unix.Workspace](https://libtmux.org/en/ocaml/latest/workspace/reference/libtmux_unix-workspace/) --- # API glossary Source: https://libtmux.org/en/ocaml/latest/workspace/reference/glossary/ > Kinds, modifiers and package labels in the OCaml API reference. ## Symbol kinds Kinds identify declarations in the OCaml API reference. ### module An OCaml module exposing values, types and nested modules through a signature. References: [OCaml module types](https://ocaml.org/manual/5.5/modtypes.html#s:modtypes). ### interface An OCaml module type specifying the declarations a module must provide. References: [OCaml module types](https://ocaml.org/manual/5.5/modtypes.html#s:modtypes). ### struct An OCaml record type with named fields. References: [OCaml type and exception definitions](https://ocaml.org/manual/5.5/typedecl.html#s:tydef). ### enum An OCaml variant type with named constructors, which can carry values. References: [OCaml type and exception definitions](https://ocaml.org/manual/5.5/typedecl.html#s:tydef). ### function An OCaml value with a function type. Arguments are curried and can carry labels or be optional. References: [OCaml expressions](https://ocaml.org/manual/5.5/expr.html#s:value-expr). ### attribute A field of an OCaml record. Its module supplies the qualified name; the reference groups it under its declaring type. References: [OCaml type and exception definitions](https://ocaml.org/manual/5.5/typedecl.html#s:tydef). ### constant A named OCaml value or variant constructor. The signature does not promise a compile-time constant. References: [OCaml module types](https://ocaml.org/manual/5.5/modtypes.html#s:modtypes). ### typealias An OCaml type declaration. A declaration without an equation or representation is abstract. References: [OCaml type and exception definitions](https://ocaml.org/manual/5.5/typedecl.html#s:tydef). ### exception An OCaml exception constructor. References: [OCaml type and exception definitions](https://ocaml.org/manual/5.5/typedecl.html#s:tydef). ## Modifiers Modifiers add information about calling, implementing, or accessing a declaration. Their precise effect follows the language and the signature. ### opaque An abstract OCaml type whose representation is hidden by its public signature. References: [OCaml module types](https://ocaml.org/manual/5.5/modtypes.html#s:modtypes). ## Package context Package labels identify where an API is supplied. ### package An installable OCaml library package. Its public modules supply the declarations in this reference. A package name and an OCaml module path are distinct: libtmux-unix provides Libtmux_unix. References: [OCaml libraries with Dune](https://ocaml.org/docs/libraries-dune). --- # OCaml topics Source: https://libtmux.org/en/ocaml/latest/topics/ > Understand native client operations, streaming and compatibility. Client operations perform explicit I/O. Captured observations remain local and readable after client closure. Follow the guides below for operational contracts, cancellation and supported environments. - [Operations and completion](https://libtmux.org/en/ocaml/latest/guides/control/): Distinguish admission, acknowledgement, completion and uncertainty. - [Streaming and cancellation](https://libtmux.org/en/ocaml/latest/guides/streaming/): Observe bounded output subscriptions without blocking other clients. - [Compatibility](https://libtmux.org/en/ocaml/latest/guides/compatibility/): Check qualified platforms, tmux releases and remaining limitations. --- # Workspace guide Source: https://libtmux.org/en/ocaml/latest/workspace/source-guide/ > Workspace guide: libtmux documentation. **Workspace Manager concept example** A working example of the core library. It can change without notice. `tmux-workspace` validates a JSON configuration, previews an advisory plan, and applies a fresh plan to an explicit socket. Install it through the [source installation](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/README.md#install-from-source); `opam install .` includes the CLI. Run this guide from the repository root. `check` performs no tmux I/O. `plan` observes the endpoint and never starts a daemon. `apply` rereads the document, checks current state, then executes ordered actions. A preview is not a transaction or an executable target. ## Define the layout The shipped [project.json](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/examples/workspace/project.json) creates an `editor` window with two panes and a `logs` window with one. Each pane runs `/bin/cat -u` directly, keeping the demonstration alive without a shell command. ```json { "schema": "libtmux.workspace/1", "session": "ocaml-project", "cwd": ".", "width": 120, "height": 40, "environment": {"PROJECT": "ocaml"}, "options": [{"name": "@project", "kind": "text", "value": "ocaml"}], "windows": [ { "name": "editor", "layout": "even-horizontal", "panes": [ {"exec": ["/bin/cat", "-u"]}, {"exec": ["/bin/cat", "-u"], "direction": "right"} ] }, {"name": "logs", "panes": [{"exec": ["/bin/cat", "-u"]}]} ] } ``` Relative directories resolve against the JSON file's directory. Environment values merge from session to window to pane. `exec` supplies literal argv; `shell` explicitly requests shell interpretation. They are mutually exclusive. Omitting both uses tmux's default command. [`startup`]() entries send literal text followed by Enter; they do not wait for a command to finish. ## Validate, preview and apply This walkthrough uses a private server and an empty tmux configuration. The `seed` session keeps the server alive while the CLI creates the project. Inherited `TMUX` and `TMUX_PANE` do not select the endpoint. ```console $ export LIBTMUX_WORKSPACE_DIR="$(mktemp -d -t libtmux-ocaml-workspace.XXXXXX)" ``` Copying the file also makes its working directory the private directory: ```console $ cp examples/workspace/project.json "$LIBTMUX_WORKSPACE_DIR/project.json" ``` ```console $ opam exec -- tmux-workspace check "$LIBTMUX_WORKSPACE_DIR/project.json" ``` Expected output: `Valid workspace "ocaml-project": 2 windows, 3 panes`. ```console $ env -u TMUX -u TMUX_PANE tmux \ -S "$LIBTMUX_WORKSPACE_DIR/server" \ -f /dev/null \ new-session \ -d \ -s seed \ -- "exec cat" ``` ```console $ opam exec -- tmux-workspace plan \ --socket "$LIBTMUX_WORKSPACE_DIR/server" \ --require-existing \ "$LIBTMUX_WORKSPACE_DIR/project.json" ``` The plan lists session creation, settings, the split and layout, and the logs window. Applying it requires the original configuration: ```console $ opam exec -- tmux-workspace apply \ --socket "$LIBTMUX_WORKSPACE_DIR/server" \ --require-existing \ --json \ "$LIBTMUX_WORKSPACE_DIR/project.json" \ > "$LIBTMUX_WORKSPACE_DIR/receipt.json" ``` The receipt uses [`libtmux.workspace-receipt/1`](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/json/libtmux_json.mli). On success, [`status`]() is `succeeded`; `failed`, `uncertain` and `skipped` are empty. Each creation records the IDs returned by tmux. Check the resulting windows independently: ```console $ env -u TMUX -u TMUX_PANE tmux \ -S "$LIBTMUX_WORKSPACE_DIR/server" \ list-windows \ -t '=ocaml-project' \ -F '#{window_name}:#{window_panes}' ``` Expected output is `editor:2` followed by `logs:1`. Remove only the sessions this walkthrough created: ```console $ env -u TMUX -u TMUX_PANE tmux \ -S "$LIBTMUX_WORKSPACE_DIR/server" \ kill-session \ -t '=ocaml-project' ``` ```console $ env -u TMUX -u TMUX_PANE tmux \ -S "$LIBTMUX_WORKSPACE_DIR/server" \ kill-session \ -t '=seed' ``` ```console $ rm -f -- "$LIBTMUX_WORKSPACE_DIR/server" \ "$LIBTMUX_WORKSPACE_DIR/project.json" \ "$LIBTMUX_WORKSPACE_DIR/receipt.json" ``` ```console $ rmdir "$LIBTMUX_WORKSPACE_DIR" ``` Without `--require-existing`, `apply` can start a missing endpoint by creating the requested session. That server uses the normal tmux configuration. Closing the CLI's client leaves the server running; its appearance does not grant daemon ownership. `plan` on a missing endpoint checks the local tmux version and returns a bootstrap plan without creating the socket. ## Collisions and recovery An existing session name fails with exit 4 by default. `--append` adds new windows to that session and rejects window-name collisions. Append never changes the existing session's options or environment; omit those session settings from an append document. It preserves existing panes. Use `--json` on `check` or `plan` to obtain `document_digest`, then pass that value through `--expect-document` on apply. A changed document fails before endpoint I/O. The digest does not lock tmux state. Each apply builds and rechecks its own plan on the current connection generation. On failure or SIGINT, inspect the receipt before deciding what to do next. Default compensation kills only panes whose creation the journal proved, in reverse order. A foreign pane moved into a new window remains. `--keep-partial` retains proven creations instead. Cleanup failures and creations whose IDs could not be bound remain explicit. Never rerun an uncertain mutation solely because its caller was interrupted. The [retained-operation example](https://libtmux.org/en/ocaml/latest/guides/recovery/) demonstrates waiter recovery. The [native workspace example](https://github.com/libtmux/libtmux-ocaml/blob/205dfda49742592b479247049d186956e4a4cfe9/docs/workspace-recovery.md) interrupts an apply, inspects its journal after client cleanup and verifies compensation. Read the available journal, identify proven effects, then choose another explicit action. A saved JSON receipt describes observations; it is not an executable target or a transaction to replay. The execution journal is retained in process memory. `await` initiates steps only once for a particular execution handle; a new handle or CLI invocation starts a new execution. Operation IDs correlate receipts and do not suppress duplicates. Neither a retained handle nor an exported receipt is a durable write-ahead log. Compensation cannot undo files, network requests or other effects of the programs launched in panes. | Exit | Meaning | | --- | --- | | 0 | Validation, planning or apply succeeded | | 2 | Invalid arguments or document | | 3 | Required endpoint missing | | 4 | Collision, changed document or stale plan | | 5 | Failed, partial or uncertain apply; inspect the receipt | | 6 | Transport, capability or result-delivery failure | | 130 | SIGINT; the available journal is emitted within the cleanup bound | SIGINT does not undo admitted commands. Receipt output is bounded; a blocked or broken stdout cannot produce a successful exit. Forced process termination cannot promise a delivered receipt. Expected failures are distinct from Eio cancellation in the native API, which propagates cancellation to the caller. ## Bounds and native callers Documents use UTF-8 JSON with a 64 KiB input bound and depth limit 32. Unknown and duplicate properties fail. Configurations allow at most 64 windows and 256 panes; a plan allows at most 4096 actions. Plans and receipts have a 4 MiB output bound. Plans omit environment values, option values and startup text. They can still contain command arguments and paths; treat them as application data. For native code, use [`Libtmux_json.Workspace.of_string`](), [`Libtmux_workspace.Plan.make`](), and the backend's `Workspace.submit` or `Workspace.submit_bootstrap`. `inspect` reads the retained journal locally; `await` performs I/O. The application owns the Eio switch and scheduler. The [native bootstrap walkthrough](https://libtmux.org/en/ocaml/latest/guides/bootstrap/) starts one private session without a JSON dependency and captures its graph through the resulting client. See the [API reference](https://libtmux.org/en/ocaml/latest/reference/), [control outcomes](https://libtmux.org/en/ocaml/latest/guides/control/) and [compatibility evidence](https://libtmux.org/en/ocaml/latest/guides/compatibility/).