gotmuxcc is a drop-in replacement for
github.com/GianlucaP106/gotmux.
It mirrors the public API of the original library while internally using a
persistent tmux control-mode (tmux -C) connection instead of spawning a new
tmux process for every command. This dramatically reduces overhead when
issuing many tmux operations from Go.
- API-compatible with gotmux — public structs, method signatures, and return types match the original library so switching requires only a module path change.
- Persistent control-mode connection — a single
tmux -Csubprocess is kept alive for the lifetime of theTmuxhandle; all commands flow over stdin/stdout pipes. - Concurrency-safe router — commands are dispatched, correlated with
%begin/%end/%errorframes, and returned to callers without external locking. The router automatically absorbs the initial control-mode handshake so the first user command is never mismatched. - Notification stream —
Notifications()delivers every tmux control-mode notification (%window-add,%layout-change,%session-changed, …) as a typed value, without ever blocking the connection. The stream outlives reconnects (DisconnectedNotification,ReconnectedNotification), reports what a slow consumer missed (OverflowNotification) and passes unrecognised lines through (UnknownNotification). - Subscriptions —
Subscribe/Unsubscribewrappers forrefresh-client -Bwith helpers for session, window, and pane targets, plusSubscribeServerTopologyfor a server-wide change signal. - Flow control — pause-after mode, per-pane output control, and arbitrary
control flag management via
SetControlFlags. - Client & window sizing —
SetClientSize,SetWindowSize, andClearWindowSizefor programmatic layout control. - Custom format queries —
ListSessionsFormat,ListWindowsFormat, andListPanesFormataccept arbitrary-Fformat strings and-ffilters. - Target-string APIs —
Tmux.SplitWindow,Tmux.SelectLayout,Tmux.SelectPane,Tmux.SelectWindow, andTmux.GlobalOptionaccept raw tmux target strings, enabling bulk session/window/pane creation during restore without needing materialized objects. - Debug tracing — set
GOTMUXCC_TRACE=1(or component names likerouter,transport) to write structured debug logs to a trace file.
Requires Go 1.22+ and tmux 3.2 or later (structured queries rely on the n: format modifier).
Install the module:
go get github.com/atomicstack/gotmuxcc/gotmuxccCreate a client bound to the default tmux socket:
package main
import (
"fmt"
"github.com/atomicstack/gotmuxcc/gotmuxcc"
)
func main() {
tmux, err := gotmuxcc.DefaultTmux()
if err != nil {
panic(err)
}
defer tmux.Close()
info, err := tmux.GetServerInformation()
if err != nil {
panic(err)
}
fmt.Printf("tmux version: %s\n", info.Version)
}To point at a custom socket:
tmux, err := gotmuxcc.NewTmux("/path/to/socket")To run a particular tmux build, or give it a particular environment:
tmux, err := gotmuxcc.NewTmuxWithOptions("",
gotmuxcc.WithTmuxBinary("/opt/tmux/bin/tmux"),
gotmuxcc.WithEnv(os.Environ()))A client never starts a tmux server: the control client runs with -N, which makes that impossible. If nothing is running on the socket, the constructor still succeeds and returns a client with no connection: ListSessions reports no sessions, HasSession is false, and other commands return an error wrapping gotmuxcc.ErrNoServer. NewSession starts the server the way plain tmux would, and the control-mode connection is opened on the next command. The connection is also reopened transparently if tmux exits (for example when its last session is killed) and a session is created again later.
A command tmux rejects returns a *gotmuxcc.CommandError carrying tmux's
message (errors.As); a client that has been closed returns
gotmuxcc.ErrClosed; a socket with no server returns gotmuxcc.ErrNoServer
(errors.Is). Transport failures are none of these. Session.AttachSession
runs tmux attach-session on the calling process's terminal, as gotmux does.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for n := range tmux.NotificationsContext(ctx) {
switch n := n.(type) {
case *gotmuxcc.WindowAddNotification:
fmt.Println("new window", n.WindowID)
case *gotmuxcc.OverflowNotification, *gotmuxcc.ReconnectedNotification:
// state unknown: refetch whatever the stream was keeping current
}
}Each call to Notifications is an independent subscriber with its own buffer
(WithNotificationBuffer); nothing runs when nobody is listening. While a
subscriber exists the client redials on its own after tmux exits and announces
the new connection with a ReconnectedNotification.
tmux scopes some of what it sends to the session the control client is
attached to: %layout-change only covers that session's windows, windows in
other sessions arrive as %unlinked-window-*, and every refresh-client -B
subscription is evaluated against that session alone. A pane split or killed
in another session therefore produces no notification by itself.
SubscribeServerTopology registers a session-level subscription whose format
loops over every session, window and pane, so one connection still gets a
change signal for the whole server, at most once a second. The client also
sets no-detach-on-destroy so the connection survives its session being
destroyed while others exist (WithNoDetachOnDestroy(false) opts out).
The library is organized into five layers (top to bottom):
| Layer | Package | Responsibility |
|---|---|---|
| Public API | gotmuxcc/ |
Tmux, Session, Window, Pane types that mirror gotmux |
| Query Builder | gotmuxcc/query.go |
Fluent command assembly with #{...} format variables |
| Router | gotmuxcc/router.go |
Concurrency-safe dispatch, frame parsing, event streaming |
| Control Bridge | gotmuxcc/control_bridge.go |
Wires the transport interface to the concrete implementation |
| Control Transport | internal/control/ |
Launches and manages the tmux -C subprocess |
The controlTransport interface (defined in gotmuxcc/tmux.go) abstracts the
tmux pipe. In production it is satisfied by internal/control.Transport; in
tests it is replaced by fake or recording transports injected via
WithDialer().
Unit tests use fake control transports and do not require tmux:
make unitIntegration tests spin up isolated tmux servers with temporary sockets and require tmux to be installed:
make integrationOther targets:
make cover # Generate coverage report to coverage.out
make clean # Remove local .gocache and .gomodcacheSee docs/testing.md for details on running tests inside restricted sandboxes.
docs/api_inventory.md— one-to-one API compatibility catalog with gotmuxdocs/testing.md— test prerequisites, integration test setup, coverage notes
MIT License. See LICENSE for details.