Architecture
You don’t need anything on this page to use libtmux — every port’s objects and methods work with no setup, and Server, session, window, pane already covers the shared hierarchy, its stable IDs, and what differs between a live-refreshing handle and an immutable snapshot. This page is for when you’re curious how each port lays out the code underneath, and — the more interesting question — where the behavior actually lives, because it isn’t in the same place in every port.
Where behavior lives: on the object, or through the serverLink to section
In Python, TypeScript, Go, Rust, Java, .NET, and C++, a Session, Window,
or Pane is an object (or, in Go and Rust, a value with methods) that you
call directly: pane.send_keys(...), pane.sendKeys(...),
pane.SetOption(...), window.split(). The object carries enough of its
own identity — an ID, a reference back to its server — to act on itself.
The same three operations — type into the pane, set an option on it, kill
it — called directly on the object in each of these seven:
pane.SendKeys(ctx, tmux.SendKeysRequest{Command: &cmd})pane.SetOption(ctx, "automatic-rename", "off", tmux.SetOptionOptions{})pane.Kill(ctx)Swift is the outlier, and deliberately so: Session, Window, and Pane
are plain Sendable value types holding little beyond an ID and a few
positional fields (see Format-token fields for exactly
how little). There is no pane.sendKeys(...) or pane.kill() — every
operation is instead a method on Server that takes the value as a
parameter, the same three operations as above:
The practical effect is that Server is the one thing you hold onto in a
Swift program; a Session or Pane you got back from a snapshot() is
inert data you hand back to the server that produced it, not a handle you
call things on. Every other port’s Server is also where you start, but
Session/Window/Pane stay live actors once you have one.
A generated data table under a hand-written surfaceLink to section
Every port’s typed surface sits on top of the same two tmux primitives:
targets (-t, which command reaches which object) and FORMATS, tmux’s own
#{...} template variables that describe an object’s state. None of the
eight hand-maintains the list of format tokens or options as prose comments
— each generates a scope- and version-tagged table from tmux’s own source or
documentation, then hand-writes a thin, idiomatic accessor layer over it:
| Port | Generated table | Hand-written surface |
|---|---|---|
| Python | libtmux.constants (FORMATS, gated by scope and tmux version) | dataclass fields on Obj (libtmux.neo), None when a gate excludes a token |
| TypeScript | packages/libtmux/src/_generated/format_fields.ts ({ scope, since, token } per row) | camelCase aliases on Pane/Session/Window (packages/libtmux/src/_generated/field_aliases.ts) |
| Go | format_generated.go, option_generated.go (built by internal/generate/formats) | (value, bool) accessor methods — Go’s own “comma ok” idiom for a gate |
| Rust | formats.rs’s per-token macro rows (token, wire name, scope, kind, since version, absent-handling) | typed methods returning Option<T> |
| Java | (typed field accessors generated for the query layer — see Pane_/Session_ in Filtering and queries) | Optional<T> for fields introduced after a port’s tmux floor |
| C++, Swift | — (neither generated the full ~200-token catalog; see below) | a fixed, curated set of non-optional struct/class fields |
| .NET | a snapshot dictionary read at capture time | typed properties that throw IncompleteSnapshotException for a field the capture didn’t request, rather than gating on tmux version per field |
C++ and Swift are both exceptions here, for related but not identical
reasons. Swift’s Session/Window/Pane are small value types rather than
an in-process model of the whole server, so it picked a small, fixed set of
fields (index, width, height, isActive, currentCommand,
currentPath, the four edge flags) instead of exposing tmux’s full
format-token surface as optional properties. C++ does the same for its own
reasons — a fixed nineteen-field kFields array per type, none of them
std::optional — and reaches anything outside that set through
pane->expand("#{...}") rather than a struct member. A token outside
either curated set is reached ad hoc, not through a property — see Format-
token fields for the two mechanisms side by side. This
is a real, verified design choice in both ports, not a page that hasn’t
been written yet.
Module layout, by portLink to section
Each port’s own top-level organization, to orient yourself before opening its source:
- Python — one module per tier (
libtmux.server,.session,.window,.pane,.client), pluslibtmux.commonfor shared plumbing,libtmux.neofor the dataclass query layer,libtmux.options/libtmux.hooksas mixins every tier includes, andlibtmux.excfor the exception hierarchy. - TypeScript —
packages/libtmux/src/{server,session,window,pane,client}.tshold the public classes; nearly everything they call into lives under_internal/operations/(one file per concern —pane_io.ts,hooks.ts,options.ts,topology.ts) and_generated/(the format/option/hook catalogs above). Separate packages in the same monorepo cover workspaces (@libtmux/workspace) and an MCP server. - Go — a single
tmuxpackage, split by concern into many files rather than many packages (model.gofor the core structs,lifecycle_kill.go,pane_capture.go,pane_geometry.go,hierarchy.go,plan_server.gofor folded invocations);tmuxqis a separate package for predicate queries over an already-read snapshot (Filtering and queries), andworkspacea separate one again. - Rust —
crates/libtmux/src/{server,session,window,pane}/directories, each split into files by concern (asettings.rsper tier holding that tier’s options-and-hooks methods, matching the pattern in Options and hooks);hooks.rs,options.rs, andformats.rshold the shared, scope-generic machinery those call into. Workspaces and the MCP server are separate crates in the same workspace. - Java —
io.github.libtmuxholdsServer,Session,Window, andPaneasfinalclasses; each exposes its option and hook tables through.options()/.hooks()accessor methods returning a separateOptions/Hooksview scoped to that object, rather than mixing those methods directly into the entity class the way Python and Go do.Session_,Window_, andPane_are a parallel set of typed-field classes that exist only for the query layer. - .NET —
src/LibTmux/gives every entity its own name (Pane.cs,Session.cs, …) but splits each into severalpartial classfiles by concern rather than by inheritance —Pane.Capture.cs,Pane.Input.cs,Pane.Relations.cs,Pane.Scopes.cs,Pane.Topology.cs, and so on all contribute to onePanetype.Options/Hooksare reached through.Options/.Hooksproperties, structurally the same idea as Java’s accessor methods. - C++ —
include/libtmux/entities.hppdeclaresSession,Window, andPanetogether as value types (private Rowbases), with their method bodies insrc/rather than the header;server.hpp,options.hpp, andcapabilities.hppare separate headers. A privatetestingcomponent (include/libtmux/testing/) ships separately from the library proper — see Context managers for what it’s for. - Swift —
Sources/LibTmux/Server.swiftis the hub every operation extends;Session.swiftandPane.swiftdeclare the thin value types,Snapshot.swiftholds the relationship queries (Traversal), andOptions.swift,PaneInteraction.swift, andMutations.swiftareextension Serverfiles grouping options/hooks, send/capture, and kill respectively — all reachable only throughServer, per the section above.
Naming conventionsLink to section
Every port ports tmux’s own dash-separated command and token names
(new-window, automatic-rename) into its own identifier convention:
Python and Rust use snake_case (new_window, automatic_rename read
through get_option("automatic-rename") — the option name stays
dash-separated since it’s a string tmux itself defines; only the method
name changes). TypeScript, Java, .NET, and Swift use camelCase for methods
(sendKeys, setOption) while, again, leaving tmux’s own option and hook
names as the dashed strings tmux expects. Go and C++ use PascalCase /
snake_case methods respectively, following each language’s own
conventions rather than tmux’s.