Skip to content

tmux: prefer a newer tmux binary in the bin/tmux wrapper when available#76

Merged
katosh merged 3 commits into
mainfrom
dominik/tmux-wrapper-prefer-newer
Jul 5, 2026
Merged

tmux: prefer a newer tmux binary in the bin/tmux wrapper when available#76
katosh merged 3 commits into
mainfrom
dominik/tmux-wrapper-prefer-newer

Conversation

@katosh

@katosh katosh commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Problem

On hosts whose system tmux is an ancient build — e.g. tmux 2.6 (2017), still the default on several enterprise Linux baselines — Claude Code (and other modern full-screen TUIs) render with intermittent garbling/tearing inside the sandbox. The dominant cause is synchronized output (DCS ?2026h/l), which batches a full-frame redraw so partial paints never show; tmux only learned to proxy it correctly in 3.2–3.3. RGB negotiation (terminal-features, 3.2) and DCS passthrough (allow-passthrough, 3.3) are in the same boat. No 2.6-era config fully fixes the rendering — the durable fix is to run a newer tmux.

But bin/tmux hardcodes /usr/bin/tmux, so even when the user installs a modern tmux (e.g. brew install tmux → 3.5+), the sandbox keeps using the old one.

Change

bin/tmux now picks the highest-version tmux among the system binary and the common Homebrew locations ($HOMEBREW_PREFIX/bin, ~/.linuxbrew/bin, /home/linuxbrew/.linuxbrew/bin, /opt/homebrew/bin, /usr/local/bin), falling back to /usr/bin/tmux when nothing newer is present. Details:

  • Recursion-safe — the wrapper shadows tmux on PATH, so each candidate is readlink -f-compared against the wrapper itself and skipped if it resolves to this script.
  • Version-compared, not first-match — a candidate is only chosen if its tmux -V parses to a higher major*100+minor than the current best, so an equally-old binary in /usr/local/bin never displaces the system one.
  • SANDBOX_TMUX_BIN escape hatch — set it to pin a specific binary regardless of version.

After brew install tmux outside the sandbox, the wrapper uses it automatically — no further config.

Note on the injected conf

The companion half — sandbox-tmux.conf erroring with invalid option: allow-passthrough / extended-keys / terminal-features on tmux < 3.3 — was already fixed in v0.13.0-rc.2 by guarding the version-only options with set -q (verified on tmux 2.6: a source-file of the guarded conf exits 0 with zero warnings, vs. 4 errors unguarded). This PR is the second, durable half: it makes the wrapper actually run a modern tmux when one is available, fixing the rendering itself rather than just silencing the config warnings. Installs still on 0.13.0-rc.1 will continue to emit those conf warnings until upgraded to ≥ 0.13.0-rc.2.

Testing

  • bash -n bin/tmux — clean.
  • Selection logic unit-tested with a stub reporting tmux 3.5a: chosen over the 2.6 system binary (305 > 206); SANDBOX_TMUX_BIN override honored; self-reference skipped.
  • End-to-end on a host with only tmux 2.6: wrapper falls back to /usr/bin/tmux, launches a session cleanly (exit 0, no warnings), prefix C-a from the conf applied, isolated -L socket throughout.
  • escape-time (and the rest of the conf) untouched — no regression to paste-timing behavior.

Operator activation

brew install tmux          # outside the sandbox → ~/.linuxbrew/bin/tmux (3.5+)
# restart the sandbox; bin/tmux now prefers it automatically

katosh added 3 commits June 30, 2026 00:19
The wrapper hardcoded /usr/bin/tmux, which on many hosts is an ancient
build (e.g. 2.6) that lacks the escape-sequence proxying modern
full-screen TUIs rely on (synchronized output, RGB negotiation, DCS
passthrough), so such TUIs render with intermittent garbling/tearing.

Pick the highest-version tmux among the system binary and common
Homebrew locations (recursion-safe: never re-invoke the wrapper itself),
falling back to /usr/bin/tmux when nothing newer is installed. Add a
SANDBOX_TMUX_BIN escape hatch to pin a specific binary.

After 'brew install tmux' outside the sandbox, the wrapper uses it
automatically — no further config.
@katosh

katosh commented Jul 5, 2026

Copy link
Copy Markdown
Owner Author

Reviewed adversarially and re-verified the selection logic empirically (exec-override harness sourcing the wrapper, stub tmux binaries):

  • Prefer-newer works: a 3.5a stub in ~/.linuxbrew/bin is chosen over a 2.6 system stub.
  • Fallback intact: with no candidate anywhere, the system binary is exec'd — behavior identical to before the PR.
  • Pin honored: SANDBOX_TMUX_BIN wins over a newer candidate, no version check.
  • Strict ordering: an equal-version (2.6 vs 2.6) candidate does NOT displace the system binary.
  • Recursion-safe: a candidate symlink resolving to the wrapper itself is skipped (falls back correctly).
  • HOMEBREW_PREFIX candidate picked up when set.
  • Version parser: tmux 3.5a → 305, tmux 2.6 → 206, tmux next-3.4 → 304, tmux 3.3a-rc2 → 303, tmux master → correctly rejected (candidate skipped rather than mis-ranked).

Security posture: no weakening — the wrapper execs a user-reachable binary inside the sandbox, which any sandboxed process could do anyway; SANDBOX_TMUX_BIN crosses no privilege boundary.

Two additions pushed to the branch (merge of main + one commit):

  1. CHANGELOG entry under [Unreleased] / ### Fixed — the repo logs every behavioral change, and this one was missing. Branch was also pre-0.13.1-release, so I merged origin/main first to write the entry against the released layout (no force-push).

Two minor notes, non-blocking:

  • SANDBOX_TMUX_BIN is documented only in the wrapper's comment; a line in docs/ (or sandbox.conf's advanced section) would make it discoverable. Note it must be exported in the launch environment to reach the wrapper (a sandbox.conf shell variable won't forward) — worth saying wherever it gets documented, especially given the export-forwarding discussion in issue #75.
  • A set-but-non-executable SANDBOX_TMUX_BIN is silently ignored (falls through to the auto-scan). A one-line stderr warning would make a typo'd pin visible instead of silently running a different tmux.

@katosh katosh merged commit 4977fa0 into main Jul 5, 2026
11 checks passed
katosh added a commit that referenced this pull request Jul 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant