Skip to content

Pushing terminfo over ssh’s stdin

 2 min Fixing ‘unknown terminal type’ by streaming terminfo through ssh’s stdin and compiling it remotely, with no separate copy step.
Published

Apr 9, 2019

You ssh into a server and full-screen programs fail because it has never heard of your terminal. One fix is to copy the terminfo source, connect again, compile it, and remove the source. SSH already has the transport: its standard input.

The function streams the source to a temporary file on each host and compiles it there:

terminfo-ssh () {
  local source="${ELLIPSIS}data/profile/iterm2/iterm2-direct-compat.terminfo"
  for host in "$@"; do
    ssh "$host" -- 'set -eu
      tmp=$(mktemp)
      cleanup() { rm -f -- "$tmp"; }
      trap cleanup EXIT
      trap "exit 129" HUP
      trap "exit 130" INT
      trap "exit 143" TERM
      cat >"$tmp"
      tic -x "$tmp"
    ' <"$source"
  done
}

The local <file redirect makes the terminfo source SSH’s standard input. The remote shell copies that stream into a private temporary file, compiles it with tic, and removes the source on success, failure, or a caught HUP, INT, or TERM. The signal handlers exit with conventional 128+signum statuses; the separate EXIT trap performs the removal instead of swallowing termination. No copy command or second connection is required; the only persistent result is the compiled entry under the remote user’s terminfo directory. Looping over "$@" seeds several hosts with the same source.

tic -x is the flag that matters. Without -x, tic drops any capability outside the standard set—which for a modern terminal means losing the truecolor (24-bit RGB) capabilities, the exact ones you pushed the file to get. -x keeps the extended entries.

What it deploys is a hand-built -compat terminfo. The description advertises only 16 colors (colors#0x10, pairs#0x100) while still carrying the full truecolor capability set—Tc, plus both spellings of the RGB setters (setrgbf/setrgbb and setb24/setf24, so tmux, ncurses, and vim each find the one they probe for). The reason to lie small in the number is application compatibility. The repository first changed colors#0x7fff to 8—“Advertise 8 colors (Tc flag implies the others)”—then to 16. The ncurses FAQ documents the older signed-16-bit capability boundary and applications that mishandle large advertised color counts. The file reports 16 while supplying the extended RGB capabilities separately.

The transport lives in my dotfiles. The example adds mktemp and cleanup traps to the original function, so concurrent runs do not share a source file and failed compiles remove it too.