# PySSH Client Client SSH/SFTP personnel (type MobaXterm/Termius/Bitvise), **100% local**. **Aucun compte requis, aucun service cloud, aucune telemetrie.** Sessions, cles, mots de passe memorises et snippets restent dans des fichiers locaux (ou le trousseau securise de Windows) β€” rien n'est jamais envoye a un serveur tiers. La seule fonctionnalite qui communique vers l'exterieur est l'**assistant IA**, entierement optionnel, desactive par defaut, et qui n'envoie quoi que ce soit qu'au fournisseur que *vous* configurez avec *votre propre* cle API (voir plus bas). ## Fonctionnalites ### Terminal SSH - Sessions SSH multiples dans des onglets, avec terminal ANSI colore et trois themes - Copier-coller **a la souris, comme dans un terminal Unix** : selectionner copie aussitot (double-clic pour un mot), et le clic droit colle directement β€” ni menu, ni raccourci. Le clic molette colle aussi. `Ctrl+Shift+C` / `Ctrl+Shift+V` / `Maj+Inser` restent disponibles au clavier, et `Ctrl+C` reste l'interruption comme dans tout terminal - Confort d'affichage : zoom `Ctrl+molette` ou `Ctrl+ +/-` (`Ctrl+0` remet a zero, la taille est memorisee), barre de defilement de l'historique, `Maj+PagePrec/Suiv`, `Ctrl+Fin` pour revenir en direct, et recherche dans l'historique avec `Ctrl+F` - Onglets renommables (double-clic, ou clic droit) et deplaΓ§ables ; le clic droit permet aussi de les diviser en **2 ou 4 panneaux** (cote a cote, empiles, ou grille), de deplacer un autre onglet ouvert dans un panneau, ou de fermer les autres onglets - **Keepalive et reconnexion automatique** configurables par session (intervalle, nombre de tentatives, delai avec recul progressif) : une coupure reseau n'oblige plus a rouvrir la session et ressaisir les identifiants - Journalisation de session dans un fichier (bouton "Journal"), sequences ANSI retirees pour rester lisible - Reconnexion manuelle en un clic (bouton dans la barre de l'onglet) ### Connexion et authentification - **Authentification automatique** (mode par defaut) : comme la commande `ssh`, l'application utilise l'agent SSH et les cles de `~/.ssh` sans que vous ayez a designer quoi que ce soit, et ne demande un mot de passe qu'en dernier recours - **Import de votre `~/.ssh/config`** : les `Host` deja definis (HostName, User, Port, IdentityFile, ProxyJump, ForwardAgent...) apparaissent directement dans le panneau lateral (section "βš™ Configuration OpenSSH"), utilisables sans les ressaisir, ou importables comme session PySSH en un clic - **Bastion / ProxyJump** : une session peut passer par un rebond SSH, choisi parmi vos sessions existantes, un `Host` de `~/.ssh/config`, ou une configuration manuelle. Un groupe de sessions peut aussi heriter d'un bastion commun (voir Groupes ci-dessous) - **Tunnels SSH** (bouton "πŸ”€ Tunnels" dans la barre d'un onglet SSH) : redirection locale (`-L`, atteindre depuis votre poste un service qui n'ecoute que sur la boucle locale du serveur), distante (`-R`), et **proxy SOCKS dynamique** (`-D`, pour faire transiter tout le trafic d'une application par le tunnel) - Verification des empreintes de cles d'hote (TOFU) avant premiere connexion, comme un client SSH classique. Les hotes deja approuves par votre commande `ssh` (`~/.ssh/known_hosts`) sont reconnus : pas de double confirmation d'empreinte - Gestionnaire des hotes connus (menu "Hotes connus") : si une adresse en DHCP change de machine, la cle d'hote presentee ne correspond plus a celle enregistree β€” l'application le detecte et propose de remplacer l'ancienne entree directement au moment de la connexion ### Organisation - Carnet d'adresses de serveurs (JSON local, `data/sessions.json`) organise par dossiers, connexion en un clic - **Tags** (plusieurs par session) avec filtre/recherche dans le panneau lateral - **Groupes** : un dossier de sessions peut porter des parametres par defaut (utilisateur, port, cle SSH, bastion, keepalive) via clic droit > "Parametres du groupe...". Le bouton "↓ Heriter du groupe" du formulaire de session les reprend en pre-remplissage, sans empecher de les modifier au cas par cas pour une session donnee - **Workspaces** : sauvegardent l'etat complet d'une disposition de travail β€” onglets ouverts, ordre, divisions et tailles de panneaux, sessions associees β€” et la restaurent en un clic (menu "Workspaces", `Ctrl+Shift+W`). Une case optionnelle restaure le dernier workspace au demarrage. **Aucun mot de passe n'est jamais stocke dans un workspace** - **Recherche rapide** (`Ctrl+K`) : palette faΓ§on lanceur d'applications, recherche floue parmi sessions, hotes OpenSSH, workspaces, snippets et actions, entierement au clavier - **Snippets** : commandes enregistrees par categorie (des exemples Linux/Docker/Proxmox sont fournis au premier lancement), gerables (creer/modifier/supprimer/rechercher) depuis le menu "Snippets" ou le bouton "πŸ“‹ Snippets" de chaque onglet. Un snippet peut etre **insere** (le texte est ecrit, jamais valide automatiquement), **execute**, ou **execute sur tous les onglets a la fois** via Multi-Send - **Envoi simultane** : le bouton "⇉ Multi" du panneau lateral diffuse ce que vous tapez vers tous les onglets terminal ouverts (y compris dans les panneaux divises), pour piloter plusieurs serveurs d'un coup ### SFTP - Client SFTP integre facon WinSCP : double panneau local/distant avec colonnes triables (nom, taille, date, droits), glisser-deposer entre les panneaux, renommage (F2), suppression (Suppr), creation de dossier, permissions distantes (chmod), et file de transferts avec progression β€” le tout en arriere-plan, sans jamais geler l'interface - **Aucun favori n'est necessaire pour l'utiliser** β€” trois faΓ§ons d'y arriver : - *Vous avez tape `ssh user@hote` dans un terminal* : bouton "πŸ“ SFTP sur cette connexion" dans la barre de l'onglet. L'hote et l'utilisateur sont deduits de la commande tapee - *Connexion ponctuelle* : bouton "πŸ“ SFTP" du panneau lateral, ou `Ctrl+Shift+F` - *Session enregistree* : le panneau de fichiers est deja affiche a cote du terminal des l'ouverture, comme dans MobaXterm ; ou clic droit > "Ouvrir en SFTP" pour un onglet dedie ### Cles et secrets - Gestion des cles SSH : generation **Ed25519, ECDSA (courbes NIST P-256/P-384/P-521) et RSA**, import de cles existantes, export, protection par passphrase. Sous Windows, les cles privees generees ou importees reΓ§oivent des permissions NTFS restreintes (ACL), equivalent du `chmod 600` Unix - **Coffre-fort de mots de passe optionnel** : a la creation d'une session par mot de passe, une case "Memoriser de maniere securisee" permet de ne plus le ressaisir a chaque connexion. Le mot de passe est alors stocke dans le **Gestionnaire d'identifiants Windows** (chiffre par DPAPI), **jamais en clair dans un fichier de PySSH**. Sans cette case cochee (comportement par defaut), rien n'est memorise : le mot de passe est redemande a chaque connexion, comme aujourd'hui ### Assistant IA (optionnel, desactive par defaut) Une aide ponctuelle pour l'administration systeme, pas un pilote automatique : **aucune commande n'est jamais executee automatiquement**. - **Desactive par defaut**, et sans le moindre fournisseur impose : compatible avec toute API de type OpenAI (OpenAI, OpenRouter, un proxy interne, ou un modele local exposant la meme API) β€” vous indiquez l'URL et le modele, il n'y a pas de liste figee. Se configure depuis le menu "Assistant IA > Parametres...", avec un bouton "Tester la connexion" - Depuis un onglet terminal, le bouton "πŸ€– Assistant IA" permet, sur la selection courante : **Expliquer cette sortie**, **Diagnostiquer cette erreur**, **Proposer une solution**, ou **Poser une question** en y ajoutant la selection comme contexte. Les reponses s'affichent dans un panneau lateral (conversation en memoire uniquement, jamais journalisee ni ecrite sur le disque) - **Generer une commande** (a partir d'une description en langage naturel, sans selection necessaire) affiche toujours la commande proposee avant tout usage, avec trois choix : la **copier**, l'**inserer dans le terminal** (le texte est ecrit, jamais valide automatiquement), ou **annuler**. Une commande correspondant a un motif reconnu comme destructeur (`rm -rf`, `mkfs`, `dd`, un `DROP DATABASE`...) est signalee par un avertissement explicite - **Avant tout envoi** au fournisseur configure : un avis de principe s'affiche une seule fois, et chaque envoi est verifie localement a la recherche de secrets evidents (mot de passe, cle privee, jeton d'authentification) β€” si quelque chose est detecte, vous choisissez de le masquer automatiquement, de l'envoyer tel quel, ou d'annuler l'envoi - **La cle API** passe exclusivement par le meme coffre-fort securise que les mots de passe de session (Gestionnaire d'identifiants Windows) : jamais dans `settings.json`, jamais journalisee ## Installation (Windows) Necessite Python 3.10+ installe sur le poste (verifiable avec `py -3 --version`). ```bash setup.bat ``` Ce script cree un environnement virtuel local (`venv/`) et y installe les dependances (PySide6, paramiko, pyte, cryptography, pywinpty) β€” rien n'est installe globalement sur le poste. Pour contribuer au code (tests, lint, verification de types), installez plutot les dependances de developpement : ```bash venv\Scripts\pip install -r requirements-dev.txt ``` ## Lancement Trois facons, au choix : - **Raccourci Windows** (recommande) : double-cliquez sur `PySSH Client.lnk` a la racine du projet. Il lance l'application via `pythonw.exe`, donc **sans fenetre de console noire**. Vous pouvez le copier sur le Bureau, ou l'epingler a la barre des taches. Pour le (re)creer β€” apres un deplacement du projet, ou sur un autre poste : ```bash powershell -ExecutionPolicy Bypass -File tools\create_shortcut.ps1 -Desktop ``` (`-Desktop` et `-StartMenu` sont optionnels et ajoutent une copie a ces emplacements.) - **En ligne de commande**, sans console residuelle : ```bash venv\Scripts\pythonw.exe main.py ``` - **`run.bat`** : garde une fenetre de console ouverte, ce qui est pratique pour voir les erreurs si l'application refuse de demarrer. ## Version portable (sans Python installe) ```bash build_exe.bat ``` Produit un dossier autonome `dist\PySSH Client\` contenant l'executable et tout ce dont il a besoin, interpreteur Python compris. Copiez ce dossier ou vous voulez (y compris sur une cle USB) : il fonctionne sur n'importe quel poste Windows, **meme sans Python installe**. Le format retenu est un dossier plutot qu'un fichier `.exe` unique : avec PySide6, un executable unique doit decompresser environ 200 Mo a chaque lancement, ce qui ajoute plusieurs secondes d'attente a chaque demarrage. Le dossier demarre immediatement. > **Note pour qui modifie la recette de build** (`PySSHClient.spec`) : `pywinpty` a besoin de deux > executables auxiliaires, `OpenConsole.exe` (backend ConPTY) et `winpty-agent.exe` (repli WinPTY). > PyInstaller ne suit que les DLL liees et les oublie : ils sont donc ajoutes explicitement dans > `datas`. Sans eux, l'application se lance normalement mais **tous les terminaux se ferment > instantanement** ("Deconnecte (Terminal ferme)"). **Ou sont stockees les donnees ?** A cote de l'executable, dans son propre sous-dossier `data\` (mode portable : vos sessions et cles suivent la cle USB). Si cet emplacement n'est pas accessible en ecriture β€” installation dans `Program Files`, support en lecture seule β€” l'application bascule automatiquement sur `%APPDATA%\PySSHClient\`. ## Premiers pas 1. **Terminal local immediat** : bouton "πŸ’» Terminal" dans le panneau lateral (ou `Ctrl+Shift+T`), ou double-clic sur "PowerShell"/"Invite de commandes" dans la section "πŸ’» Terminaux locaux" de l'arborescence β€” aucune session a creer. 2. **Nouvelle session SSH** (bouton "+ Session" ou `Ctrl+N`) : renseignez l'hote, le port, l'utilisateur et le mode d'authentification. Vous avez deja un `~/.ssh/config` ? Ses `Host` apparaissent tout seuls dans la section "βš™ Configuration OpenSSH", inutile de les ressaisir. 3. **Cles SSH** (menu "Cles SSH > Gestionnaire de cles") : generez une nouvelle paire de cles ou importez-en une existante, puis selectionnez-la dans le formulaire de session. 4. Double-cliquez sur une session dans l'arborescence pour ouvrir un onglet terminal SSH. 5. **SFTP β€” trois facons d'y arriver**, selon la situation : - *Vous avez tape `ssh user@hote` dans un terminal* : bouton **"πŸ“ SFTP sur cette connexion"** dans la barre de l'onglet. L'hote et l'utilisateur sont deduits de la commande, le formulaire est pre-rempli. Rien a enregistrer. - *Connexion ponctuelle* : bouton **"πŸ“ SFTP"** du panneau lateral, ou `Ctrl+Shift+F`. La case "Enregistrer dans mes sessions" est decochee par defaut. - *Session enregistree* : clic droit > **"Ouvrir en SFTP"** (onglet dedie). Dans une session SSH ouverte par l'application, le panneau de fichiers est deja affiche a cote du terminal, comme dans MobaXterm. Dans tous les cas : glisser-deposer entre les deux panneaux, F2 renomme, Suppr supprime, F5 actualise, et le clic droit sur le panneau distant donne acces aux permissions (chmod). 6. **Aller plus vite** : `Ctrl+K` ouvre une recherche rapide parmi tout ce que vous avez deja ouvert ou enregistre. 7. **Retrouver une disposition de travail** : une fois plusieurs onglets/panneaux organises a votre gout, menu "Workspaces > Gestionnaire de workspaces..." (`Ctrl+Shift+W`) pour l'enregistrer et la rouvrir en un clic. 8. **Assistant IA** (facultatif) : menu "Assistant IA > Parametres..." pour indiquer l'URL, le modele et la cle API d'un fournisseur compatible OpenAI. Tant que cette etape n'est pas faite, l'assistant reste inactif et n'envoie jamais rien nulle part. ## Structure du projet ``` main.py point d'entree app/ main_window.py fenetre principale : assemble panneau lateral, onglets, menus session_manager.py base de sessions (JSON), tags, parametres de bastion/keepalive/reconnexion key_manager.py generation/import/export de cles SSH, empreintes known_hosts_manager.py gestion du fichier known_hosts (lister/retirer/remplacer une cle d'hote) ssh_connection.py connexion SSH en arriere-plan (paramiko + QThread), bastion, keepalive local_terminal.py terminal local Windows (PowerShell/cmd via pywinpty) terminal_widget.py emulateur de terminal ANSI (pyte + QPainter) sftp_panel.py client SFTP a deux panneaux, transferts en arriere-plan tunnels.py tunnels SSH : redirection -L / -R et proxy SOCKS dynamique -D bastion.py resolution d'un ProxyJump (session / Host OpenSSH / manuel) credentials.py coffre-fort optionnel (Gestionnaire d'identifiants Windows) workspace.py, snippets.py, persistance des workspaces, snippets et parametres de groupe groups.py, app_settings.py theme.py feuille de style "bleu nuit" + icone d'application ai/ assistant IA : fournisseur, redaction de secrets, detection de commandes dangereuses, parametres, execution en arriere-plan controllers/ logique d'assemblage : cycle de vie des onglets, panneau des sessions, diffusion Multi-Send, restauration des workspaces ui/ widgets d'onglet (SSH/local/SFTP), terminal divise, panneau IA dialogs/ boites de dialogue (session, cles, hotes, tunnels, snippets, workspaces, groupes, recherche rapide, parametres IA...) openssh/ lecture de ~/.ssh/config security/ protection des cles privees (ACL NTFS sous Windows) storage/ ecriture atomique de fichiers JSON, migration de schema services/ logique pure sans dependance Qt (detection ~cible SSH, recherche floue, calcul du delai de reconnexion) resources/ theme.qss feuille de style Qt "bleu nuit" data/ sessions.json, keys.json, vos donnees locales (non versionnees) - voir .gitignore workspaces.json, snippets.json, groups.json, known_hosts, keys/ tests/ suite de tests (voir "Tests" ci-dessous) ``` ## Tests ```bash pytest ``` (necessite `pip install -r requirements-dev.txt`). Chaque fichier `tests/test_*.py` reste aussi executable individuellement (`python tests/test_xxx.py`), pratique pour rejouer un seul scenario pendant le developpement. Les tests construisent de vraies fenetres Qt sans les afficher (`QT_QPA_PLATFORM=offscreen`) et couvrent, entre autres : le copier-coller et les raccourcis du terminal, la detection de l'hote dans une commande `ssh` tapee a la main, la logique SFTP (transferts, renommage, tri, suppression recursive) avec un serveur simule, le stockage atomique et la migration de schema, la lecture de `~/.ssh/config`, la resolution de ProxyJump, le coffre-fort d'identifiants, la detection de secrets et de commandes dangereuses, et l'ensemble de l'assistant IA (fournisseur HTTP, panneau, integration dans les onglets) β€” sans jamais faire d'appel reseau reel. ## Qualite ```bash ruff check app main.py tests # style et erreurs courantes pyright # verification de types (mode progressif, voir pyproject.toml) ``` Une CI Gitea Actions (`.gitea/workflows/ci.yml`) execute le lint et les tests a chaque push, et construit la version portable sur la branche principale. ## Limites connues / pistes d'amelioration - L'emulation de terminal couvre les usages courants (bash, vim, htop...) mais n'est pas un xterm parfaitement complet. - Le terminal local repose sur `pywinpty` (pseudo-console Windows). Si un onglet local reste bloque sur un ecran vide, tapez une touche : le rafraichissement de la console Windows peut etre paresseux dans certains contextes (notamment les sessions Windows non interactives) et se declenche a la premiere frappe. - Le SFTP ne propose pas encore l'edition de fichier distant en place (telecharger / modifier / renvoyer a la main). - Le ProxyJump ne gere qu'un seul rebond pour l'instant (pas de chainage de plusieurs bastions). - Le coffre-fort d'identifiants (mots de passe de session, cle API IA) repose sur le Gestionnaire d'identifiants Windows : pas d'equivalent sur Linux/macOS pour l'instant, l'application fonctionne mais sans memorisation securisee sur ces systemes. - L'application est concue et testee pour Windows uniquement (pywinpty, ACL NTFS, Gestionnaire d'identifiants). - La verification de types (`pyright`) est volontairement en mode progressif : un socle de signalements existe encore dans `app/ui/tabs.py` et `app/ssh_connection.py` (voir `pyproject.toml`), a resorber au fil du temps plutot que d'un bloc.