# How Tabby's SFTP Panel Implementation Works: A Deep Dive into the Source Code

> Explore Tabby's SFTP panel source code. Discover its layered architecture of Angular components, SSH session wrappers, and context-menu extensions for an interactive file manager UI.

- Repository: [Eugene/tabby](https://github.com/Eugeny/tabby)
- Tags: deep-dive
- Published: 2026-03-03

---

**Tabby's SFTP panel is implemented as a layered architecture of Angular components, SSH session wrappers, and context-menu extensions that convert russh SFTP channels into an interactive file manager UI.**

The SFTP panel in the [Eugeny/tabby](https://github.com/Eugeny/tabby) repository transforms authenticated SSH connections into a visual file browser. This **Tabby SFTP panel implementation** bridges the gap between low-level SSH protocol handling and a polished Angular-based user interface. Understanding this architecture reveals how the terminal emulator manages file transfers without external tools.

## Opening the SFTP Panel

The entry point for the SFTP panel follows a clear delegation chain from the UI context menu down to the SSH session layer.

### Context Menu Entry

When a user selects **"Open SFTP panel"** from an SSH tab's context menu, the `SFTPContextMenu` provider in [`tabby-ssh/src/tabContextMenu.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/tabContextMenu.ts) triggers the opening sequence. This provider registers the menu item and calls `tab.openSFTP()` on the active `SSHTabComponent`.

The `SSHTabComponent` (defined in [`sshTab.component.ts`](https://github.com/Eugeny/tabby/blob/main/sshTab.component.ts)) handles the UI request by delegating to its underlying session:

```typescript
async openSFTP (): Promise<void> {
    this.sftp = await this.session.openSFTP()
    …
}

```

### Session Initialization

The actual SFTP channel creation occurs in `SSHSession.openSFTP()` within [`tabby-ssh/src/session/ssh.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/session/ssh.ts). This method validates authentication state, creates a new channel via the russh library, and wraps it in a high-level session object:

```typescript
async openSFTP (): Promise<SFTPSession> {
    if (!(this.ssh instanceof russh.AuthenticatedSSHClient)) {
        throw new Error('Cannot open SFTP session before auth')
    }
    if (!this.sftp) {
        this.sftp = await this.ssh.activateSFTP(await this.ssh.openSessionChannel())
    }
    return new SFTPSession(this.sftp, this.injector)
}

```

This ensures the SFTP panel only opens on authenticated connections and maintains a single session instance per tab.

## The SFTPSession API Wrapper

The `SFTPSession` class in [`tabby-ssh/src/session/sftp.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/session/sftp.ts) abstracts the native `russh.SFTP` bindings into TypeScript-friendly async methods. This wrapper handles all remote filesystem operations required by the UI layer.

Key methods exposed by the wrapper include:

- **`readdir(p)`** – Lists directory contents returning `SFTPFile` objects with metadata
- **`stat(p)`** – Retrieves file attributes and permissions
- **`readlink(p)`** – Resolves symbolic links to their targets
- **`open(p, mode)` / `download(p, transfer)`** – Manages file streaming for transfers
- **`mkdir(p)`, `rmdir(p)`, `rename(old, new)`** – Handles directory and file mutations

The `SFTPFile` interface (lines 8-16) defines the metadata structure passed throughout the application, including `name`, `path`, type flags, `permissions`, `size`, and modification dates.

## The SFTPPanelComponent UI Layer

The visual interface resides in `SFTPPanelComponent` ([`tabby-ssh/src/components/sftpPanel.component.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/components/sftpPanel.component.ts)), which orchestrates user interactions and renders file listings.

### Navigation and File Listing

During initialization, the component obtains a session instance and immediately populates the root directory:

```typescript
async ngOnInit(): Promise<void> {
    this.sftp = await this.session.openSFTP()
    await this.navigate(this.path)
}

```

The `navigate(newPath)` method updates breadcrumb UI elements, clears active filters, and retrieves directory contents:

```typescript
this.fileList = await this.sftp.readdir(this.path)

```

Results are automatically sorted to display directories before files using the `isDirectory` property flag.

### File Operations and Transfers

The component handles three primary interaction types:

**Directory navigation** occurs when users double-click folders, triggering recursive path updates. **File downloads** utilize the platform-wide `PlatformService` to create `FileDownload` transfer objects, delegating byte-stream handling to `SFTPSession.download()`.

For recursive folder downloads, `downloadFolderRecursive()` walks the remote directory tree, creates local directory structures via `transfer.createDirectory()`, and streams individual files through `transfer.createFile()`.

The `getIcon(item)` method maps file extensions to FontAwesome categories (code, image, PDF) for visual identification, while `getModeString(item)` formats UNIX permission bits into human-readable `drwxr-xr-x` notation.

### Context Menu Extensions

The panel supports extensible context menus through the `SFTPContextMenuItemProvider` interface defined in [`tabby-ssh/src/api/contextMenu.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/api/contextMenu.ts). During menu construction, the component aggregates items from all registered providers:

```typescript
for (const section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(item, this)))) {
    items.push({ type: 'separator' })
    items = items.concat(section)
}

```

Default providers supply standard actions like **Delete** and **Create directory**, while platform-specific extensions (such as `EditSFTPContextMenu` in [`tabby-electron/src/sftpContextMenu.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-electron/src/sftpContextMenu.ts)) add **Launch WinSCP** functionality on Windows systems.

## Extending the SFTP Panel

Developers can customize the **Tabby SFTP panel implementation** through two primary extension points. The `SFTPContextMenuItemProvider` interface allows plugins to inject custom menu actions by implementing the `getItems()` method and registering the provider in the Angular dependency injection system.

UI customization requires modifying the Pug template (`sftpPanel.component.pug`) and associated SCSS styles ([`sftpPanel.component.scss`](https://github.com/Eugeny/tabby/blob/main/sftpPanel.component.scss)), though theme authors should note these are core components rather than public API surfaces.

## Summary

- **Tabby's SFTP panel** opens via `SFTPContextMenu` triggering `SSHTabComponent.openSFTP()`, which delegates to `SSHSession.openSFTP()` in [`tabby-ssh/src/session/ssh.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/session/ssh.ts).
- The `SFTPSession` wrapper in [`tabby-ssh/src/session/sftp.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/session/sftp.ts) exposes high-level async methods like `readdir()`, `download()`, and `rename()` over the native russh bindings.
- **SFTPPanelComponent** manages the Angular UI, handling navigation, file listing sorting, permission formatting, and recursive transfer operations.
- Context menus are extensible via the `SFTPContextMenuItemProvider` interface, allowing plugins to register custom file actions.
- File transfers integrate with the platform's `PlatformService` to handle local filesystem operations while streaming remote data through the SFTP session.

## Frequently Asked Questions

### How does Tabby create the SFTP connection when opening the panel?

Tabby creates the SFTP connection by calling `SSHSession.openSFTP()` in [`tabby-ssh/src/session/ssh.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/session/ssh.ts), which verifies the underlying SSH client is authenticated, then activates an SFTP channel through the russh library and wraps it in a `SFTPSession` object for the UI components to consume.

### What file operations does the SFTPSession wrapper support?

The `SFTPSession` class supports standard filesystem operations including `readdir()` for listing directories, `stat()` and `readlink()` for metadata, `mkdir()` and `rmdir()` for directory management, `rename()` for moving files, and streaming methods `download()` and `upload()` for transfer operations.

### Can I add custom actions to the SFTP panel's right-click menu?

Yes, you can add custom actions by implementing the `SFTPContextMenuItemProvider` interface from [`tabby-ssh/src/api/contextMenu.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/api/contextMenu.ts) and registering your provider in the Angular module. Your implementation should return `MenuItemOptions` arrays from the `getItems()` method, which the `SFTPPanelComponent` will merge into the context menu alongside default actions like Delete and Create directory.

### Where is the SFTP panel UI defined in the Tabby source code?

The SFTP panel UI is defined in [`tabby-ssh/src/components/sftpPanel.component.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-ssh/src/components/sftpPanel.component.ts) (logic), `sftpPanel.component.pug` (template structure), and [`sftpPanel.component.scss`](https://github.com/Eugeny/tabby/blob/main/sftpPanel.component.scss) (styling). Modal dialogs for delete confirmation and directory creation reside in [`sftpDeleteModal.component.ts`](https://github.com/Eugeny/tabby/blob/main/sftpDeleteModal.component.ts) and [`sftpCreateDirectoryModal.component.ts`](https://github.com/Eugeny/tabby/blob/main/sftpCreateDirectoryModal.component.ts) respectively.