SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor

Auto-héberger open-kritt pour scanner votre code

Installez open-kritt sur un VPS avec Docker Compose, figez une release, accédez à l’interface via un tunnel SSH sur le port 5173 et définissez votre budget.

Pourquoi auto-héberger open-kritt sur un VPS plutôt que sur votre ordinateur portable

Auto-hébergez open-kritt sur un serveur que vous pouvez détruire et recréer. L’outil exécute ses agents d’analyse en tant que root dans des conteneurs de jobs temporaires, fournit à chacun une copie inscriptible de votre code et un accès direct à Internet, puis monte le socket Docker de l’hôte dans son service engine. Ce compromis est raisonnable sur une machine dédiée à cette tâche. Il est mauvais sur la machine qui contient vos clés SSH.

Quatre propriétés de la configuration par défaut justifient cette recommandation. Elles proviennent toutes du README et du fichier compose du projet.

Les agents sont conçus pour être puissants. Le README indique que les agents dotés d’outils s’exécutent en tant que root dans des conteneurs de jobs temporaires, avec des copies inscriptibles des dépôts et un accès direct à Internet. Ils peuvent donc installer des outils, compiler des cibles, exécuter des tests et construire des preuves de concept. Une analyse n’est pas un linter qui lit des fichiers. Il s’agit d’une exécution de code arbitraire que vous avez explicitement demandée. Cet accès à Internet présente un risque dans les deux sens : tout ce qu’un agent récupère pendant l’analyse d’une cible est du texte non fiable qui arrive dans son prompt. Vous êtes exposé de la même manière lorsque vous donnez à un agent son propre accès à la recherche web.

L’engine détient le socket Docker. docker-compose.yml monte le socket Docker de l’hôte dans le service engine, car l’engine construit et lance un conteneur d’analyse pour chaque job. Tout processus qui peut accéder à ce socket peut démarrer un conteneur montant le système de fichiers de l’hôte. L’engine dispose donc, en pratique, des privilèges root sur l’hôte qui l’exécute.

Il n’y a pas d’écran de connexion. Le backend est fourni sans authentification applicative. L’accès au port donne accès à vos résultats et au crédit de votre fournisseur.

Le code analysé ne vous appartient souvent pas. Pointer les agents vers un dépôt tiers revient à exécuter le build de ce dépôt sur votre machine, en tant que root et avec un accès réseau.

Si vous avez lu pourquoi les agents de programmation doivent rester dans une VM temporaire, il s’agit du même modèle de menace, mais plus puissant. Donnez à open-kritt un VPS qui n’héberge rien d’autre et administrez ce VPS depuis un compte utilisateur distinct avec le principe du moindre privilège, plutôt que depuis root.

Ce que fait réellement open-kritt

open-kritt (le dépôt est Kritt-ai/open-kritt et est distribué sous licence AGPL-3.0) décompose la recherche de vulnérabilités en petites tâches, exécute ces tâches en parallèle avec plusieurs agents IA, puis déduplique et classe les résultats. Vous définissez un workflow sous la forme d’une chaîne de prompts ciblés. Chaque étape reçoit le contexte structuré des étapes précédentes. La cible du scan est un dépôt git distant ou local. Le moteur d’analyse est Codex ou Claude Code. Lorsqu’un résultat potentiel apparaît, des post-scripts facultatifs peuvent tenter de le valider ou de construire une preuve de concept. Concevoir cette chaîne relève du fonctionnement courant des agents, et non de la sécurité. Si les prompts, les outils et le transfert de contexte vous sont encore peu familiers, apprendre comment les agents sont construits vous aidera davantage que n’importe quel réglage présenté dans ce guide.

À la fin, vous obtenez une liste classée de résultats potentiels. Utilisez-la comme une file de triage, et non comme un rapport.

