Skip to content

Sideband overview

Beyond stdin/stdout/stderr, τ-mux opens three extra file descriptors for every shell. Scripts running inside the terminal can use them to render structured content (images, SVG, HTML, interactive widgets) into floating canvases — without disturbing the regular terminal output stream.

fdDirectionPurposeFormat
3script → terminalMetadata: panel definitions, updates, clearsJSONL (one JSON object per line)
4script → terminalBinary data referenced from fd 3 (PNG bytes, etc.)raw bytes, length-prefixed via byteLength
5terminal → scriptEvents: clicks, drags, resizes, system errorsJSONL

The channel layout is published in the HYPERTERM_CHANNELS env var as JSON, so scripts can adapt if the layout ever changes.

OSC sequences (the iTerm2 way) are simple but tightly bound to the terminal text stream — they steal escape codes, are length-limited in many shells, and break if anything else is reading stdout (e.g. tee, pipes). Sideband fds:

  • Don’t compete with stdout.
  • Have native binary support (no base64 round-trip).
  • Have a back-channel (fd 5) for the terminal to talk to the script.
  • Survive pipes — only the original child sees the fds; piped commands don’t.

The trade-off is platform support: only programs running directly inside τ-mux can use the channels. Anything launched via SSH or inside Docker doesn’t see them — and the client libraries no-op gracefully in that case.

Python:

from hyperterm import ht
ht.show_image('photo.png', x=100, y=50, draggable=True)
ht.show_html('<button onclick="alert(1)">Click me</button>', interactive=True)
for event in ht.events():
print("got:", event)

TypeScript:

import { ht } from "./hyperterm";
const id = ht.showSvg('<svg width="200" height="200">…</svg>', { x: 100, y: 50 });
ht.update(id, { x: 200 });
ht.onEvent((e) => console.log(e));

Both libraries are safe no-ops when not running inside τ-mux — detection is a simple HYPERTERM_PROTOCOL_VERSION env check.