DrawingScripting API
Sheet6 / 11
Rev0.3.66
Date08-2026
Scale1:1
MaterialC99

Drive a running terminal from the shell

Every running rbterm can expose a control channel, and a client built into the same binary — rbterm @ <command> — sends it JSON commands and prints the reply. Commands execute on the terminal's own main thread, which is why the same channel also drives rbterm's ~240-check self-test suite.

Off by default Unix socket or in-band escape Length-prefixed JSON X25519 · AES-256-GCM
SEC 01

What it is

A tiny client, the same binary, one JSON command per invocation — a scripted action is exactly as safe as a keystroke.

Every running rbterm can expose a control channel. A tiny client built into the same binary — rbterm @ <command> — sends JSON commands to it and prints the reply. Commands are executed on the terminal's own main thread, so a scripted action is exactly as safe as a keystroke. The result: your terminal becomes a programmable surface you can wire into shell scripts, editor plugins, build tools, dashboards, and test suites.

# enable it once (Settings → Window → Scripting), then:
rbterm @ ls                                  # list windows / tabs / panes as JSON
rbterm @ send-text 'make && ./run\n'         # type a command into the active pane
rbterm @ get-text                            # read the pane's text back
rbterm @ screenshot --output ~/shot.png      # save a PNG of a pane
rbterm @ split --axis horizontal             # split the focused pane
rbterm @ launch --cwd ~/src htop             # new tab running a command

Enable it in Settings → Window → Scripting, or launch with --listen-on unix:/tmp/rb.sock. When enabled, rbterm exports RBTERM_LISTEN_ON into every shell it opens, so rbterm @ … inside a pane just targets its own window.

SEC 02

Command reference

Everything the API can do, grouped by what it touches. Flags are shown in the description; every command also accepts -m / --match.

Read & inspect

CommandWhat it does
lsDump every window, tab and pane as JSON — ids, titles, working directories, process ids, grid size, and any user variables.
get-textReturn a pane's on-screen text. --extent all includes scrollback; --ansi preserves colours and attributes as escape sequences.
get-colorsReturn a pane's full palette — foreground, background, cursor, and all 256 indexed colours.
screenshotRender any pane to a PNG (--output PATH) — visible or not, sized from its own grid. Pixel-accurate, no window capture.

Input

CommandWhat it does
send-textType text into a pane's input, with C-style escapes (\n \t \e \xNN).
send-keySend key chords by name — enter, ctrl+c, up, f5, alt+x, …

Windows, tabs & panes

CommandWhat it does
launch / new-tabOpen a new tab, optionally in a given --cwd and running a command.
new-window / splitSplit the focused pane (--axis vertical|horizontal).
focus-tab / focus-paneMove focus to a tab or pane by index or matcher.
close-tab / close-paneClose a tab or a single pane.
resize-paneGrow or shrink a split by an --increment.
resize-os-windowResize the OS window itself (--width, --height).

Appearance

CommandWhat it does
set-colorsRecolor a pane live — foreground, background, cursor, or any colorN.
set-tab-colorGive a tab an accent colour in the tab bar.
set-tab-title / set-window-titleRename a tab or a pane.
set-font-sizeSet the active tab's font size (absolute or +n/-n).
set-spacingAdjust padding around the grid.
set-background-opacitySet window background transparency (0.01.0).
disable-ligaturesTurn ligature shaping on or off.
scroll-windowMove the scrollback view — top, bottom, page-up, page-down, or by N lines.

Process & environment

CommandWhat it does
signal-childSend a signal (SIGTERM, SIGINT, SIGKILL, …) to a pane's process.
runRun a command and capture its output and exit code.
envSet environment variables for future shells.
set-user-varsAttach arbitrary key/value metadata to a pane — surfaced in ls and usable as a match target.
load-configReload settings from the config file.
goto-layout / actionSwitch layout (e.g. maximize the focused pane) or trigger a built-in action (next tab, clear, scroll home, …).
SEC 03

Targeting panes

A boolean matcher picks exactly which panes a command hits.

Pass -m / --match to any command. Match on id, title, cwd, pid, num (tab index), state, or a user var — and combine them with and, or, not, and parentheses. Without a matcher, commands act on the focused pane.