Ce dont vous avez besoin avant de commencer

  • Un VPS exécutant Ubuntu 24.04, Debian 12 ou Rocky Linux 9. La documentation d’installation indique qu’il s’agit des distributions testées, sur x86_64 et ARM64.
  • Docker Engine avec le plugin Compose.
  • Node.js 20 ou une version ultérieure sur l’hôte, car la CLI ./kritt s’exécute sur l’hôte et non dans un conteneur.
  • Un fournisseur de modèles : un compte Codex, ou OPENAI_API_KEY, CODEX_API_KEY, ANTHROPIC_API_KEY ou OPENROUTER_API_KEY.
  • GITHUB_TOKEN uniquement si vous prévoyez d’analyser des dépôts privés. Le .env.example fourni l’indique clairement : un token GitHub seul ne permet pas d’exécuter des analyses.

Installer d’abord Docker et Node 20

curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER

Déconnectez-vous, puis reconnectez-vous pour appliquer l’appartenance au nouveau groupe. Vérifiez ensuite que le plugin Compose est présent.

docker compose version

Une chaîne de version indique que Compose est installé comme plugin. docker: 'compose' is not a docker command indique que vous disposez à la place de l’ancien binaire autonome docker-compose, et open-kritt appelle docker compose. L’appartenance au groupe docker équivaut à des privilèges root sur l’hôte. Ajoutez-y uniquement le compte qui exécute open-kritt. Pour la procédure complète, consultez exécuter Docker sur un VPS.

Ubuntu 24.04 fournit Node 18 dans son propre dépôt, et la CLI s’arrête avec toute version antérieure à 20. Node 20 a depuis atteint sa fin de vie et ne reçoit plus de mises à jour. Installez donc Node 22 depuis NodeSource. Il satisfait le même contrôle « 20 ou version ultérieure ».

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
node -v

node -v doit afficher v22. ici. Toute valeur inférieure à v20. sera refusée par la CLI. Sur Rocky Linux 9, l’équivalent est sudo dnf module enable nodejs:22 -y, suivi de sudo dnf install -y nodejs.

Cloner open-kritt et figer une version étiquetée

git clone https://github.com/Kritt-ai/open-kritt
cd open-kritt
git fetch --tags
git tag --list
git checkout v1.4.1

main évolue sous votre contrôle. Un tag, non. Au 12 octobre 2026, le tag le plus récent est v1.4.1, publié le 16 août 2026, et git tag --list indique l’état du dépôt le jour du clonage. v1.4.0 (12 août 2026) a ajouté l’admission des runners tenant compte de la mémoire, décrite dans la section sur le dimensionnement. v1.4.1 a corrigé un bug qui faisait ignorer au ranker les règles de classement fournies par l’utilisateur. Le checkout d’un tag place le dépôt dans l’état detached HEAD. C’est le comportement attendu ici : vous utilisez ce clone comme un déploiement figé, et non comme une branche sur laquelle vous effectuez des commits. Pour effectuer une mise à niveau ultérieure, consultez les notes de version, exécutez git fetch --tags, faites le checkout du nouveau tag, puis exécutez à nouveau ./kritt start, car start reconstruit les images.

N’exécutez pas ./kritt avec sudo. La documentation l’indique explicitement. La CLI gère les répertoires d’identifiants propres au projet sous .data/. Une exécution en tant que root laisse donc ces répertoires appartenir à root, et l’exécution normale suivante ne peut pas y écrire.

Configurer l’accès aux modèles avec ./kritt setup

./kritt setup

La commande crée .env à partir de .env.example s’il n’existe pas, affiche l’état de chaque identifiant et permet de les définir ou de les supprimer. Elle n’affiche jamais les valeurs dans le terminal. .env et le fichier d’identifiants du moteur sont tous deux écrits avec le mode 0600.

Si vous préférez le faire manuellement :

cp .env.example .env
chmod 600 .env
mkdir -p .data/codex
chmod 700 .data/codex

Modifiez ensuite la clé du fournisseur dans .env et conservez le fichier avec le mode 0600. Dans les deux cas, un identifiant de fournisseur fonctionnel se trouve désormais sur ce serveur. C’est une raison supplémentaire de ne rien héberger d’autre sur cette machine. Créez une clé dédiée à ce seul projet afin que sa révocation ultérieure ne perturbe rien d’important. Garder les secrets hors de portée des agents IA présente cette pratique plus largement.

Définir un plafond de dépenses chez le fournisseur avant le premier scan

open-kritt est conçu pour répartir les tâches, et c’est cette répartition qui est facturée. Les valeurs par défaut de .env.example dans la version v1.4.1 sont prudentes : ENGINE_WORKER_COUNT=2, décrit dans le fichier comme une valeur par défaut prudente pour une petite machine équipée de 2 vCPU, et ENGINE_MAX_CONCURRENT_SCANS=1. Au-dessus se trouve ENGINE_WORKERS_PER_ACCOUNT=15, le nombre maximal d’appels concurrents au modèle principal autorisés sur un compte fournisseur, ainsi que ENGINE_CODEX_MAX_SUBAGENTS_PER_SESSION=5, car une session Codex peut exécuter jusqu’à cinq agents enfants. Si vous augmentez le nombre de workers sur un VPS plus puissant, le nombre d’appels au modèle en cours augmente également.

Le dépôt ne limite pas vos dépenses. .env.example ne contient aucun paramètre de budget. Les seules conditions d’arrêt propres au moteur sont ces limites de workers et ENGINE_HARNESS_TIMEOUT_SECONDS, qui vaut par défaut 7200 secondes par exécution du harness. Ici, un harness désigne la boucle qui continue d’appeler le modèle avec des outils et du contexte jusqu’à l’arrêt de l’exécution. Ce délai correspond donc à une durée maximale mesurée par l’horloge pour le programme qui encapsule le modèle, et non à une limite de dépenses du modèle dans ce programme. Le plafond doit donc être défini chez le fournisseur. Ouvrez la console de votre fournisseur et définissez une limite mensuelle stricte avant le premier scan, et non après. Contrôler le coût d’un agent IA sur un VPS détaille les paramètres propres à chaque fournisseur.

Il existe également un frein local. Définir ENGINE_WORKER_COUNT=0 suspend la prise en charge de nouveaux jobs. Vous pouvez aussi modifier les mêmes valeurs de workers dans l’écran Settings une fois la stack démarrée.

Ce guide n’indique aucun prix par scan, car le coût dépend de la taille du dépôt, du workflow que vous construisez et du modèle utilisé. Lancez un scan sur un petit dépôt, puis consultez la page d’utilisation de votre fournisseur avant de cibler un dépôt volumineux.

Démarrer la stack et vérifier son état

./kritt start

Cette commande vérifie .env et au moins un identifiant, puis exécute docker compose up --build. Le premier build est lent, car il construit les images du frontend, du backend, du moteur, de la vue executor et de la base de données. Il s’exécute aussi au premier plan. La fermeture de la session SSH arrête donc la stack. Démarrez-la dans tmux, ou démarrez-la en mode détaché une fois le premier build terminé. Ces deux méthodes ne survivent pas seules à un redémarrage. Si vous voulez que la stack redémarre après celui-ci, le modèle d’unité systemd présenté dans maintenir un agent auto-hébergé après les redémarrages s’applique directement.

docker compose up -d --build
docker compose ps

docker compose ps doit lister open-kritt-frontend, open-kritt-backend, open-kritt-engine, open-kritt-executor-view et open-kritt-db. Vérifiez ensuite que le backend répond directement sur le serveur.

curl -s http://127.0.0.1:3002/api/health

Une réponse JSON indique que le backend fonctionne. Failed to connect to 127.0.0.1 port 3002: Connection refused indique le contraire, et docker compose logs backend en précise la cause. Arrêtez tous les composants avec docker compose down depuis le répertoire du dépôt.

Une étape facultative : docker compose exec backend npm run seed charge des données de démonstration. C’est un moyen simple de vérifier l’interface avant de lancer un véritable scan.

Accéder à l’interface sur le port 5173 via un tunnel SSH

Chaque service du fichier compose est lié à 127.0.0.1 par défaut : le frontend sur 5173, le backend sur 3002, la vue de l’exécuteur sur 8090 et Postgres sur 5432. Ne modifiez pas ces liaisons. Transférez le port via SSH depuis votre propre machine.

ssh -N -L 5173:127.0.0.1:5173 you@your-server-ip

Ouvrez http://localhost:5173 dans votre navigateur local pendant l’exécution de cette commande. -N signifie que la connexion effectue la redirection et n’ouvre pas de shell. Ajoutez un second -L 8090:127.0.0.1:8090 à la même commande lorsque vous voulez également accéder à la vue de l’exécuteur.

La tentation consiste à définir FRONTEND_BIND_ADDRESS=0.0.0.0 et à supprimer le tunnel. Ne le faites pas. Le backend n’a pas d’écran de connexion. Toute personne qui atteint cette page peut lancer des analyses et consommer le crédit de votre fournisseur. À titre de comparaison, Vaultwarden est conçu pour être exposé sur Internet, et son durcissement repose toujours sur le jeton d’administration et le fichier de sauvegarde, deux leviers qu’open-kritt ne vous fournit pas. Un second piège se trouve en dessous : un port de conteneur publié est traité avant l’application de la stratégie par défaut d’ufw. Une règle ufw deny 5173 semble donc correcte, mais ne bloque rien. Les ports Docker qui contournent ufw présente la chaîne de règles à l’origine de ce comportement.

Dimensionnement du VPS

ENGINE_MIN_FREE_STORAGE_GB vaut 20 par défaut. Le moteur refuse de démarrer un nouveau conteneur de scan par tâche lorsque l’espace de stockage disponible passe en dessous de cette valeur. Les images générées, le cache des checkouts, les données Postgres et les espaces de travail des tâches résident tous sur le même disque. Un VPS de 20 GB ne démarre donc jamais de scan. Considérez 40 GB comme le minimum, et prévoyez davantage si vous analysez de gros dépôts. Évitez de récupérer de l’espace en installant à côté un service gourmand en stockage. Cette comparaison de PhotoPrism et Immich montre à quelle vitesse une bibliothèque multimédia consommerait la marge nécessaire à un scan, avec ses besoins minimaux mesurés en RAM et sur disque. Il en va de même pour les ajouts décoratifs qui semblent légers à côté d’un scanner : un front-end de navigateur qui transforme l’interface d’une bibliothèque Jellyfin en magasin de location des années 90 entraîne tout de même un serveur multimédia complet et ses transcodages sur le disque. Installez-le donc sur un autre hôte.

La mémoire se calcule simplement. Les paramètres correspondants ont été ajoutés dans v1.4.0. Ils s’appliquent donc au tag épinglé plus haut, mais pas aux versions plus anciennes. ENGINE_MEMORY_RESERVE_GB=2 réserve de la mémoire au moteur, à la base de données, à l’API et aux surcharges de courte durée. Chaque runner de scan dispose ensuite d’une réservation souple de ENGINE_SCAN_RUNNER_MEMORY_RESERVATION_MB=1536 et d’une limite stricte de ENGINE_SCAN_RUNNER_MEMORY_MB=1536. Deux workers nécessitent donc environ 5 GB avant même le démarrage des autres services. Le moteur n’autorise que les runners qui tiennent dans le budget restant. Sur une petite machine, les scans sont donc mis en file d’attente au lieu d’échouer. C’est un comportement bien préférable à l’intervention de l’out-of-memory killer. Le même calcul fixe le minimum nécessaire à tout outil qui attribue un conteneur à chaque unité de travail. C’est pourquoi le conteneur et le navigateur d’OpenBot pour chaque collègue IA atteint une limite de RAM bien avant une limite de CPU.

