How cmux Detects and Handles SSH Sessions for Seamless File Transfers
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 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:
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 (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-newunless explicitly configured by the user. - Connection Multiplexing:
ControlMasteris disabled forscpuploads to prevent socket conflicts. - IPv6 Literals: Brackets are added to IPv6 addresses in destination strings.
- Background Processes: Only processes where
pgid == tpgidare considered, filtering out background daemons. - Cancellation: Both upload and cleanup respect
TerminalImageTransferOperation.isCancelledchecks.
Practical Implementation Example
The following pattern demonstrates integrating detection with a drop handler:
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/psandsysctl(KERN_PROCARGS2)to identify foreground SSH processes attached to the current terminal. - Command Parsing: The
parseSSHCommandLinemethod extracts all SSH flags into a structuredDetectedSSHSessionobject. - Secure Transfer: Files upload via
/usr/bin/scpusing 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:
TerminalImageTransfercoordinates 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →