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
AsyncSSHClientandAsyncSFTPClientalongside 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}")
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
- GitHub: https://github.com/stratza/spindlex
- Documentation: https://spindlex.readthedocs.io/
-
PyPI:
pip install spindlex
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)