feat(multi): tmux keys to toggle broadcast and exclude single panes

In windows opened by netssh multi, prefix S toggles broadcast for all
panes and prefix e excludes the current pane (a pane-level
synchronize-panes off, shown as [SOLO] in the border) or includes it
again. netssh sync --pane does the same from a shell.

S and e are unbound in a default tmux (E would be "spread evenly").
netssh writes the bindings to a small tmux config and sources it, binds
a key only if the user's tmux.conf does not use it (netssh's own earlier
binding is refreshed), and the bindings act only in windows marked
@netssh. multi.sync_key and multi.solo_key change the keys or turn them
off (none); also in the configurator. zellij syncs whole tabs only, so
excluding a pane is tmux-only.

testenv: the render test presses the keys through the xterm client
(send-keys -K) and checks [SOLO], that broadcast skips the excluded
pane, that S toggles, and that other windows are unaffected. The client
gets GNU grep: BusyBox grep -oF counted only one match per line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Sebastian UnterschützandClaude Opus 5.5 committed 2026-11-14 13:32:32 +01:00
1 parent 699b9469b6
commit 396d489187
14 files changed
+342 -31

No files matched your search

+13 -6
View File
@@ -150,7 +150,7 @@ A menu then gives access to every other setting; open only the sections you want
| SSH options | `proxy_jump`, `host_key_checking`, `target`, `address_family`, `extra_args` (one per line), `options` (`Key=Value` per line), `forwards` (`NAME L\|R\|D SPEC` per line) |
| SSH rules | add, edit, remove `ssh.rules` — match by tag, instance, kind, name pattern, each with its own SSH options |
| Static hosts | add, edit, remove hosts that are not in NetBox, each with user, port, tags, and SSH options |
| Multi-session | multiplexer (auto, tmux, zellij), panes per window, groups (`NAME: sel1, sel2` per line) |
| Multi-session | multiplexer (auto, tmux, zellij), panes per window, tmux keys for broadcast and excluding a pane, groups (`NAME: sel1, sel2` per line) |
| Resolver & cache | strategies, management subnets, interface name, hostname domain, cache TTL, cache file |
| Shortcuts & command alias | domain suffixes, hyphen stripping, rewrite rules (`REGEX => REPLACEMENT` per line), `shell.alias` |
| Completion | `completion.max_results` |
@@ -237,6 +237,7 @@ Every key, its default, and the section that explains it. Keys marked *layered*
| `groups` | map of lists | `{}` | Named host lists for `netssh multi @NAME`; entries are hosts or selectors. |
| `multi.multiplexer` | `auto` \| `tmux` \| `zellij` | `auto` | Which multiplexer `netssh multi` uses. |
| `multi.panes_per_window` | int | `9` | More hosts open in further tmux windows or zellij tabs. |
| `multi.sync_key`, `multi.solo_key` | tmux key \| `none` | `S`, `e` | Keys after the tmux prefix: broadcast to all panes, exclude/include the current pane. |
### Environment variables
@@ -366,7 +367,7 @@ A hint is shown during `netssh configure` if a legacy v1 token is entered.
| `netssh tunnel [netssh flags] [ssh options] HOST FORWARD...` | Port forwards without a shell — see [Port forwarding](#port-forwarding). |
| `netssh hosts [list] \| add NAME ADDRESS [--user U] [--port P] [--tag T]... [--replace] \| remove NAME` | Manage static hosts — see [Static hosts](#static-hosts). |
| `netssh multi [--sync] [--kind K] [--tmux\|--zellij] [-l U] [--jump H\|--no-jump] HOST\|tag:T\|cluster:C\|@GROUP...` | Several hosts side by side — see [Multiple servers](#multiple-servers-in-one-window). |
| `netssh sync` | Toggle typing into all panes of the current tmux window or zellij tab. |
| `netssh sync [--pane]` | Toggle typing into all panes of the current tmux window or zellij tab; `--pane` excludes/includes the current tmux pane. |
| `netssh configure` | Setup wizard. |
| `netssh cache list \| refresh [--tag T] [--kind device\|vm] [--instance I] \| clear` | Inspect and manage the host cache. |
| `netssh search QUERY` | Print matching cached hostnames, one per line. |
@@ -581,6 +582,8 @@ groups:
multi:
multiplexer: auto # auto | tmux | zellij
panes_per_window: 9 # more hosts open in further windows/tabs
sync_key: S # tmux: prefix S = broadcast on/off (none = no key)
solo_key: e # tmux: prefix e = exclude/include this pane
```
**tmux and zellij.** With `auto`, netssh uses the multiplexer you are in; otherwise tmux, then zellij, whichever is installed (`--tmux` / `--zellij` choose explicitly).
@@ -590,7 +593,11 @@ multi:
| Inside a session | new window in the current session | new tab in the current session |
| Outside | new session `netssh-NAME`, attached | new session from a generated layout |
| Layout | tiled; pane borders show the host | grid; pane titles show the host |
| Broadcast | `--sync`, or `netssh sync` / `Ctrl+b :setw synchronize-panes` to toggle; borders show `[SYNC]` | `--sync` inside zellij, or `Ctrl+t` then `s` (also `netssh sync`) |
| Broadcast to all panes | `--sync`; toggle with `Ctrl+b S` or `netssh sync` | `--sync` inside zellij; toggle with `Ctrl+t` then `s` or `netssh sync` |
| Exclude one pane | `Ctrl+b e` (again: include) or `netssh sync --pane` | not possible — zellij syncs whole tabs |
| Shown in the border | `[SYNC]` receives broadcast, `[SOLO]` excluded | `(SYNC)` in the tab name |
**Keys in tmux.** `S` and `e` are free in a default tmux (`E` would be "spread panes evenly"). netssh binds them after your prefix when it opens panes, but only if your own `tmux.conf` does not already use them — then it keeps your binding and says so. They act only in windows opened by `netssh multi`; elsewhere they just show a message. Change or turn them off with `multi.sync_key` / `multi.solo_key` (`none`), e.g. `sync_key: C-s` for `prefix Ctrl+s`.
**In the TUI**, mark hosts with `Space` (or all listed hosts with `Ctrl+A`) and press `Enter` to open them together.
@@ -882,8 +889,8 @@ netssh fsnbx1 # also works as shell alias: fsnbx1
```sh
netssh multi --sync cluster:pve-fsn1 # every node in one window, typing goes to all
# … run apt upgrade on all nodes, then turn broadcast off for per-node work:
netssh sync # from another pane, or Ctrl+b :setw synchronize-panes
# … run apt upgrade on all nodes; Ctrl+b e on a node that needs something
# different excludes just that one, Ctrl+b S turns broadcast off for all
```
**Deploy a file and restart a service**
@@ -1122,7 +1129,7 @@ End-to-end tests run against real services in a local Docker environment — two
```sh
testenv/up.sh # build and start (first start ~6 min)
testenv/test.sh # 178 checks: connect, strategies, instances, shortcuts, rules, sftp/scp, tunnels, notes, shells, TUI, configurator, multi with tmux and zellij, and what xterm renders (screenshots + OCR)
testenv/test.sh # 185 checks: connect, strategies, instances, shortcuts, rules, sftp/scp, tunnels, notes, shells, TUI, configurator, multi with tmux and zellij, and what xterm renders (screenshots + OCR)
testenv/down.sh
```