id
Pane id, as reported by ls.
title
Pane title.
cwd
The pane's working directory.
pid
The process id running in the pane.
num
Tab index.
state
Pane state — including the derived agent status.
var
A user variable attached with set-user-vars.
and · or · not · ( )
Boolean operators and parentheses, combining any of the above.
rbterm @ get-text   -m '(title:vim or id:3) and not cwd:/tmp'
rbterm @ send-text  -m 'all' '\x03'          # Ctrl-C every pane
rbterm @ set-colors -m 'cwd:/var/log' background=#2a0000
rbterm @ signal-child -m 'pid:34521' SIGTERM
SEC 04

Two ways to connect

Pick automatically — no setup for the common case.

Transport 01Local socket

For external scripts and other programs. rbterm listens on a per-process Unix socket with owner-only permissions. Point the client at it with --to unix:/path, or let it inherit RBTERM_LISTEN_ON. Requests and replies are length-prefixed JSON.

Transport 02In-band escape sequence

For driving the terminal from inside one of its own panes — and, crucially, over SSH. The request rides a terminal escape sequence in the pane's output stream; the controlling rbterm intercepts it and writes the reply straight back. No socket, no port, nothing to forward.

The client chooses for you: it uses the socket when one is reachable, and otherwise falls back to the in-band channel of the terminal it's running in. A reader thread parses the framing; every command that touches tabs, panes or the renderer runs on the main loop — so remote commands are as safe as keypresses, and a flood of them can't corrupt state.

Inside tmux, both automatic paths miss: RBTERM_LISTEN_ON isn't in the environment (tmux panes inherit the tmux server's frozen environment, not rbterm's), and tmux swallows the in-band escape unless passthrough is enabled. Recover the address from the tmux client process, which was launched from the rbterm pane and still has it.

CLIENT_PID="$(tmux display-message -p '#{client_pid}')"
ps eww -p "$CLIENT_PID" | tr ' ' '\n' | grep -E '^RBTERM_(PANE_ID|LISTEN_ON|EXE)='

# then target explicitly — and re-derive next time, the id can change
"$RBTERM_EXE" @ --to "$RBTERM_LISTEN_ON" ls -m "id:$RBTERM_PANE_ID"
SEC 05

Encrypted & authenticated

Lock the control channel to a password — end to end.

By default the control socket is owner-only and rbterm only opens it when you opt in. For anything stronger, set a remote-control password. rbterm then refuses every plain command and accepts only authenticated, encrypted ones: an ephemeral X25519 key exchange derives a shared key (hashed with SHA-256), and each command is sealed with AES-256-GCM. The password and a timestamp travel inside the encrypted payload, with a five-minute replay window and a constant-time check.

rbterm --rc-password 'hunter2'                  # or set it in the config file

# the client encrypts automatically when it has the password + key:
export RBTERM_RC_PASSWORD=hunter2
export RBTERM_PUBLIC_KEY=...                  # rbterm prints this on startup
rbterm @ ls                                   # → encrypted; plain commands are rejected
Key exchange
X25519 (ECDH)
Cipher
AES-256-GCM
Key derivation
SHA-256 of the shared secret
Replay guard
5-minute timestamp window
SEC 06

Test your terminal with it

The same API is rbterm's end-to-end test harness.

Because the API can both drive a pane and read it back, it doubles as a way to test the terminal itself. The bundled suite (make test) launches rbterm, feeds escape sequences through send-text, and asserts on get-text — verifying colour and attribute rendering, cursor positioning, screen erase, auto-wrap, scrollback, split/close behaviour, and live recolouring. Your own scripts can do the same.

rbterm @ send-text 'printf "\\033[1mBOLD\\033[0m\\n"\n'
rbterm @ get-text --ansi      # assert the bold SGR is present
SEC 07

A small example

Spin up a working layout from a script.

#!/bin/sh
# open a build pane and a log pane side by side, then tail the log
rbterm @ launch --cwd ~/project
rbterm @ split --axis vertical
rbterm @ send-text -m 'cwd:~/project' 'make watch\n'
rbterm @ send-text -m 'all' '\n'
rbterm @ set-tab-title 'build'
rbterm @ screenshot --output ~/build-layout.png