# How cmux Detects and Handles SSH Sessions for Seamless File Transfers

> Learn how cmux detects and handles SSH sessions by scanning TTY for foreground ssh processes and reusing connection parameters for seamless file transfers. Optimize your workflow with cmux.

- Repository: [manaflow-ai/cmux](https://github.com/manaflow-ai/cmux)
- Tags: how-to-guide
- Published: 2026-03-29

---

**cmux detects active SSH sessions by scanning the terminal TTY for foreground `ssh` processes, parsing their command-line arguments via `sysctl`, and reusing those connection parameters to upload files over the existing multiplexed connection.**

The `manaflow-ai/cmux` repository implements intelligent SSH session detection to enable drag-and-drop file uploads to remote hosts. When a user drops files into a terminal window running an active SSH connection, cmux automatically discovers the session parameters and transfers the files using the same secure channel.

## Detecting Foreground SSH Processes

The detection logic resides in [`Sources/TerminalSSHSessionDetector.swift`](https://github.com/manaflow-ai/cmux/blob/main/Sources/TerminalSSHSessionDetector.swift) and begins with identifying which process currently owns the terminal.

### Scanning the Terminal TTY with ps

The `detect(forTTY:)` method serves as the primary entry point at line 403. It invokes `/bin/ps` with the `-t <tty>` flag to capture a snapshot of every process attached to the specified terminal device.

The snapshot retrieves critical process metadata including **pid**, **pgid** (process group ID), **tpgid** (terminal process group ID), **tty**, and the **executable name**. This data populates the `processSnapshots` array defined between lines 84–90.

### Filtering for the Foreground SSH Client

Not every `ssh` process qualifies as the active session. The `isForegroundSSHProcess` helper (lines 76–82) applies strict criteria to isolate the foreground client:

```swift
normalizeTTYName(process.tty) == normalizeTTYName(ttyName) &&
process.executableName == "ssh" &&
process.pgid > 0 && process.tpgid > 0 && process.pgid == process.tpgid

```

This check ensures the process is bound to the correct TTY, is named exactly `ssh`, and is the current foreground leader (`pgid == tpgid`).

### Extracting and Parsing the SSH Command Line

Once a candidate PID is identified, cmux extracts the raw argument vector using `sysctl` with `KERN_PROCARGS2` (lines 30–45). The `commandLineArguments` function converts this binary data into a `[String]` array.

The `parseSSHCommandLine(_:)` method (lines 582–617) then walks this array to reconstruct the session configuration. It handles single-letter flags (`-p`, `-i`, `-A`), long options (`-o …`), and the destination host, producing a `DetectedSSHSession` value that stores:

- Destination host and port
- Identity file path
- ControlPath for connection multiplexing
- Jump host configuration
- IPv4/IPv6 flags
- Agent forwarding and compression settings
- Additional raw `sshOptions`

The first successfully parsed candidate is returned; if none match, the method returns `nil`.

## Uploading Files Over the Detected Session

When a drag-and-drop event occurs, [`Sources/TerminalImageTransfer.swift`](https://github.com/manaflow-ai/cmux/blob/main/Sources/TerminalImageTransfer.swift) (lines 341–351) invokes the detector and delegates the upload to the resulting `DetectedSSHSession` struct.

### Constructing the SCP Command

The `scpArguments(localPath:remotePath:)` method (lines 125–168) builds an argument list that mirrors the original SSH options. It preserves the user's ControlPath, identity file, jump host settings, and IP protocol preferences. For safety, it injects `-o StrictHostKeyChecking=accept-new` unless the user already specified a strict host key policy, and forces `-o ControlMaster=no` for the `scp` process to avoid interfering with existing multiplexed masters.

IPv6 literal addresses are automatically wrapped in brackets by `scpRemoteDestination(_:)` to ensure valid URI formatting.

### Executing the Transfer and Cleanup

The `runProcess(executable:arguments:timeout:operation:)` helper (lines 48–71) spawns `/usr/bin/scp` with the constructed arguments. It streams stdout and stderr, respecting cancellation tokens and timeout limits defined in the `TerminalImageTransferOperation`.

After successful upload, `cleanupUploadedRemotePaths` (lines 23–32) executes a remote `rm -f` command via `ssh` using the same connection parameters—including the ControlPath—to ensure cleanup occurs over the identical multiplexed session. If any step fails, the system returns a detailed `NSError` and removes already-uploaded files to prevent orphaned remote data.

## Handling SSH-Specific Edge Cases

- **User-supplied ControlPath**: The parser skips automatic ControlPath injection if `hasSSHOptionKey(..., key:"ControlPath")` returns true.
- **Strict Host Key Checking**: Automatically set to `accept-new` unless explicitly configured by the user.
- **Connection Multiplexing**: `ControlMaster` is disabled for `scp` uploads to prevent socket conflicts.
- **IPv6 Literals**: Brackets are added to IPv6 addresses in destination strings.
- **Background Processes**: Only processes where `pgid == tpgid` are considered, filtering out background daemons.
- **Cancellation**: Both upload and cleanup respect `TerminalImageTransferOperation.isCancelled` checks.

## Practical Implementation Example

The following pattern demonstrates integrating detection with a drop handler:

```swift
import TerminalSSHSessionDetector
import TerminalImageTransfer

func handleDrop(urls: [URL], ttyName: String) {
    // Detect an active ssh session attached to the terminal
    guard let sshSession = TerminalSSHSessionDetector.detect(forTTY: ttyName) else {
        print("No foreground SSH – drop ignored")
        return
    }

    // Upload the files using the same connection parameters
    let op = TerminalImageTransferOperation()
    sshSession.uploadDroppedFiles(urls, operation: op) { result in
        switch result {
        case .success(let remotePaths):
            print("Uploaded to remote:", remotePaths)
        case .failure(let err):
            print("Upload failed:", err.localizedDescription)
        }
    }
}

```

This approach guarantees that file transfers reuse the exact SSH connection the user already established, preserving all authentication and networking configurations.

## Summary

- **TTY Scanning**: cmux uses `/bin/ps` and `sysctl(KERN_PROCARGS2)` to identify foreground SSH processes attached to the current terminal.
- **Command Parsing**: The `parseSSHCommandLine` method extracts all SSH flags into a structured `DetectedSSHSession` object.
- **Secure Transfer**: Files upload via `/usr/bin/scp` using mirrored SSH options, with automatic handling of ControlPath, host key checking, and IPv6 literals.
- **Resource Cleanup**: Remote temporary files are removed using the same SSH session to ensure no orphaned data remains on the host.
- **Integration**: `TerminalImageTransfer` coordinates detection and upload, providing cancellation support throughout the operation.

## Frequently Asked Questions

### How does cmux identify which SSH process belongs to the current terminal?

cmux calls `detect(forTTY:)` with the terminal's TTY name, then executes `/bin/ps -t <tty>` to list attached processes. It filters for processes where the executable name is `ssh`, the TTY matches, and the process group ID equals the terminal process group ID (`pgid == tpgid`), ensuring only the foreground leader is selected.

### What SSH options does cmux preserve when uploading files?

The `DetectedSSHSession` struct captures the destination host, port, identity file (`-i`), ControlPath, jump host (`-J`), IPv4/IPv6 flags (`-4`/`-6`), agent forwarding (`-A`), compression (`-C`), and any additional options passed via `-o`. These are reconstructed into `scp` arguments by `scpArguments(localPath:remotePath:)`.

### How does cmux handle existing SSH ControlPath sockets?

If the user already specified a ControlPath in their SSH command, cmux detects this via `hasSSHOptionKey` and preserves it. For the `scp` upload process, it explicitly adds `-o ControlMaster=no` to prevent the file transfer from attempting to become a master connection or interfering with the existing socket.

### What happens if the SSH session is terminated during a file upload?

The `runProcess` helper monitors the `TerminalImageTransferOperation` for cancellation status. If the operation is cancelled or the timeout expires, the process is terminated. Additionally, if the upload fails partway through, `cleanupUploadedRemotePaths` attempts to remove any files already written to the remote host to prevent partial data remnants.