Skip to content

Instantly share code, notes, and snippets.

@sdrik
Created July 12, 2026 09:47
Show Gist options
  • Select an option

  • Save sdrik/cfc2fb8fe6f4845f050083db7cd52df0 to your computer and use it in GitHub Desktop.

Select an option

Save sdrik/cfc2fb8fe6f4845f050083db7cd52df0 to your computer and use it in GitHub Desktop.
tmux Control Mode Protocol — complete commit-by-commit history (tmux -C/-CC), reconstructed from source & git history

tmux Control Mode Protocol — Complete History

A commit-by-commit history of tmux control mode (tmux -C / -CC), the text protocol used by iTerm2, WezTerm and similar integrations. For every protocol change: the introducing commit and the release it first shipped in.

Reconstructed from the source code and git history, not from documentation.

Method

Read-only git archaeology in the upstream tmux repository:

  • git log --diff-filter=A --follow on control.c / control-notify.c for the origin;
  • pickaxe git log --all -S/-G '<string>' for each %... notification;
  • release mapping via git tag --contains <hash> --sort=version:refname, taking the first real release tag (cross-checked with git describe --contains --match '[0-9]*').

Every hash, date, release and protocol string below was read directly from the repository.

Key files: control.c, control-notify.c, cmd-queue.c (the %begin/%end/%error guards), client.c (%exit, DCS handshake), cmd-refresh-client.c (-C/-A/-B/-f), cfg.c (%config-error), cmd-display-message.c (%message).

Authorship

Control mode is a joint effort of George Nachman (author of iTerm2, tmux@georgester.com — original design and most feature diffs) and Nicholas Marriott (tmux maintainer, nicm — integration and commits). Both names appear in the copyright header of control.c / control-notify.c from their very first versions, and nearly every incremental control-mode commit is annotated "from George Nachman".

Notes

  • Duplicate commits. The repo interleaves Nicholas Marriott's portable commits with their "Sync OpenBSD patchset" mirrors. Two changes therefore have an identical hash pair (same author/date/subject): notifications intro 41a448199247c90d; guard flags field a0172a6a662d4712. The first of each pair is cited below.
  • %flags-changed does not exist in tmux — zero occurrences across all refs. It is an iTerm2-side control-mode concept, never emitted by the tmux server.
  • No on-wire protocol version. Nothing in the control protocol advertises a version number. Only the doubled -CC variant emits a handshake: DCS \033P1000p on attach and ST \033\\ on exit.

Timeline by release

tmux 1.7 (2012) — genesis

Commit Date Release Protocol change
2942eca8 2012-06-18 1.7 Birth. Adds control.c, the CLIENT_CONTROL flag and the -C / -CC command-line options (the doubled -CC additionally enables termios handling). A line-by-line read loop executes each line as a tmux command. %begin / %end are bare (no arguments, emitted from cmd-list.c at this point), plus %error in line "%s": %s for an unparsable line. Deliberately undocumented.
41a44819 2012-09-03 1.7 control-notify.c — the first % notifications: %layout-change #{window_id} #{window_layout}, %window-add @%u, %window-close @%u, %window-renamed @%u %s, %unlinked-window-{add,close,renamed}, %session-changed $%u %s, %session-renamed $%u %s, %sessions-changed.
17bbf903 2012-09-25 1.7 %output %<pane> <data> — the pane output stream sent to the client. The piece that makes -CC usable as a real multiplexer transport.

tmux 1.8 (2013) — consolidation & modern framing

Commit Date Release Protocol change
3964309c 2013-02-23 1.8 Command queue rewrite: guard emission moves into cmd-queue.c (cmdq_guard).
70397e4a 2013-03-04 1.8 A failed command block now closes with %error instead of %end → the %begin / %end / %error triad.
208881a7 2013-03-04 1.8 %exit and %exit <reason> (the reason argument exists from the start). -CC handshake: DCS \033P1000p on attach, ST \033\\ on exit.
801d64a1 2013-03-21 1.8 refresh-client -C W,H — a control client sets its own size. (Subject says "-c" but the code adds uppercase -C.)
c41d92d2 2013-03-11 1.8 Guards gain a timestamp + command number: %begin <time> <number> (likewise %end/%error).
0ef24f99 2013-03-25 1.8 Only send the %end guard if %begin was sent.
7b4084a1 2013-03-11 1.8 Control mode is documented in the man page for the first time.

tmux 1.9 (2014) — final guard shape

Commit Date Release Protocol change
a0172a6a 2013-06-23 1.9 Guards gain a third flags argument → the modern form %begin <time> <number> <flags> (flags = !!(cmd->flags & CMD_CONTROL), i.e. the command itself came from a control client). Current emitter in cmd-queue.c: control_write(c, "%%%s %ld %u %d", guard, t, number, flags).
4c9f41f1 2013-10-10 1.9 flags passed to cmdq_guard as an argument (robustness when cmd is NULL).

tmux 2.5 (2017) — per-client notifications

Commit Date Release Protocol change
d98d3169 2017-05-04 2.5 Four notifications: %pane-mode-changed %<pane>, %window-pane-changed @%u %<pane>, %client-session-changed <client> $%u <name>, %session-window-changed $%u @%u.

tmux 2.6 / 2.9 — size handling (infrastructure)

