DEV Community

Di3Z1E
Di3Z1E

Posted on

SpindleX: A Modern Pure-Python SSHv2 and SFTP Library

SpindleX: A Modern Pure-Python SSHv2 and SFTP Library

After years of working with existing SSH libraries in Python, I decided to build a new one from the ground up with modern needs in mind.

Today I'm excited to introduce SpindleX (spindlex) - a clean, high-performance, secure-by-default SSHv2 and SFTP client/server library for Python 3.9+.

Why Another SSH Library?

Paramiko has been the go-to for a very long time, but it carries a lot of legacy code and older defaults. asyncssh is excellent for async use cases, but I wanted something that combines excellent developer experience, strong security defaults, great performance, first-class asyncio support, and a fresh, maintainable codebase.

Why SpindleX?

  • Business Friendly: MIT Licensed. Permissive use for commercial and proprietary projects.
  • Maintainable Code: Modular architecture designed for clarity and easier security auditing.
  • Modern API: Clean, intuitive interface with consistent error handling and minimal dependencies.
  • Focused Scope: No support for insecure legacy protocols, resulting in a leaner and more secure codebase.

Key Features

  • Secure by default: Only modern algorithms (ChaCha20-Poly1305 preferred, Ed25519, ECDSA, AES-CTR, etc.), Legacy ciphers are excluded, Full protection against Terrapin attacks and strict host key verification.

  • High Performance SFTP: Adaptive chunk sizes (up to 255 KB), excellent pipelining, and optimized buffering.

  • Native Async Support: Clean AsyncSSHClient and AsyncSFTPClient alongside the synchronous version.

  • Usability: Recursive directory upload/download, ProxyJump / bastion host support, local/remote port forwarding, and a fully
    typed API.

  • Minimal Dependencies: Mainly relies on cryptography. MIT licensed.

  • Both client and server implementations.

Quick Example

from spindlex import SSHClient
from spindlex.hostkeys.policy import RejectPolicy

with SSHClient() as client:
    # Secure default: reject unknown host keys. Make sure the server's key
    # is in ~/.ssh/known_hosts before running (e.g. via `ssh user@host` once).
    client.set_missing_host_key_policy(RejectPolicy())
    client.get_host_keys().load()

    client.connect('server.example.com', username='admin', password='password')

    # exec_command returns (stdin, stdout, stderr)
    stdin, stdout, stderr = client.exec_command('ls -l /tmp')

    # stdout and stderr are iterable and return lines
    for line in stdout:
        print(line.strip())

    # Get the exit status (0 usually means success)
    exit_status = stdout.recv_exit_status()
    print(f"Command exited with status: {exit_status}")
Enter fullscreen mode Exit fullscreen mode

Current Status

  • Latest version: v0.7.0 (released today)
  • Still in Beta, early stage with limited real-world usage
  • Actively developed with frequent releases

Links

Try It & Give Feedback

If you're working on automation, DevOps tools, remote management, or any project that needs SSH/SFTP, I'd love for you to try SpindleX, especially on new projects.

Feedback, bug reports, feature requests, and contributions are all very welcome!

What do you think? Are you happy with Paramiko/asyncssh, or would you consider switching to something newer?

Let me know in the comments!

Top comments (0)