Skip to main content

TurboSSH — SSH / Serial / SFTP / FTP terminal & automation toolkit for automotive & embedded (API, CLI, and a MobaXterm-style PyQt5 GUI).

Project description

TurboSSH

PyPI Python

TurboSSH is an SSH / Serial / SFTP / SCP / FTP / RDP terminal and automation toolkit built on Paramiko — for automotive & embedded work, test automation, and everyday remote ops. It ships three ways in one install:

  1. Python APIimport turbossh (or ssh_handler) for scripts & test frameworks.
  2. CLIturbossh …, fully argument-driven.
  3. MobaXterm-style GUIturbossh-gui, a tabbed multi-session terminal with a true VT100 emulator (htop/vim/top work), an SFTP browser, serial consoles, and RDP launch. Ships as a prebuilt Windows exe (PyQt5 baked in — no PyQt5 install needed).
pip install turbossh
turbossh-gui

Table of contents

Install

pip install turbossh           # API + CLI + prebuilt Windows GUI exe
pip install "turbossh[gui]"    # also installs PyQt5 to run the GUI from source

Batteries included: paramiko, scp, pyserial, keyring, pywinrm, and pyte (VT100) are pulled in automatically. Only PyQt5 is optional — the shipped GUI exe already contains it, so turbossh-gui works without installing PyQt5 (handy on Windows ARM64, where PyQt5 has no wheel).

The GUI (MobaXterm-style)

turbossh-gui
  • Session manager sidebar — saved SSH / Serial / RDP sessions. Passwords are kept in the OS credential vault, never in the JSON. New / Edit / Delete / Connect.
  • Tabbed sessions — many connections open at once, each a closable tab.
  • True VT100 terminal (pyte) — full-screen apps like htop, vim, top, less, nano render correctly, with colors, a block cursor, and live PTY resize.
  • Quick buttons — one-click slog2info -w, journalctl -f, dmesg -w, tail -f, plus Ctrl-C and Clear.
  • Split SFTP browser under each SSH terminal — navigate, upload, download, mkdir, rename, delete (transfers on a separate channel; UI never blocks).
  • Serial console — open a COM port and type/read live.
  • RDP session — launches native Remote Desktop (mstsc) with credentials pre-seeded.
  • Keyboard shortcuts — Ctrl+T/N new session, Ctrl+W close tab, Ctrl+Enter connect, F1 docs; in the terminal: Ctrl+Shift+C/V copy-paste, Ctrl-C/D/Z, arrows, Tab-completion (server-side), function keys.
  • Crash-proof — background threads for all I/O; a startup failure shows a native popup and writes ~/.ssh-handler/crash.log; runtime errors are logged, never fatal.

Quick start (API)

from turbossh import SSHHandler, SSHConfig

with SSHHandler(SSHConfig(host="10.0.0.5", username="root", password="pw")) as ssh:
    print(ssh.run("uname -a").text)            # clean single-line output
    ssh.run("systemctl restart nginx", check=True)
    ssh.push("local.txt", "/tmp/remote.txt")
    ssh.pull("/etc/nginx", "./backup", recursive=True)

Connecting — local & via RDP jump host

The same methods work everywhere; only the config changes.

# direct
cfg = SSHConfig(host="10.0.0.5", username="root", password="pw",
                host_key_policy="ignore")          # ignore = lab/reimaged devices

# through an RDP/bastion machine (laptop -> jump -> target)
jump = SSHConfig(host="10.232.9.22", domain="CORP", username="user", password="pw")
cfg  = SSHConfig(host="10.120.1.91", username="root", password="pw",
                 jump_host=jump, host_key_policy="ignore")

with SSHHandler(cfg, quiet=True) as ssh:
    print(ssh.run("hostname").text)
host_key_policy Behaviour
"auto" (default) add unknown keys, reject changed keys
"ignore" accept any key incl. changed — for reimaged/DHCP lab devices
"reject" strict; only keys already in known_hosts

Running commands

r = ssh.run("ls -la", timeout=30, check=False)
print(r.text)        # stdout, stripped     print(r.stdout)  # raw
print(r.exit_code, r.ok, r.duration)
ssh.run_many(["a", "b", "c"], stop_on_error=True)
ssh.sudo("systemctl restart app", password="pw")

Continuous logs

# stream live, match a pattern, tee to a file (ANSI auto-cleaned)
ssh.stream("slog2info -w", on_line=print,
           match=r"error|fail", save_to="device.log", timeout=120)

# generator form
for line in ssh.iter_lines("journalctl -f"):
    print(line)

match is a regex; stop_on_match=True stops at the first hit (send/expect).

File transfer — SFTP / SCP / FTP

ssh.push("fw.bin", "/tmp/fw.bin")                 # SFTP upload
ssh.pull("/var/log/messages", "messages.log")     # SFTP download
ssh.push("./build", "/tmp/build", recursive=True) # folder, with progress callback
ssh.scp_push("img.tar", "/tmp/img.tar")           # SCP protocol

from turbossh import FTPHandler, FTPConfig
with FTPHandler(FTPConfig(host="ftp.x", username="u", password="p", use_tls=True)) as ftp:
    ftp.push("a.txt", "a.txt"); ftp.pull("b.txt", "b.txt")

Via RDP: identical — transfers ride the jump_host tunnel automatically. Remote FS helpers: listdir, stat, exists, isdir, mkdir, makedirs, rename, remove, chmod, read_text, write_text, walk.