Commit Date Release Protocol change
2dc9bfd9, 385bf084 2017-05 2.6 A control client no longer affects the session size until it has sent -C; session sizes are recalculated on refresh-client -C.
641191ab, 64699538 2018 2.9 The window-size option + WINDOW_SIZE_* machinery (windows larger than the client).

tmux 3.0a (2019) — first client flags

Commit Date Release Protocol change
f4d858e7 2019-07-10 3.0a refresh-client -F to set control-client flags; a single flag at first: no-output (CLIENT_CONTROL_NOOUTPUT). From Thomas Adam.

tmux 3.2 (2021) — the big overhaul (flow control, flags, subscriptions)

Work done mid-2020; all shipped in 3.2.

Flow control / pause:

Commit Date Release Protocol change
31e3f2d5 2020-05-21 3.2 Per-client offsets (used/acknowledged) into pane buffers; refresh-client -A <pane>:<state> to turn receiving a pane on/off.
a54a88ed 2020-06-01 3.2 control_write / control_flush buffering + a fair rate limit (GitHub #2217).
c5862089 2020-06-05 3.2 %pause %<pane> / %continue %<pane>; the pause-after[=<sec>] flag (via -f); CLIENT_CONTROL_PAUSED.
fddcad69 2020-06-10 3.2 %extended-output %<pane> <age> : <data> — a variant of %output carrying the output age when pause-after is set.
23d79cfd 2020-06-10 3.2 A 300-second limit of buffered data per control client.
50ee4142 2020-06-11 3.2 -A option to pause a pane manually.

Generalized client flags:

Commit Date Release Protocol change
0bdbf47e 2020-05-14 3.2 The active-pane flag.
cf9baddd 2020-05-16 3.2 refresh-client -f (generalized to any client; -F becomes an alias); the read-only and ignore-size flags.
2372b0fd 2020-06-18 3.2 The wait-exit flag: the client waits for an empty line before exiting (avoids stray commands ending up in the shell).

Subscriptions & detach:

Commit Date Release Protocol change
66d5e5de 2020-07-06 3.2 refresh-client -B <name>:<what>:<format> (what = session / @window / %pane) + %subscription-changed <name> $<sess> <@win|-> <idx|-> <%pane|-> : <value>. Subscribe to a format instead of polling (GitHub #2242).
8b800b41 2021-03-16 3.2 %client-detached <client> (from Mohsin Kaleem).

tmux 3.3 (2021) — per-window size

Commit Date Release Protocol change
fd756a15 2021-08-27 3.3 refresh-client -C @<win>:<W>x<H> — a hard per-window size limit (sscanf "@%u:%ux%u"); @<win>: alone clears it (GitHub #2594). The old -C W,H / WxH form still works.

tmux 3.4 (2023) — buffer & message notifications

Commit Date Release Protocol change
a888ce99 2022-06-20 3.4 %config-error <msg> — config file errors sent to the control client instead of a pane (GitHub #3193).
7c2dcd72 2022-08-15 3.4 %paste-changed (initial name). From George Nachman (GitHub #3302).
8edece2c 2022-10-28 3.4 Renamed to %paste-buffer-changed <name> + new %paste-buffer-deleted <name>. (Both final forms first exposed in 3.4.)
93b1b781 2023-02-05 3.4 %message <msg>display-message works for control clients (GitHub #3449).

tmux 3.5 (2025) — peripheral

Commit Date Release Protocol change
093b5a55 2024 3.5 refresh-client -r — OSC 10/11 (color) responses; peripheral to the core protocol.

One-line recap per milestone

  1. 2942eca8 — 1.7 — skeleton: -C/-CC, bare %begin/%end, %error in line.
  2. 41a44819 — 1.7 — notifications: %layout-change, %window-*, %session-*.
  3. 17bbf903 — 1.7 — %output.
  4. 70397e4a — 1.8 — %error as the failure guard.
  5. 208881a7 — 1.8 — %exit [reason] + DCS 1000p/ST handshake for -CC.
  6. 801d64a1 — 1.8 — refresh-client -C W,H.
  7. c41d92d2 — 1.8 — guards %begin <time> <number>.
  8. a0172a6a — 1.9 — guards %begin <time> <number> <flags> (modern form).
  9. d98d3169 — 2.5 — %pane-mode-changed, %window-pane-changed, %client-session-changed, %session-window-changed.
  10. f4d858e7 — 3.0a — refresh-client -F no-output.
  11. 31e3f2d5/a54a88ed/c5862089/fddcad69 — 3.2 — flow control: -A, %pause/%continue, pause-after, %extended-output.
  12. cf9baddd/0bdbf47e/2372b0fd — 3.2 — refresh-client -f + flags active-pane/read-only/ignore-size/wait-exit.
  13. 66d5e5de — 3.2 — subscriptions -B + %subscription-changed.
  14. 8b800b41 — 3.2 — %client-detached.
  15. fd756a15 — 3.3 — refresh-client -C @win:WxH (per-window size).
  16. a888ce99/8edece2c/93b1b781 — 3.4 — %config-error, %paste-buffer-changed/-deleted, %message.

Verification

  • Each release confirmed with git tag --contains <hash> --sort=version:refname (first real release tag).
  • Protocol strings quoted from the current tree: control-notify.c:52 (%layout-change), cmd-queue.c:832 (guards), client.c:425-439 (%exit/ST), cfg.c:229 (%config-error), cmd-display-message.c:151 (%message).
  • %flags-changed: absent (0 occurrences across all refs) → an iTerm2 concept, not tmux.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment