DeepSeek Harness sur VPS : accès privé via tunnel SSH
Installez DeepSeek Harness sur un VPS Linux, figez la version npm, découvrez le rôle des plugins et ouvrez l’interface web du port 3080 via un tunnel SSH.
Ce qu’est le DeepSeek Harness
Le DeepSeek Harness (dsh) est un runtime d’agent Node.js que vous pouvez exécuter sur un VPS (serveur privé virtuel). La méthode sûre consiste à le lier à 127.0.0.1, puis à y accéder depuis votre navigateur au moyen d’un tunnel SSH (secure shell). Il fournit une interface web (user interface) sur le port 3080 au lieu de s’exécuter dans un terminal. Ce serveur web ne demande aucun mot de passe spécifique. Un port 3080 exposé sur Internet donne donc à toute personne qui le découvre accès à un agent capable de lire vos fichiers et d’exécuter des commandes avec les droits de votre utilisateur Linux.
DeepSeek l’a publié le 13 août 2026 sous licence MIT, sous la forme du package npm @deepseek-ai/dsh. Le projet se présente comme une developer preview et précise que des changements incompatibles sont attendus. Tous les numéros de version ci-dessous correspondent à un état du projet en août 2026. Consultez donc le repository avant de copier ces éléments sur un serveur important.
Une idée traverse toute la conception : tout est un plugin. L’adaptateur de modèle, le registre des outils, le journal de session, le sandbox, le scheduler et la boucle de l’agent elle-même sont des plugins chargés dans un contexte partagé unique, et chacun peut être remplacé. Il n’existe aucun cœur privilégié que les plugins se contenteraient d’enrichir. C’est ce qui rend ce harness intéressant à tester, mais c’est aussi là que se trouve le seul véritable risque. La pertinence de ce compromis dépend du point de comparaison retenu, et la comparaison avec Claude Code et Omnigent met la conception où tout est un plugin en regard de deux autres approches concernant le couplage au modèle, la licence et la quantité de ressources qu’elles exigent sur un VPS.
Un harness n’est pas un modèle
Le harness exécute la boucle de l’agent. Le raisonnement s’effectue dans un modèle situé ailleurs. Rien ne fonctionne tant que vous ne lui fournissez pas une clé API (interface de programmation d’application) ou l’adresse d’un endpoint de modèle que vous hébergez vous-même. Tout ce qui est décrit dans cet article concerne la configuration du harness, pas le comportement du modèle. Il est utile de bien comprendre la distinction entre les deux avant de passer un après-midi à chercher de quel côté vient un problème.
Vous configurez cela dans l’interface, sous Settings, puis Models. Le catalogue propose des fiches prêtes à l’emploi pour les principaux fournisseurs d’API (DeepSeek, OpenAI, Anthropic), dans lesquelles vous collez une clé. L’option « Add a custom provider » est la plus intéressante : elle demande un identifiant de fournisseur, un nom d’affichage, une URL de base, un protocole d’API et un identifiant d’authentification. Elle utilise le protocole compatible avec OpenAI. Toute gateway ou tout serveur local qui implémente ce protocole fonctionne donc. Les fournisseurs personnalisés peuvent également interroger l’endpoint compatible avec OpenAI GET /models pour renseigner automatiquement la liste des modèles.
C’est ainsi que vous indiquez au harness d’utiliser un modèle sur le même VPS. Ollama expose une API compatible avec OpenAI à l’adresse http://127.0.0.1:11434/v1/. Le champ de clé API doit contenir n’importe quelle chaîne, par convention ollama, car ce champ est obligatoire puis ignoré. Déterminer si un modèle assez petit pour tenir sur votre VPS est suffisamment performant pour piloter un agent est une question plus difficile. La différence entre Ollama et vLLM en tant que serveurs de modèles locaux détermine la quantité de RAM nécessaire.
Les clés saisies dans l’interface sont accessibles en écriture uniquement. Le harness les stocke dans $DSH_HOME/.credentials.yaml et ne conserve qu’une référence d’identifiant d’authentification dans settings.yaml. $DSH_HOME utilise ~/.dsh par défaut. Traitez ce fichier comme un fichier de mots de passe, car c’en est un : toute personne qui peut le lire peut utiliser votre budget d’API. Si vous préférez modifier directement ces fichiers plutôt que de passer par Settings, le guide de configuration de dsh consacré aux fichiers de configuration, aux clés et aux endpoints de modèles explique le rôle de chaque clé et les données qui quittent votre machine dans chaque mode.
Ce qu’il vous faut avant l’installation
- un VPS exécutant Ubuntu 24.04 ou une autre distribution Linux récente, avec un accès SSH
- Node.js 22.19 ou une version ultérieure de la branche 22.x, ou Node.js 24 ou une version ultérieure, versions utilisées par le projet pour ses builds et ses tests
- un compte utilisateur normal, et non
root, car l’agent exécute les commandes shell avec l’utilisateur qui a démarré le processus pnpmdans le PATH si vous prévoyez d’installer des plugins, car la commande du plugin l’exécute via un shell- le port 3080 fermé dans votre firewall et dans le firewall réseau distinct de votre fournisseur
Le paquet nodejs fourni par Ubuntu est plus ancien que ce dont le harness a besoin. Installez donc Node avec NodeSource ou nvm plutôt que d’utiliser apt install nodejs. Si le VPS est neuf, durcir SSH avant toute autre opération vaut dix minutes, car le tunnel dont vous allez dépendre est aussi sûr que le serveur SSH situé derrière.
Installer DeepSeek Harness sur un VPS en figeant une version
node --version
npx @deepseek-ai/dsh@0.1.0-rc.6 webnpx télécharge le paquet et exécute son binaire dsh. web est un alias de --profile web, qui démarre l’application web. Le processus affiche l’adresse sur laquelle il est en écoute. La valeur par défaut est http://127.0.0.1:3080. Ces numéros correspondent à un choix de liaison, et non à une simple valeur par défaut cosmétique. Pourquoi dsh affiche une adresse loopback explique ce à quoi le harness répondra ou non avant que vous ne cherchiez à y accéder depuis votre ordinateur portable.
Figez la version. npx @deepseek-ai/dsh web résout le tag latest vers la version qu’il désigne au moment de l’exécution. Le projet a déjà publié plusieurs release candidates et indique que des breaking changes sont à venir. 0.1.0-rc.6 correspond à la version désignée par latest le 13 August 2026. Une version figée garantit que le serveur configuré aujourd’hui se comportera de la même manière le mois prochain. Une mise à niveau devient ainsi une décision, et non un incident découvert après coup. Si une commande utilisant une version figée démarre malgré tout le mauvais build ou refuse complètement l’installation, les erreurs d’installation et de version dsh courantes expliquent comment vider le cache npx et vérifier quel npm est fourni avec votre Node.
Pour l’utilisation quotidienne, installez-le une seule fois au lieu de résoudre à nouveau la version à chaque démarrage.
npm install -g @deepseek-ai/dsh@0.1.0-rc.6
dsh --profile web --helpCette deuxième ligne mérite d’être exécutée, car le launcher et l’application web utilisent des jeux de flags distincts. dsh --help affiche les options propres au launcher. dsh --profile web --help affiche les flags acceptés par l’application web. C’est là que se trouvent --port, --host et --trusted-host, qui peut être répété.
Vérifiez maintenant l’adresse sur laquelle le processus est en écoute.
ss -tlnp | grep 3080La colonne de l’adresse locale doit afficher 127.0.0.1:3080. Si elle affiche 0.0.0.0:3080, l’interface est accessible depuis Internet. Arrêtez le processus avant toute autre opération.
Pourquoi vous ne devez jamais publier le port 3080
Le serveur web ne possède aucune couche d’authentification. Sa configuration expose une adresse d’écoute et un port d’écoute, et c’est toute sa surface d’exposition. Le contrôle d’accès pour les déploiements autres que loopback repose sur un paramètre distinct de type trusted host. Ce paramètre n’affiche pas d’écran de connexion.
Examinez maintenant ce qui se trouve derrière ce port. L’agent modifie des fichiers dans le workspace et exécute des commandes shell. Vos identifiants auprès du fournisseur sont également stockés sur le disque, à côté de l’agent. Un port 3080 ouvert équivaut donc à un shell distant avec une interface de chat, exécuté avec les droits de l’utilisateur qui l’a démarré, et auquel votre clé API est associée. Personne n’a besoin d’un exploit pour y accéder. Il lui suffit de connaître le numéro de port. Les scanners trouvent les ports dans les heures qui suivent la mise en ligne d’un hôte.
La CLI (interface de ligne de commande) confirme ce choix. Depuis 0.1.0-rc.6, elle ne prend volontairement pas en charge --host 0.0.0.0 et quitte avec une erreur d’utilisation au lieu de démarrer. Ce refus est une fonctionnalité. Ne cherchez donc pas de patch pour le supprimer.
Deux autres modes de déploiement sont raisonnables lorsqu’un tunnel ne vous convient pas. Placez le serveur sur un réseau overlay privé afin qu’il ne possède qu’une adresse accessible par routage depuis vos propres appareils. C’est ce que permet un serveur de contrôle Headscale auto-hébergé. Vous pouvez aussi le placer derrière un reverse proxy qui authentifie la requête avant qu’elle n’atteigne le port 3080, par exemple un serveur d’authentification unique Authentik avec forward auth. Un reverse proxy sans authentification en amont n’est pas un contrôle de sécurité. C’est seulement une URL plus longue.
Accéder à l’interface web via un tunnel SSH
Exécutez cette commande sur votre ordinateur portable, pas sur le serveur.
ssh -N -L 3080:127.0.0.1:3080 you@your-server-L ouvre le port 3080 sur votre ordinateur portable et transfère toute connexion qui y arrive via la session SSH chiffrée. La partie 127.0.0.1:3080 est résolue sur le serveur. La connexion arrive donc sur le harness depuis loopback, exactement comme si vous étiez devant la machine. -N indique de ne pas démarrer de shell distant, car vous voulez uniquement le transfert.
Ouvrez ensuite http://127.0.0.1:3080 dans votre navigateur local. Si le port 3080 est déjà utilisé sur votre ordinateur portable, modifiez le nombre de gauche : ssh -N -L 3180:127.0.0.1:3080 you@your-server, puis accédez à http://127.0.0.1:3180. Le nombre de gauche correspond au port local et celui de droite au port du serveur. Seul le nombre de gauche change.
Enregistrez-le dans ~/.ssh/config et ne le saisissez plus manuellement.
Host dsh
HostName 203.0.113.10
User deploy
IdentityFile ~/.ssh/id_ed25519
LocalForward 3080 127.0.0.1:3080Ensuite, ssh -N dsh démarre le tunnel. Si le navigateur indique que la connexion a été refusée, cela signifie généralement que le tunnel est actif, mais que rien n’écoute à l’autre extrémité. SSH transfère le port, que le harness soit en cours d’exécution ou non. Vérifiez le serveur avec la commande ss ci-dessus.
Conserver le harness après la déconnexion
Une commande npx s’arrête avec votre shell. Un service utilisateur systemd reste actif et relance le harness après un crash ou un redémarrage. L’unité présentée ici reste volontairement minimale. Si vous voulez exécuter le harness sous son propre compte restreint, figer la version dans l’unité et disposer de journaux réellement exploitables avec une recherche, la configuration systemd headless pour dsh couvre ce cas en détail.
loginctl enable-linger $USER
mkdir -p ~/.config/systemd/user
command -v dshenable-linger est nécessaire, car les services utilisateur s’arrêtent normalement à la fin de votre dernière session. Sans cette option, le harness s’arrête dès que vous fermez le tunnel. Utilisez le chemin absolu affiché par command -v dsh dans l’unité, car systemd ne recherche pas dans le PATH construit par votre shell de connexion.
[Unit]
Description=DeepSeek Harness web UI
After=network-online.target
[Service]
Type=simple
WorkingDirectory=%h/projects/site
ExecStart=/usr/local/bin/dsh web
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.targetWorkingDirectory n’est pas un détail esthétique. Le processus dsh utilise le répertoire depuis lequel il a été lancé comme emplacement par défaut dans le système de fichiers. Un service démarré au mauvais endroit fournit donc à l’agent un espace de travail par défaut incorrect. Vous pouvez toujours sélectionner l’espace de travail dans l’interface utilisateur.
systemctl --user daemon-reload
systemctl --user enable --now dsh
systemctl --user status dshUne unité qui refuse de démarrer utilise presque toujours un chemin ExecStart incorrect ou une version de Node rejetée par le binaire. journalctl --user -u dsh -n 50 indique laquelle. Le même modèle permet de maintenir un agent de programmation actif sur un VPS, avec des causes de panne identiques.
Ce qu’un plugin est autorisé à faire
Un plugin est un module qui ajoute des services, des événements typés et des effets réversibles à un contexte partagé. Les points d’extension méritent une attention particulière :
- enregistrer un fournisseur de modèles sur
ctx.llm - ajouter des outils accessibles au modèle sur
ctx.tools - fournir le backend shell utilisé par
ctx.shell - fournir l’accès au système de fichiers ou les règles de sécurité utilisées par
ctx.fs - enregistrer des commandes utilisateur sur
ctx.commands - exécuter des tâches en arrière-plan via
ctx.jobs - encapsuler les processus lancés avec un backend
ctx.sandbox - intercepter les requêtes et les appels d’outils via les événements
agent/*ettools/* - étendre l’état de session persistant
- piloter l’interface utilisateur via
ctx.agents
Lisez cette liste comme le ferait un attaquant. Un plugin peut fournir la couche du système de fichiers et la couche shell. Il peut aussi s’intercaler dans chaque appel d’outil effectué par le modèle. Aucune boîte de dialogue d’autorisation ne sépare un plugin de ces points d’extension, car un plugin est du code Node ordinaire chargé dans le même processus que le reste de l’application. Installer un plugin revient à exécuter le code d’un tiers avec les permissions de votre agent. Les permissions de votre agent sont celles de votre utilisateur Unix.
C’est la même décision de confiance que lorsque vous rattachez un serveur MCP à un agent sur un VPS, MCP désignant le protocole de contexte du modèle. C’est aussi pour cette raison que l’exécution sûre d’un agent de programmation sur un VPS commence par le compte sous lequel il s’exécute, et non par le modèle. C’est enfin la raison pour laquelle les attaques de la chaîne d’approvisionnement npm sont particulièrement graves sur les serveurs : l’étape d’installation constitue la compromission, et aucune confirmation ne vous est demandée.
D’où viennent les plugins
Les plugins sont stockés dans des profils. Un profil est une composition nommée enregistrée sous $DSH_HOME, qui vaut par défaut ~/.dsh. Chaque répertoire de profil contient les plugins externes qu’il installe. La CLI les gère en transmettant directement vos arguments à pnpm, en utilisant le répertoire du profil comme répertoire de travail.
dsh plugin --profile web add github:deepseek-harness/turtle-ui
dsh plugin --profile web remove turtle-uiComme les arguments parviennent à pnpm sans modification, add, remove, update et why se comportent comme dans n’importe quel projet pnpm. Un plugin peut être un package npm ou une référence GitHub. pnpm doit d’abord être présent dans le PATH. Avec Node 22 et les versions ultérieures, corepack enable pnpm l’y place.
La découverte s’effectue via un topic GitHub. Les auteurs de plugins ajoutent le topic dsh-plugin à leur dépôt. Parcourir ce topic permet de voir les plugins disponibles. Un topic est une étiquette qu’un auteur applique à son propre dépôt. Personne ne le vérifie ni ne le signe. La page du topic classe les dépôts selon leur nombre d’étoiles, ce qui mesure la popularité et non la sécurité.
Quatre habitudes permettent de garder cette approche sous contrôle. Lisez le code source avant l’installation, car la plupart des plugins sont assez petits pour être lus en dix minutes. Épinglez la version exacte ou le commit au lieu de suivre une branche. Exécutez le harness avec un utilisateur qui ne possède rien d’autre, sur un VPS que vous accepteriez de réinstaller. Donnez à l’agent sa propre clé API avec sa propre limite de dépenses, séparée de la clé utilisée par vos services de production. Lorsque vous examinez un plugin et souhaitez savoir quels fichiers présentent un risque, le guide d’audit d’un plugin dsh passe en revue le manifeste, le point d’entrée et les points d’extension qu’un plugin enregistre.
Si vous préférez comparer les architectures avant d’en choisir une, le framework multi-agent Omnigent répond au même problème avec une structure différente. Les compromis deviennent évidents dès que les plugins entrent en jeu. Si vous finissez par en conserver deux ou trois sur le même serveur au lieu d’en retenir un seul, placer tous les frameworks derrière une API auto-hébergée vous évite de créer un tunnel par port. En contrepartie, vous ajoutez un service qui doit être lié à loopback et protégé par un vrai mot de passe dès le premier jour.
Ce qui casse en premier
Node est trop ancien. Le projet cible Node 22.19 et les versions plus récentes de la branche 22.x, ainsi que Node 24 et les versions ultérieures. La CI les teste. Un runtime plus ancien échoue au démarrage, car le code utilise une syntaxe et des API qui n’y existent pas. Exécutez node --version avant toute autre chose.
Le port 3080 est déjà utilisé. Il peut être occupé par un deuxième harness, un processus obsolète ou une autre application qui utilise également le port 3080. Trouvez le processus avec ss -tlnp | grep 3080, puis arrêtez-le ou démarrez le harness ailleurs avec dsh web --port 3180. --port appartient à l’application web et doit donc être exécuté après web.
Le navigateur ne peut pas se connecter via le tunnel. Vérifiez que vous avez ouvert 127.0.0.1, et non l’adresse publique du serveur, car le port redirigé n’existe que sur votre ordinateur portable. Vérifiez ensuite que le harness écoute bien sur le serveur, car SSH configure la redirection, qu’un service réponde ou non à l’autre extrémité.
dsh plugin échoue immédiatement. Cette commande encapsule pnpm. Un binaire pnpm absent l’arrête donc avant le début du traitement des plugins.
L’agent ne voit pas votre projet. Par défaut, le workspace correspond au répertoire dans lequel le processus a démarré. Une unité dont le WorkingDirectory est votre répertoire personnel donne donc à l’agent accès à ce répertoire. Sélectionnez le workspace dans l’interface ou corrigez l’unité, puis rechargez-la.
FAQ
Est-il sûr d’exposer l’interface web de DeepSeek Harness sur le port 3080 ?
Non. Le serveur web n’a pas de système de connexion intégré, et l’agent qui s’exécute derrière modifie des fichiers et lance des commandes shell avec les droits de l’utilisateur qui a démarré le processus. Votre clé API provider est également stockée sur le même disque. Laissez le listener sur 127.0.0.1 et utilisez-le via un tunnel SSH. Un réseau overlay privé ou un reverse proxy qui authentifie chaque requête avant de la transmettre au port convient également. Depuis la version 0.1.0-rc.6, la CLI refuse --host 0.0.0.0 et se termine avec une erreur d’utilisation. Cela indique clairement ce que les auteurs pensent de cette configuration.
Ai-je besoin d’une clé API DeepSeek ou puis-je utiliser un modèle local ?
Les deux sont possibles, car le harness est un runtime, pas un modèle. Dans Settings, puis Models, vous pouvez coller une clé dans la fiche d’un provider du catalogue. Vous pouvez aussi choisir « Add a custom provider » et lui fournir une base URL compatible avec le protocole OpenAI. Un serveur Ollama local répond sur http://127.0.0.1:11434/v1/ et accepte n’importe quelle chaîne dans le champ de clé API. Les clés sont enregistrées dans $DSH_HOME/.credentials.yaml, dont la valeur par défaut est ~/.dsh/.credentials.yaml.
Que donne réellement l’installation d’un plugin DeepSeek Harness au plugin ?
Les permissions du compte qui exécute le harness. Un plugin est du code Node chargé dans le même processus. Les points d’extension incluent le shell backend, la couche filesystem, le registre des outils et les événements qui entourent chaque appel d’outil. Rien n’isole un plugin de ces interfaces, sauf si le plugin fournit lui-même le sandbox. Lisez le code source avant l’installation. Exécutez aussi le harness avec un utilisateur qui ne possède rien d’important pour vous.
Quelle version dois-je installer et continuera-t-elle à fonctionner ?
Installez une version exacte, par exemple npx @deepseek-ai/dsh@0.1.0-rc.6 web. C’est la version vers laquelle pointait le tag latest le 13 août 2026. Le projet se présente comme une developer preview et précise que des changements incompatibles sont prévus. Une commande non épinglée peut donc se comporter différemment d’un jour à l’autre. Consultez le repository avant toute mise à niveau. Attendez-vous à ce que les clés de configuration et les interfaces des plugins évoluent tant que la version commence par 0.