Hister : installer votre moteur de recherche personnel
Installez Hister sur un VPS pour retrouver le texte intégral de vos pages et fichiers. Guide des binaires, Docker, TLS, connexion et endpoint MCP.
Ce qu’est Hister et ce qu’il n’est pas
Hister est un moteur de recherche personnel que vous hébergez vous-même. Il indexe le texte intégral des pages que vous avez consultées et des fichiers que vous conservez, puis vous permet d’effectuer des recherches dans cette collection depuis une interface web, un client en ligne de commande, une API HTTP ou un assistant IA (intelligence artificielle). Hister répond à une seule question : où ai-je lu cela ?
La plupart des lecteurs découvrent cette idée avec SearXNG, mais les deux outils sont différents. Si le nom que vous connaissez est l’ancien Searx, ce projet n’a reçu aucun commit de code depuis 2023 et SearXNG en poursuit le développement, si bien qu’une nouvelle instance que vous déployez aujourd’hui est de toute façon une instance SearXNG. SearXNG est un proxy de métamoteur de recherche. Vous lui envoyez votre requête, il interroge d’autres moteurs en votre nom, puis vous renvoie leurs résultats sans les éléments de tracking. L’index appartient à ces moteurs. Hister construit son propre index à partir des contenus que vous lui fournissez : pages capturées par une extension de navigateur, historique de navigation importé, URL explorées et fichiers présents dans les répertoires que vous lui indiquez. Une instance SearXNG auto-hébergée vous donne un accès privé au Web public. Hister vous permet de rechercher dans vos propres contenus consultés. Ces fonctions sont différentes ; il est donc normal d’exécuter les deux sur la même machine. Dans ce cas, il est utile de savoir dans quelle mesure SearXNG masque réellement vos recherches, car il remplace votre adresse IP par celle de votre serveur auprès des moteurs, sans dissimuler les requêtes elles-mêmes.
Hister est un logiciel libre distribué sous licence AGPLv3 (GNU Affero General Public License, version 3) ou une version ultérieure. Il n’effectue aucune télémétrie et ne nécessite aucun service cloud. Ce guide utilise la version v0.17.0, qui était la version courante le 2026-07-28. Consultez la page des releases pour connaître le tag actuel avant de copier quoi que ce soit, puis utilisez ce tag.
Pourquoi auto-héberger Hister sur un VPS
Un index n’est utile que s’il est complet, et il n’est complet que si le serveur fonctionnait pendant votre lecture. Un ordinateur portable reste en veille la moitié de la journée. Les pages que vous ouvrez sur votre téléphone pendant ce temps ne l’atteignent jamais, et un import nocturne ne démarre pas. Un VPS (serveur privé virtuel) reste disponible en permanence. Tous vos appareils alimentent donc le même index, et le crawler continue de fonctionner pendant votre sommeil.
La deuxième raison est la séparation. La définition de user_handling: true dans la section app donne à chaque compte ses propres identifiants et sa propre collection de documents au sein d’une instance unique. Un seul serveur peut ainsi héberger un foyer ou une petite équipe, sans que personne ne puisse rechercher dans les lectures des autres.
La troisième raison concerne l’infrastructure. Le VPS dispose déjà d’un nom d’hôte public et d’un certificat, ce dont l’extension de navigateur a besoin pour joindre le serveur depuis un réseau que vous ne contrôlez pas. Cette même paire est également utilisée ailleurs sur le serveur, car openGym enregistre sa première passkey sur le nom d’hôte actif à ce moment-là, ce qui signifie que le nom et le certificat doivent être définis avant la création du premier compte.
Chemin d’installation 1 : le binaire de la release
Hister fournit un binaire par plateforme. Téléchargez-le avec le fichier de sommes de contrôle, puis vérifiez-le avant l’installation.
cd /tmp
curl -LO https://github.com/asciimoo/hister/releases/download/v0.17.0/hister_0.17.0_linux_amd64
curl -LO https://github.com/asciimoo/hister/releases/download/v0.17.0/hister_0.17.0_checksums.txt
sha256sum --ignore-missing -c hister_0.17.0_checksums.txtLe résultat attendu est l’unique ligne hister_0.17.0_linux_amd64: OK. Une ligne FAILED signifie que le téléchargement est endommagé ou a été modifié. Téléchargez-le à nouveau au lieu de l’installer.
Installez le binaire, puis créez un compte système et les répertoires qu’il utilisera.
sudo install -m 755 /tmp/hister_0.17.0_linux_amd64 /usr/local/bin/hister
sudo useradd --system --home-dir /var/lib/hister --shell /usr/sbin/nologin hister
sudo install -d -o hister -g hister -m 750 /var/lib/hister
sudo install -d -m 755 /etc/hister
sudo hister create-config /etc/hister/config.ymlcreate-config écrit un fichier de configuration par défaut et confirme également que le binaire s’exécute sur cette machine. Un téléchargement destiné à une architecture incorrecte échoue à cette étape avec cannot execute binary file: Exec format error.
Modifiez les quelques paramètres importants. Vous pouvez conserver le reste du fichier généré tel quel.
app:
directory: /var/lib/hister
access_token: 'paste-a-long-random-string-here'
server:
address: 127.0.0.1:4433
base_url: https://hister.example.comGénérez le token avec openssl rand -hex 32. Le fichier contient désormais un identifiant d’authentification. Restreignez donc ses permissions avant même le démarrage du service.
sudo chown root:hister /etc/hister/config.yml
sudo chmod 640 /etc/hister/config.ymlL’exécuter avec systemd
Écrivez /etc/systemd/system/hister.service :
[Unit]
Description=Hister personal search engine
After=network-online.target
Wants=network-online.target
[Service]
User=hister
Group=hister
Environment=HISTER_CONFIG=/etc/hister/config.yml
ExecStart=/usr/local/bin/hister listen
Restart=on-failure
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/hister
[Install]
WantedBy=multi-user.targetHISTER_CONFIG est la variable d’environnement documentée pour définir le chemin de configuration. L’unité ne dépend donc pas du répertoire personnel du compte hister. ProtectSystem=strict rend l’ensemble du système de fichiers accessible en lecture seule pour ce service. C’est pourquoi ReadWritePaths doit désigner le répertoire de données. ProtectHome=yes masque /home pour le service. Un répertoire surveillé sous /home apparaîtrait donc vide pour l’indexeur. Supprimez cette ligne si vous devez indexer les fichiers qui s’y trouvent.
sudo systemctl daemon-reload
sudo systemctl enable --now hister
systemctl status hister --no-pager
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:4433/Tout code d’état HTTP affiché par cette dernière commande signifie que le processus écoute. curl: (7) Failed to connect signifie que ce n’est pas le cas, et journalctl -u hister -n 50 --no-pager indiquera pourquoi.
Chemin d’installation 2 : Docker Compose
L’image est publiée dans le registre de conteneurs GitHub, avec un tag par version.
services:
hister:
image: ghcr.io/asciimoo/hister:v0.17.0
container_name: hister
user: '1000:1000'
restart: unless-stopped
environment:
- HISTER__SERVER__ADDRESS=0.0.0.0:4433
- HISTER__SERVER__BASE_URL=https://hister.example.com
- HISTER__APP__ACCESS_TOKEN=${HISTER_ACCESS_TOKEN}
volumes:
- ./data:/hister/data
ports:
- 127.0.0.1:4433:4433Chaque clé de configuration peut être remplacée par une variable d’environnement au format HISTER__<SECTION>__<KEY>, avec deux traits de soulignement comme séparateur. Un déploiement de conteneur n’a donc pas besoin de monter un fichier de configuration. Conservez HISTER_ACCESS_TOKEN dans un fichier .env situé à côté du fichier Compose. Si vous préférez modifier un fichier, docker run --rm ghcr.io/asciimoo/hister:v0.17.0 create-config > config.yml affiche les valeurs par défaut.
Les deux lignes ci-dessus sont faciles à configurer incorrectement. Il est utile de comprendre pourquoi.
L’adresse d’écoute dans le conteneur doit être 0.0.0.0:4433. Un conteneur possède son propre espace de noms réseau. Un processus lié à 127.0.0.1 dans cet espace n’est donc accessible que depuis le conteneur. Le port publié n’a alors aucune destination vers laquelle transmettre le trafic.
Le port publié s’écrit 127.0.0.1:4433:4433, et non 4433:4433. Docker publie les ports en ajoutant ses propres règles netfilter. Ces règles sont évaluées avant les règles ufw. Un simple 4433:4433 reste donc accessible depuis Internet, même sur un serveur où ufw status indique que le port est fermé. Lier le côté hôte à 127.0.0.1 fait du reverse proxy le seul point d’accès. Le même piège concerne tous les conteneurs du serveur. Docker Compose sur un VPS explique le reste.
L’image par défaut s’exécute avec l’UID 1000 et le GID 1000. ./data doit donc être accessible en écriture par ce compte. Sinon, le conteneur s’arrête au démarrage avec une erreur de permissions. sudo chown -R 1000:1000 ./data corrige ce problème. Si ces valeurs ne vous sont pas familières, consultez d’abord l’UID et le GID utilisés par un conteneur pour écrire ses fichiers.
Pourquoi un index de recherche personnel est la pire chose à exposer
Hister écoute par défaut sur 127.0.0.1:4433, et cette valeur par défaut est volontaire. Réfléchissez au contenu de l’index après un mois d’utilisation : pages du wiki interne, factures, tickets de support ouverts pendant que vous étiez connecté, pages de réinitialisation de mot de passe et texte intégral de tout le reste de ce que vous avez consulté. La documentation du projet l’indique clairement : « Hister transmet l’intégralité de votre historique de navigation, avec le contenu des pages, vers le serveur et depuis celui-ci. »
Une base de données de mots de passe divulguée doit encore être déchiffrée. Un index personnel divulgué est en texte brut et peut déjà être interrogé. Il nécessite donc davantage de précautions que la petite application auto-hébergée à laquelle il ressemble.
Deux faits en découlent. Hister ne demande aucune authentification par défaut. Un reverse proxy suffit donc à publier une copie interrogeable de vos lectures à toute personne qui connaît le nom d’hôte. Le endpoint MCP est également servi par défaut sur /mcp. Sans token, tout client qui l’atteint peut lancer une recherche dans l’index.
Configurez l’authentification avant que le service ne quitte localhost pour la première fois. Pour un seul utilisateur, app.access_token suffit : il s’agit d’un secret partagé envoyé par l’extension de navigateur, le client terminal et tout client MCP. Pour plusieurs personnes, définissez user_handling: true et créez les comptes :
sudo -u hister hister create-user alice --admin --config /etc/hister/config.ymlLa commande demande un mot de passe d’au moins 8 caractères. Chaque compte possède ses propres documents et un token API personnel, que son propriétaire peut régénérer depuis la page de profil ou avec l’option --regen-token de hister update-user. La génération d’un nouveau token invalide immédiatement le précédent. Tous les appareils utilisés par ce compte doivent donc être mis à jour ensuite.
Ne modifiez pas app.public sans raison précise. Le mode public autorise la recherche sans authentification, les aperçus, le service de fichiers et la recherche MCP, tout en bloquant les écritures, l’accès à l’historique et les opérations d’administration.
Proxy inverse, TLS et pare-feu
Hister ne sert pas directement HTTPS. Il faut donc terminer TLS (transport layer security) en amont. Caddy est la solution la plus simple, car il demande et renouvelle lui-même les certificats via ACME (automatic certificate management environment).
hister.example.com {
reverse_proxy 127.0.0.1:4433
}Rechargez-le avec sudo systemctl reload caddy. Deux conditions doivent être remplies avant l’émission d’un certificat : l’enregistrement A de hister.example.com doit pointer vers ce serveur, et le port 80 doit être ouvert, car le challenge HTTP-01 y reçoit sa réponse. Si l’une de ces conditions n’est pas remplie, le navigateur affiche une erreur TLS au lieu de la page et le journal de Caddy répète l’échec du challenge.
Fermez ensuite tous les autres ports.
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw statusLe port 4433 est volontairement absent de cette liste. Un hostname public n’est pas le seul moyen d’accéder au service, et un service onion pointant vers le même port loopback permet d’atteindre votre index depuis vos propres appareils, sans enregistrement DNS ni port entrant ouvert.
server.base_url doit correspondre à l’adresse saisie dans le navigateur, y compris le schéma. Sinon, l’interface se charge avec du texte sans style et des images manquantes, car le serveur construit les liens vers ses ressources à partir de base_url, puis le navigateur les demande auprès d’une origine qui ne répond pas. Cette même URL doit être indiquée dans l’extension du navigateur.
Remplir l’index
L’extension de navigateur est le principal collecteur. Installez-la depuis Mozilla Add-ons ou le Chrome Web Store, ouvrez sa page d’options, définissez l’URL du serveur sur https://hister.example.com, puis collez le jeton d’accès. Elle capture ensuite le titre, le texte intégral, le HTML et le favicon de chaque page que vous consultez, puis les envoie à votre serveur. L’extraction s’effectue côté client, dans le navigateur. L’extension ne contacte aucun tiers. La seule requête externe qu’elle effectue concerne le favicon de la page.
L’extraction côté client permet de créer un index privé. L’extension voit une page exactement comme vous la voyez, après l’authentification et le rendu. Une page de wiki interne ou un article payant est donc correctement indexé, sans que votre serveur ait besoin de vos identifiants. En contrepartie, tout ce que vous consultez peut être ajouté à l’index. C’est pourquoi les règles d’exclusion doivent être définies avant d’ajouter davantage de contenu.
Les règles d’exclusion se trouvent dans rules.json pour une installation mono-utilisateur, ou par utilisateur dans la base de données. L’onglet Rules de l’interface web est le moyen le plus simple de les modifier. Ce sont des expressions régulières Go comparées à l’URL complète :
^https://mail\.example\.com
^https://bank\.example\.com
.*?utm_source=Un motif tel que ^mail.example.com ne correspond jamais, car la chaîne testée commence par https://. Un $ final échoue également pour toute URL contenant une query string, car les paramètres de requête sont conservés lors de la comparaison.
L’historique existant est importé en lisant la propre base de données du navigateur. La commande doit donc être exécutée sur la machine qui contient le profil du navigateur, c’est-à-dire votre ordinateur portable et non le VPS. Installez-y le même binaire et indiquez-lui le serveur :
export HISTER_TOKEN='your-access-token'
hister import browser firefox -u https://hister.example.com -t "$HISTER_TOKEN"Un import s’exécute comme une tâche reprenable nommée browser-import-YYYY-MM-DD. Vous pouvez donc l’interrompre, puis le relancer plus tard. Les services de gestion de signets s’importent de la même façon, notamment Linkwarden, Karakeep, Wallabag, Linkding, Readeck et Shaarli. Un nouvel import ne récupère que les éléments plus récents que ceux du précédent.
Les fichiers présents sur le serveur sont indexés en indiquant les répertoires dans la configuration :
indexer:
directories:
- path: '/var/lib/hister/documents'
label: 'documents'
filetypes: ['pdf', 'docx', 'md', 'txt']Les fichiers PDF, DOCX, Markdown, Org mode et les fichiers texte UTF-8 valides sont lus intégralement. Les photos et les vidéos ne figurent pas dans cette liste. Une photothèque a donc besoin d’un serveur qui indexe les visages, les lieux et les dates plutôt que le texte. PhotoPrism et Immich sont les deux solutions généralement comparées pour ce rôle. Une page est ajoutée avec hister index https://example.com. Transformer des sites entiers en texte propre pour d’autres outils est une tâche distincte, assurée par des crawlers auto-hébergés qui convertissent les pages en texte propre.
La recherche repose sur des champs. Le langage de requête mérite donc dix minutes de lecture :
"connection reset" domain:github.com added:<30d
title:(wireguard|nftables) -tutorial sort:-visitsDiriger un agent de code vers votre propre index via MCP
MCP (model context protocol) est l’interface qu’un assistant utilise pour appeler des outils sur un serveur. Hister l’expose à l’adresse POST /mcp sous la même URL de base, via le transport HTTP streamable, et fournit search, get_preview et get_history. L’authentification utilise le même bearer token que le reste de l’API. Si l’appel d’outils est nouveau pour vous, écrire vous-même une petite boucle d’agent est le moyen le plus rapide de voir ce qu’un endpoint comme celui-ci transmet réellement à un assistant.
{
"mcpServers": {
"hister": {
"url": "https://hister.example.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
}
}
}Un en-tête X-Access-Token fonctionne comme alternative à Authorization.
L’intérêt vient de ce que l’agent recherche. Une recherche sur le Web renvoie les résultats actuellement les mieux classés. Pour les logiciels qui évoluent rapidement, il s’agit souvent de la documentation d’une version que vous n’utilisez pas. Votre propre index renvoie la page que vous avez déjà consultée et choisi de conserver, et get_preview fournit la copie stockée. La réponse reste donc disponible même si la page d’origine est mise hors ligne. Fournissez les deux sources à l’agent si vous voulez aussi des résultats publics : une compétence de recherche dans un navigateur basée sur SearXNG ajoute le Web ouvert comme outil séparé. Dès que vous utilisez plusieurs de ces endpoints, héberger des serveurs MCP sur un VPS mérite votre attention, car ils partagent tous ce problème d’exposition.
Disque, sauvegardes et maintenance
La documentation estime qu’une page indexée occupe environ 100 KB, aperçu compressé compris. Cent mille pages représentent donc environ 10 GB. Aucun système de quotas n’est prévu. Deux paramètres sont souvent confondus : indexer.max_file_size_mb (1 MiB par défaut) limite la taille d’un fichier surveillé, tandis que server.max_batch_body_size (40 MiB par défaut) limite la taille d’une requête API.
Le répertoire indiqué par app.directory contient index.db, avec les fichiers d’index propres à chaque langue, db.sqlite3 pour les comptes et les tâches, data/html/ pour les aperçus, ainsi que rules.json. Une sauvegarde consiste à arrêter le service, puis à copier l’intégralité de ce répertoire et le fichier de configuration. hister export backup.json exporte les documents au format JSON pour une migration. Ce n’est pas une sauvegarde du serveur.
Deux commandes de maintenance sont utiles. hister reindex reconstruit les index de recherche. Cette opération est nécessaire après toute modification des paramètres d’indexation. Si la consommation mémoire augmente pendant une importation importante, définissez detect_languages: false dans la section indexer, puis réindexez. hister cleanup supprime les fichiers d’aperçu et de favicon orphelins après des suppressions.
La suppression s’effectue avec une requête. Commencez donc par l’exécuter en mode dry run :
hister delete 'domain:example.com' --dry --verboseUne page supprimée réapparaît si un collecteur continue de la soumettre. Ajoutez donc la règle d’exclusion avant de la supprimer.
L’AGPLv3 ne devient pertinente que si vous modifiez le code. L’utilisation d’une copie non modifiée pour votre propre usage ne crée aucune obligation. Si vous modifiez Hister et permettez à d’autres personnes d’utiliser votre version sur un réseau, la licence vous impose de leur proposer le code source modifié.
Modes d’échec et messages affichés
Le serveur ne démarre pas. Le port 4433 est déjà utilisé ou le fichier de configuration contient une erreur de syntaxe YAML. sudo ss -lntp | grep 4433 indique ce qui utilise le port, et journalctl -u hister -n 50 --no-pager affiche l’erreur d’analyse.
L’interface se charge, mais son affichage est incorrect. Du texte désordonné et des images manquantes indiquent que server.base_url ne correspond pas à l’URL affichée dans la barre d’adresse. La présence ou l’absence d’un slash final suffit à créer une différence.
L’extension ne se connecte pas. L’URL du serveur configurée dans l’extension doit être identique à base_url, le serveur doit être démarré et à jour, et un pare-feu intermédiaire peut bloquer la connexion sans afficher de message dans la page. Firefox n’envoie pas les journaux des extensions dans la console standard : ouvrez about:debugging#/runtime/this-firefox et examinez l’extension Hister.
Le conteneur s’arrête au démarrage. Une erreur de permission sur ./data indique que le répertoire appartient à un UID différent de 1000, qui est le compte utilisé dans l’image par défaut.
403 Forbidden depuis une route d’administration. POST /api/reindex et POST /api/cleanup sont réservés aux administrateurs lorsque la gestion des utilisateurs est activée. Un compte ordinaire y est donc refusé.
La consommation mémoire augmente pendant un import. La détection de la langue sur un historique volumineux en est généralement la cause. Définissez detect_languages: false, puis exécutez hister reindex.
FAQ
En quoi Hister diffère-t-il de SearXNG ?
SearXNG est un proxy de métamoteur : il transmet votre requête à des moteurs publics et renvoie leurs résultats sans les éléments de suivi. L’index appartient donc à ces moteurs. Hister conserve son propre index plein texte des pages que vous avez consultées et des fichiers que vous conservez. Il répond à la question « où ai-je lu cela ? », tandis que SearXNG répond à « que dit le Web ? ». Ces outils répondent à des besoins différents, et de nombreuses personnes les exécutent tous les deux sur un même serveur.
Est-il sûr de placer tout mon historique de navigation sur un VPS ?
Uniquement après avoir configuré correctement l’exposition du service. Hister est lié à 127.0.0.1:4433 et n’exige aucune authentification par défaut. Définissez app.access_token ou user_handling: true, placez un reverse proxy avec TLS devant le service et gardez le port 4433 fermé dans le pare-feu. Un index plein texte de vos lectures est en clair. Toute personne qui atteint le port peut donc tout lire sans avoir à casser un chiffrement.
Ai-je besoin de l’extension de navigateur, ou puis-je simplement importer mon historique ?
L’importation sert à effectuer un rattrapage initial unique. Elle lit la base de données d’historique du navigateur. Elle s’exécute donc sur l’ordinateur qui contient le profil du navigateur, et non sur le serveur. L’extension maintient ensuite l’index à jour. Elle capture aussi les pages protégées par une authentification, car elle en extrait le contenu dans le navigateur après le rendu de la page. Une configuration courante consiste à effectuer une importation, puis à installer l’extension.
Un agent de programmation peut-il rechercher dans mon index Hister ?
Oui. Hister est un serveur MCP (model context protocol) accessible à l’adresse POST /mcp sur votre URL de base. Il expose search, get_preview et get_history. Configurez le client pour utiliser https://your-host/mcp avec un en-tête Authorization: Bearer contenant votre jeton d’accès. L’agent recherche alors dans la documentation que vous avez réellement consultée, dans la version que vous avez lue, au lieu d’utiliser les résultats actuellement les mieux classés par un moteur de recherche public.