Deux paramètres de purge valent true par défaut : ENGINE_AUTO_PRUNE_DOCKER_BUILD_CACHE et ENGINE_AUTO_PRUNE_UNUSED_DOCKER_IMAGES. Une fois la tâche terminée, le moteur supprime le cache de build inutilisé, les images inutilisées et les conteneurs de scan arrêtés. Les images référencées par un conteneur, les conteneurs de service, les bind mounts, les données de la base de données, les credentials et les volumes sont conservés. C’est une raison supplémentaire de ne pas partager l’hôte : un pruner que vous n’avez pas configuré agit sur ce Docker daemon.

Paramètres du moteur que la plupart des utilisateurs finissent par modifier
  • ENGINE_WORKER_COUNT : nombre total de slots de workers partagés entre les étapes de scan et le post-traitement. Définissez-le sur 0 pour suspendre la prise en charge des nouvelles tâches.
  • ENGINE_MAX_CONCURRENT_SCANS : nombre de scans autorisés simultanément. Les scans en attente patientent jusqu’à ce que le pool actif soit vide.
  • ENGINE_MAX_WORKERS_PER_SCAN : 0 répartit équitablement les slots agrégés entre les scans.
  • ENGINE_HARNESS_TIMEOUT_SECONDS : 7200 par défaut. Il s’agit de la durée maximale d’une tâche unique qui s’emballe. Le moteur accepte les valeurs comprises entre 60 et 86400. Toute valeur en dehors de cette plage, y compris 0, revient à 7200.
  • ENGINE_MIN_FREE_STORAGE_GB : seuil minimal de stockage. ENGINE_IGNORE_LOW_STORAGE=true désactive cette protection, et le fichier avertit que le disque de l’hôte peut alors être saturé.
  • ENGINE_SCAN_RUNNER_MEMORY_MB : limite stricte de mémoire par runner. 0 supprime cette limite.
  • ENGINE_SCAN_RUNNER_MEMORY_RESERVATION_MB : réservation souple par runner prise en compte lors de l’admission. La réduire entraîne une surallocation de la mémoire inactive, tandis que la limite stricte contient toujours les pointes.

Analyser un dépôt local sans l’exposer

LOCAL_REPOS_PATH est défini par défaut sur ./local_repos et est monté en bind en lecture seule dans les conteneurs backend et engine. Un dépôt que vous placez dans ce dossier sur l’hôte apparaît donc immédiatement dans les conteneurs. Utilisez un clone récent, pas votre arborescence de travail. Le conteneur de tâche dispose d’une copie accessible en écriture, de root à l’intérieur du conteneur et d’un accès sortant à Internet. Tout ce qui se trouve dans cette copie peut donc être modifié ou envoyé hors du serveur. Supprimez les fichiers .env et les clés privées avant d’y copier un projet.

Résultats obtenus et limites

Vous obtenez des résultats candidats classés. Vous n’obtenez pas des vulnérabilités vérifiées. Le classement et la déduplication déterminent l’ordre de votre file de triage. Ils ne prouvent pas qu’une entrée est réelle. Les post-scripts peuvent tenter une validation et construire une preuve de concept. C’est le signal le plus fort fourni par l’outil. Cependant, l’échec d’un post-script ne prouve pas que le résultat est faux. Une personne doit toujours examiner chaque candidat. Cette différence entre un candidat et une preuve explique pourquoi demander à un agent des éléments que vous pouvez reproduire vous-même est utile ici : un résultat que vous pouvez reproduire à la demande vaut plus qu’un résultat classé que vous devez accepter sans vérification.

Ce guide ne prétend pas indiquer combien de bugs réels open-kritt détecte, car nous ne l’avons pas mesuré. Quiconque cite un taux de détection pour votre codebase ne l’a pas exécuté sur votre codebase. Analysez d’abord un dépôt que vous connaissez déjà bien : les résultats que vous pouvez évaluer vous-même constituent le moyen de calibration le moins coûteux.

L’autorisation est encore plus importante ici qu’avec la plupart des outils auto-hébergés. Les agents compilent et exécutent du code, et accèdent au réseau. Une étape de preuve de concept peut donc toucher des systèmes en production. Utilisez l’outil uniquement sur du code qui vous appartient ou que vous êtes autorisé à tester dans le cadre d’un contrat, et définissez par écrit le périmètre cible avant toute exécution. Si vous configurez ANTHROPIC_API_KEY et utilisez le moteur Claude Code, les pratiques de sandboxing décrites dans exécuter Claude Code en toute sécurité sur un VPS s’appliquent également à ces agents.

FAQ

Pourquoi open-kritt a-t-il besoin de son propre VPS ?

Parce que ses agents d’analyse s’exécutent avec les privilèges root dans des conteneurs de tâches jetables, avec des copies inscriptibles de votre code et un accès direct à Internet. De plus, le service moteur monte le socket Docker de l’hôte afin de lancer un conteneur par tâche. Tout processus qui accède à ce socket peut démarrer un conteneur montant le système de fichiers de l’hôte. Il faut donc considérer toute la stack comme équivalente à root sur l’hôte. Sur un VPS dédié, ce compromis est acceptable et la reconstruction du serveur ne vous coûte rien. Sur votre poste de travail quotidien, vos clés SSH et vos profils de navigateur se retrouvent dans la même limite de confiance que le code analysé.

Puis-je exposer le port 5173 au lieu d’utiliser un tunnel SSH ?

Vous ne devriez pas. Le backend est fourni sans authentification applicative. Le port est donc le seul élément entre Internet, vos résultats d’analyse et votre crédit fournisseur. Le fichier Compose lie chaque service à 127.0.0.1 pour cette raison. Exécutez plutôt ssh -N -L 5173:127.0.0.1:5173 you@your-server-ip, puis ouvrez http://localhost:5173 en local. Une règle ufw ne constitue pas une protection équivalente, car un port Docker publié est traité avant l’application de la stratégie par défaut d’ufw.

Comment empêcher open-kritt de dépenser plus que prévu ?

Définissez une limite stricte dans la console de votre fournisseur de modèles avant la première analyse, car open-kritt ne possède pas de paramètre de budget propre. Conservez les valeurs de concurrence fournies par défaut lors des premières exécutions, ENGINE_WORKER_COUNT=2 et ENGINE_MAX_CONCURRENT_SCANS=1. Notez qu’un compte fournisseur autorise par défaut jusqu’à 15 appels concurrents au modèle root, tandis qu’une session Codex peut exécuter jusqu’à cinq agents enfants. ENGINE_WORKER_COUNT=0 suspend la prise en charge des nouvelles tâches. C’est l’arrêt local le plus rapide.

Quelle version dois-je extraire ?

Un tag, jamais main. git fetch --tags suivi de git tag --list affiche les versions disponibles. v1.4.1, publié le 16 août 2026, est le plus récent au 12 octobre 2026. Épingler une version garantit qu’une reconstruction effectuée plusieurs mois plus tard produit la même stack. Cela transforme la mise à niveau en décision prise après lecture des notes de version, plutôt qu’en effet secondaire d’un clonage effectué un autre jour.

Une analyse ne démarre jamais. Que dois-je vérifier ?

Vérifiez d’abord l’espace disque disponible, car le moteur ne lance pas de conteneur d’analyse par tâche lorsque l’espace libre est inférieur à ENGINE_MIN_FREE_STORAGE_GB, dont la valeur par défaut est de 20 GB. Vérifiez ensuite que ENGINE_WORKER_COUNT n’est pas égal à 0, car cette valeur suspend la prise en charge des nouvelles tâches. Confirmez enfin qu’un identifiant de modèle est réellement configuré en exécutant ./kritt setup, car un GITHUB_TOKEN seul ne peut pas lancer d’analyse. docker compose logs engine indique la raison pour laquelle la tâche a été ignorée.