Modern SSH terminal widget for PyQt6 with credential vault and jump host support
Project description
nterm
A modern SSH terminal for network engineers
PyQt6 terminal widget with encrypted credential vault, jump host chaining, YubiKey/FIDO2 support, and legacy device compatibility.
Built for managing hundreds of devices through bastion hosts with hardware security keys.
Features
Terminal
- xterm.js rendering via QWebEngineView — full VT100/ANSI support
- Built-in themes: Catppuccin, Dracula, Nord, Solarized, Gruvbox (dark/light/hybrid)
- Custom YAML themes with independent terminal and UI colors
- Tab or window per session — pop sessions to separate windows
- Unicode, emoji, box-drawing characters
Authentication
- SSH Agent with YubiKey/FIDO2 hardware keys
- Password, key file, keyboard-interactive, certificate auth
- Multiple auth methods with automatic fallback
- RSA SHA-1 fallback for legacy devices (OpenSSH < 7.2)
- Legacy crypto support for old Juniper/Cisco gear
Connection Management
- Jump host chaining (unlimited hops)
- Auto-reconnection with exponential backoff
- Connection profiles in YAML/JSON
- Pattern-based credential resolution
Credential Vault
- AES-256 encryption with PBKDF2 (480,000 iterations)
- Pattern matching — map credentials to hosts by wildcard or tag
- Cross-platform keychain: macOS Keychain, Windows Credential Locker, Linux Secret Service
- Full PyQt6 management UI
Screenshots
| Gruvbox Hybrid Theme | Credential Manager |
|---|---|
| Connection Dialog | Multi-vendor Support |
|---|---|
Installation
From PyPI
pip install nterm
# Optional: system keychain support
pip install nterm[keyring]
# Run
nterm
From Source
git clone https://github.com/scottpeterman/nterm.git
cd nterm
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # Linux/macOS
# .venv\Scripts\activate # Windows
# Install in development mode
pip install -e .
# Optional: system keychain support
pip install keyring
# Run
nterm
# or
python -m nterm
Requirements
- Python 3.10+
- PyQt6 with WebEngine
- paramiko
- cryptography
- pyyaml
Platform Support
| Platform | PTY | Keychain |
|---|---|---|
| Linux | ✅ Native | Secret Service |
| macOS | ✅ Native | macOS Keychain |
| Windows 10+ | ✅ pywinpty | Credential Locker |
Quick Start
As a Widget
from PyQt6.QtWidgets import QApplication, QMainWindow
from nterm import ConnectionProfile, AuthConfig, SSHSession, TerminalWidget, Theme
app = QApplication([])
terminal = TerminalWidget()
terminal.set_theme(Theme.gruvbox_hybrid())
profile = ConnectionProfile(
name="router",
hostname="192.168.1.1",
auth_methods=[AuthConfig.password_auth("admin", "secret")],
)
session = SSHSession(profile)
terminal.attach_session(session)
session.connect()
window = QMainWindow()
window.setCentralWidget(terminal)
window.resize(1000, 700)
window.show()
app.exec()
With Credential Vault
from nterm.vault import CredentialStore, CredentialResolver
store = CredentialStore()
store.unlock("master-password")
# Add credential with pattern matching
store.add_credential(
name="network-devices",
username="admin",
password="secret",
match_hosts=["*.network.corp", "192.168.1.*"],
match_tags=["cisco", "juniper"],
)
# Auto-resolve credentials by hostname
resolver = CredentialResolver(store)
profile = resolver.resolve_for_device("switch01.network.corp", tags=["cisco"])
session = SSHSession(profile)
session.connect()
Themes
Built-in
Theme.default() # Catppuccin Mocha
Theme.dracula() # Dracula
Theme.nord() # Nord
Theme.solarized_dark() # Solarized Dark
Theme.gruvbox_dark() # Gruvbox Dark
Theme.gruvbox_light() # Gruvbox Light
Theme.gruvbox_hybrid() # Dark UI + Light terminal
Custom YAML
# ~/.nterm/themes/my-theme.yaml
name: my-theme
terminal_colors:
background: "#1a1b26"
foreground: "#c0caf5"
cursor: "#c0caf5"
black: "#15161e"
red: "#f7768e"
green: "#9ece6a"
yellow: "#e0af68"
blue: "#7aa2f7"
magenta: "#bb9af7"
cyan: "#7dcfff"
white: "#a9b1d6"
# ... bright variants
# UI chrome (can differ from terminal)
background_color: "#1a1b26"
foreground_color: "#c0caf5"
border_color: "#33467c"
accent_color: "#7aa2f7"
Jump Hosts
profile = ConnectionProfile(
name="internal-db",
hostname="db01.internal.corp",
auth_methods=[AuthConfig.agent_auth("dbadmin")],
jump_hosts=[
JumpHostConfig(
hostname="bastion.corp.com",
auth=AuthConfig.agent_auth("youruser"),
requires_touch=True,
touch_prompt="Touch YubiKey for bastion...",
),
],
)
Legacy Device Support
nterm automatically handles old network equipment:
- RSA SHA-1 fallback for OpenSSH < 7.2 servers
- Legacy KEX algorithms: diffie-hellman-group14-sha1, group1-sha1
- Legacy ciphers: aes128-cbc, 3des-cbc
- Auto-detection: tries modern crypto first, falls back as needed
Tested with:
- Junos 14.x (2016)
- Cisco IOS 12.2
- Old Arista EOS
- Any device running OpenSSH 6.x
Architecture
nterm/
├── connection/ # ConnectionProfile, AuthConfig, JumpHostConfig
├── session/
│ ├── ssh.py # SSHSession (Paramiko) with legacy fallback
│ ├── interactive_ssh.py # Native SSH + PTY
│ └── pty_transport.py # Cross-platform PTY
├── terminal/
│ ├── widget.py # TerminalWidget (PyQt6 + xterm.js)
│ └── bridge.py # Qt ↔ JavaScript bridge
├── theme/
│ ├── engine.py # Theme system
│ └── themes/ # YAML theme files
├── vault/
│ ├── store.py # Encrypted credential storage
│ ├── resolver.py # Pattern-based resolution
│ └── manager_ui.py # PyQt6 credential manager
└── manager/ # Session tree, connection dialogs
Related Projects
- TerminalTelemetry — PyQt6 terminal with network monitoring
- Secure Cartography — Network discovery and mapping
License
GPLv3
Contributing
Contributions welcome:
- Additional themes
- Windows testing
- Session recording/playback
- Telnet/serial support
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ntermqt-0.1.1.tar.gz.
File metadata
- Download URL: ntermqt-0.1.1.tar.gz
- Upload date:
- Size: 158.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ef9c4131cd552603f01e08e3319c085ab741558f8576930cc39390cf25ce411
|
|
| MD5 |
968bfc446c7c7f45c010375779eeb118
|
|
| BLAKE2b-256 |
3ba6c85ba19ed8c0291d186ca6c87f430dbe7cc4cc2916debdaa0a2dadd18f37
|
File details
Details for the file ntermqt-0.1.1-py3-none-any.whl.
File metadata
- Download URL: ntermqt-0.1.1-py3-none-any.whl
- Upload date:
- Size: 176.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0765383046f608b01bee5471acd9b9c127fcf72fd3499195b32b1962a8b39faf
|
|
| MD5 |
b9a183958b254607902957e1e37b9ea4
|
|
| BLAKE2b-256 |
9086c32656565d5ab96448beb59a872ca9b70add94abaad024de262e5ffc7e5a
|