How Tabby's Zmodem File Transfer Works Over SSH: A Deep Dive into the Implementation

Tabby implements Zmodem file transfer over SSH as a terminal-session middleware that intercepts Zmodem handshake sequences in the SSH data stream, automatically triggering native file pickers for uploads or save dialogs for downloads while streaming file contents through the Zmodem protocol.

Tabby, the popular open-source terminal emulator maintained by Eugeny, provides seamless file transfer capabilities over SSH using the legacy Zmodem protocol. Unlike standalone SFTP clients, Tabby's implementation operates directly within your active terminal session, responding to standard rz and sz commands without requiring additional connections. This article examines the TypeScript source code in the Eugeny/tabby repository to reveal how the terminal detects, manages, and executes bidirectional file transfers through three distinct architectural layers.

Middleware Architecture and Detection

Tabby's Zmodem support functions as a terminal-session middleware that sits between the SSH stream and the terminal UI. The core logic resides in tabby-terminal/src/features/zmodem.ts, where the ZModemMiddleware class monitors every byte transmitted through the session.

The detection process begins with ZModemMiddleware.feedFromSession, which constantly watches incoming SSH data for Zmodem signatures. When the internal ZModem Sentry recognizes a start-of-transfer sequence, it invokes the on_detect callback, triggering process() to create a new session【L51-L57】. The detection.confirm() method instantiates a session object that determines whether the remote side intends to send or receive files based on the Zmodem handshake【L75-L81】.

Uploading Files to Remote Hosts

When the remote host executes the rz command (indicating it is ready to receive), Tabby detects this as a send session (type === 'send'). The middleware immediately invokes the platform layer to open a native file-picker dialog.

According to the source in tabby-electron/src/services/platform.service.ts, the startUpload() method (lines 203-215) returns an array of FileUpload objects representing the user's selections. For each file, sendFile() constructs a Zmodem offer containing the filename, size, mode, and byte counters【L68-L75】. Once the remote side accepts the offer, Tabby streams the file in chunks using transfer.read() and xfer.send(chunk), updating a live progress indicator in the terminal UI【L86-L95】.

// Triggered when user initiates upload via UI or remote runs 'rz'
const uploads: FileUpload[] = await this.platform.startUpload({multiple: true});
// Each file is offered to the remote session via sendFile()

Downloading Files from Remote Hosts

Conversely, when the remote host executes sz filename to send a file, Tabby detects a receive session (type === 'receive'). The middleware listens for zsession.on('offer') events and invokes receiveFile() for each incoming file offer【L94-L100】.

The platform layer's startDownload method displays a "Save As" dialog and returns a FileDownload object (lines 55-71 in platform.service.ts). As the Zmodem library emits data chunks through the on_input event, the middleware writes them to the local filesystem via transfer.write() while displaying transfer percentages in the terminal【L141-L149】.


# On the remote SSH host - triggers Tabby's save dialog

sz myfile.txt

Session Cancellation and Cleanup

Tabby provides immediate transfer cancellation through standard terminal interrupts. The middleware subscribes to cancelEvent, which filters the output stream for ASCII character 3 (Ctrl-C)【L24-L30】. Upon detection, both sending and receiving paths abort the active Zmodem session and dispose of temporary FileUpload or FileDownload objects to prevent resource leaks【L131-L138】.

// Cancellation logic observes for ASCII 3 (Ctrl-C)
this.cancelEvent = this.outputToSession$.pipe(
  filter(x => x.length === 1 && x[0] === 3)
);

Automatic UI Integration

The ZModemDecorator class ensures Zmodem functionality is available in every terminal tab without manual configuration. When a new terminal session initializes, the decorator injects the middleware using terminal.session.middleware.unshift(new ZModemMiddleware(...))【L49-L55】. This automatic hooking makes Zmodem transfers available for any SSH, Telnet, or serial connection managed by Tabby.

Summary

  • Middleware-based detection: The ZModemMiddleware class in tabby-terminal/src/features/zmodem.ts intercepts all SSH traffic to identify Zmodem handshakes using a Sentry pattern.
  • Bidirectional transfers: Tabby distinguishes between send sessions (uploading via rz) and receive sessions (downloading via sz), invoking appropriate platform dialogs for each direction.
  • Platform abstraction: File I/O operations are delegated to tabby-electron/src/services/platform.service.ts, which handles native Electron dialogs and streaming interfaces.
  • Automatic availability: The decorator pattern ensures every new terminal tab includes Zmodem middleware without user configuration.
  • Graceful cancellation: ASCII 3 (Ctrl-C) detection provides immediate abort capabilities for stuck or unwanted transfers.

Frequently Asked Questions

What commands trigger Tabby's Zmodem file transfer?

Tabby responds to standard Zmodem commands executed on the remote host. Running rz (receive Zmodem) triggers an upload dialog for sending local files to the remote server, while sz filename triggers a save dialog for downloading files from the remote server to your local machine.

How does Tabby differentiate between uploading and downloading files?

The implementation inspects the Zmodem session type established during the initial handshake. A type === 'send' session indicates the remote is ready to receive files, prompting Tabby to open an upload dialog. A type === 'receive' session indicates the remote is sending files, triggering Tabby's download dialog and file save logic.

Can I cancel an ongoing Zmodem transfer in Tabby?

Yes, pressing Ctrl-C (sending ASCII character 3) during an active transfer immediately aborts the session. The middleware listens for this specific byte sequence through the cancelEvent observable and cleans up temporary file handlers for both uploads and downloads.

Is Zmodem support available for all connection types in Tabby?

Yes, because the ZModemDecorator automatically injects the middleware into every new terminal session using terminal.session.middleware.unshift(), Zmodem file transfers work across SSH, Telnet, and serial connections without requiring protocol-specific configuration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →