#2567·warpgate

Option to record only user input in terminal session recordings (drop target output)

Author: DharmendraChoudhary67Created Sep 10, 2026Updated Sep 16, 2026

Problem

Terminal recordings store every byte of target output. A single cat of a large file inside a recorded SSH session writes the whole file into data.ndjson (base64, so roughly 1.3x the file size). On a busy bastion this fills recording storage quickly, and the only way to bound it today is to turn recording off entirely. It is the same storage concern as #1945 (record_scp), but for interactive output.

Measured on a test instance: pulling a 54 MB file through one SSH session produced a 72 MB recording.

Proposal

A global parameter next to record_scp and record_desktop_keyboard_input:

  • record_terminal_output (bool, default true). When off, TerminalRecorder keeps the Input stream (plus PTY resizes) and drops Output / Error. A single gate in TerminalRecorder::write covers native SSH, web SSH and Kubernetes exec. The SSH command detector still receives output, so the Shell command log entries are unchanged.
  • Admin UI switch under Session recordings, wired like the desktop keyboard toggle (migration + parameters API field).

With the flag off, the same 54 MB session leaves a 194-byte recording holding exactly the typed bytes.

Playback

The terminal player skips Input items, since the target normally echoes them, so an input-only recording currently plays as an empty screen. I would like to fix that in the same change:

  • the recorder writes an {"type":"input_only", ...} entry to index.ndjson when the flag is off (additive; existing players ignore unknown entry types);
  • the player renders the Input stream for such recordings (Enter as newline, backspace erases), in replay and live, with a short "input only" notice.

Open question: live view

Live view is a tail of the recording file, so with the flag off admins also lose target output live. A possible extension would broadcast output to live viewers without writing it, using an in-memory screen snapshot for late joiners and for lag recovery instead of file replay. That breaks the current "live equals file" invariant, so I would rather ask first: is that something you would take, or should live stay a strict tail of the file?

I have the parameter and gate implemented and tested (Rust unit tests, a Python e2e test, and a live deployment) and can open a PR, with or without the player part, depending on what you prefer.