Serial / COM ports

# local port (device plugged into THIS machine)
from turbossh import SerialHandler, list_serial_ports
print(list_serial_ports())
with SerialHandler("COM5", baudrate=115200) as ser:
    ser.write_line("version")
    ser.stream(on_line=print, match=r"login:", save_to="com5.log")

# port on a REMOTE machine, over SSH/jump (COM = Windows, /dev/tty* = Linux)
with SSHHandler(cfg, quiet=True) as ssh:
    ssh.serial_write("COM5", "version", baudrate=115200)
    ssh.serial_stream("COM5", baudrate=115200, on_line=print,
                      match=r"login:", save_to="com5.log")

Automotive / legacy devices

Old ECUs / embedded SSH servers often use crypto modern Paramiko drops. Re-enable it:

cfg = SSHConfig(host="10.0.0.9", username="root", password="pw",
                enable_legacy_algorithms=True)     # old KEX/ciphers/host-keys
# fine-grained: disabled_algorithms={"pubkeys": ["rsa-sha2-512"]}

In the GUI, tick "Enable legacy algorithms" in the session dialog.

Confidential credentials

Mechanism What it does
Secret wraps a password; logs/reprs show ********; only .reveal() exposes it
mask() redacts secrets from any string (applied to all logging automatically)
CredentialStore stores/reads passwords in the OS vault via keyring — no plaintext
prompt_password() hidden terminal input

Enable SSH on a Windows/RDP box (offline)

turbossh-setup            # self-elevates, installs OpenSSH Server from a bundled
                          # ZIP (ARM64/Win64/Win32), starts sshd, opens firewall,
                          # generates + fixes host keys. No internet/Windows Update.

Or auto-enable a remote box over WinRM: SSHConfig(auto_bootstrap_via_winrm=True).

CLI reference

turbossh run    --host H --user U [--domain CORP] [--use-stored] uname -a
turbossh push   --host H --user U ./build /tmp/build --recursive
turbossh pull   --host H --user U /var/log ./logs --recursive
turbossh info   --host H --user U --json
turbossh store-credential --user U --domain CORP --service my_lab
turbossh-gui              # launch the GUI
turbossh-setup            # install OpenSSH Server on this machine (offline)
turbossh-shortcut         # create a Desktop shortcut to the GUI
turbossh-docs             # open the docs

Password options: --password (hidden prompt), --use-stored (OS vault), --key FILE.

Result objects & error handling

  • CommandResult.exit_code, .stdout, .text, .stderr, .duration, .ok
  • TransferResult.size_bytes, .duration, .human_speed, .files
  • OperationResult — safe-mode wrapper: bool(res), .value, .error, .unwrap()

Raise mode (default): typed exceptions — SSHConnectionError, SSHAuthenticationError, SSHTimeoutError, SSHCommandError, SSHTransferError, FTPError, SerialError, WinRMError, CredentialError (all subclass SSHError). Auto-retry on connect + auto-reconnect on drop are built in, and a failed connect self-diagnoses (probes SSH/RDP ports and explains why).

Safe mode (SSHHandler(cfg, safe=True)): every call returns an OperationResult instead of raising — ideal for GUIs/long-running tools.

Embed in your own PyQt5 app

ssh_handler.pyqt_worker.SSHWorker is a QObject (safe mode) you move to a QThread and drive via signals (log, connected, command_done, transfer_done, progress, error, finished).

Building the exe

pip install "turbossh[gui]" pyinstaller
python scripts/build_exe.py            # dist/turbossh-gui/...
python scripts/build_exe.py --onefile  # single .exe

API map

turbossh/                 import shim -> re-exports ssh_handler
ssh_handler/
  config.py        SSHConfig (legacy algos, jump, host-key policy…), FTPConfig
  core.py          SSHHandler  (SSH + SFTP + SCP + stream + serial + diagnose)
  serial_handler.py  SerialHandler, list_serial_ports
  ftp.py           FTPHandler (FTP/FTPS)
  winrm_bootstrap.py  enable_openssh_via_winrm
  pool.py          SSHPool (parallel multi-host)
  credentials.py   Secret, CredentialStore, mask, prompt_password
  results.py       CommandResult, TransferResult, ShellResult, OperationResult
  cli.py           CLI + launchers (gui/docs/setup/shortcut/rdp)
  pyqt_worker.py   SSHWorker (embed in your own GUI)
  bin/turbossh-gui.exe         prebuilt GUI (PyQt5 baked in)
  gui/             MobaXterm-style app
    vt100.py       true VT100 terminal (pyte) — htop/vim render correctly
    terminal.py    reader thread
    sftp_browser.py  remote file browser (threaded transfers)
    session_dialog.py / sessions.py   session manager + saved profiles
    session_widgets.py   SSH terminal + SFTP split, serial console
    main_window.py · app.py · theme.py · log_panel.py

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

turbossh-0.4.0-py3-none-any.whl (64.4 MB view details)

Uploaded Python 3

File details

Details for the file turbossh-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: turbossh-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 64.4 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for turbossh-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bfabca078e25a56850dea5e25efd81063e9e6bcb6201a5cb35f5a92c96b2a2c8
MD5 8f9e89e99d02f6695f0d0f024fb2be5e
BLAKE2b-256 3acbe752d83b344afc66f0164a3c9b2ef4b47a3a76f01d6fdeccdf9ea7ffffc5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page