SSD Nodes Learn 🎉 VPS dès $5.50/mois
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-21

Auto-héberger Hister, votre moteur de recherche personnel

Installez Hister sur un VPS pour retrouver le texte intégral de vos pages et fichiers. Comparez les installations binaire et Docker, TLS, login et endpoint MCP.

Ce qu’est Hister, et ce qu’il n’est pas

Hister est un moteur de recherche personnel que vous auto-hébergez. Il indexe le texte intégral des pages que vous avez consultées et des fichiers que vous conservez. Vous pouvez ensuite effectuer des recherches dans cette collection depuis une interface web, un client terminal, 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 ces deux outils sont différents. SearXNG est un proxy de métamoteur de recherche. Vous lui envoyez votre requête, il interroge d’autres moteurs à votre place, puis vous renvoie leurs résultats sans les éléments de tracking. L’index appartient à ces moteurs. Hister crée 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 situés 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 d’effectuer des recherches dans vos propres lectures. Ces usages sont différents ; il est donc normal d’exécuter les deux sur le même serveur.

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 fixe la version v0.17.0, qui était la version actuelle le 2026-07-28. Consultez la page des releases pour connaître le tag actuel avant de copier quoi que ce soit, puis utilisez le tag que vous y trouverez.

Pourquoi auto-héberger Hister sur un VPS

Un index n’est utile que s’il est complet. 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 lui parviennent jamais, et un import lancé pendant la nuit ne démarre pas. Un VPS (serveur privé virtuel) reste disponible. Tous vos appareils enregistrent alors leurs données dans 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 attribue à chaque compte ses propres identifiants et sa propre collection de documents sur une même instance. Un seul serveur peut ainsi héberger les comptes d’un foyer ou d’une petite équipe, sans que personne ne puisse rechercher dans les lectures des autres.

La troisième raison concerne la connectivité. Le VPS possède déjà un hostname public et un certificat. C’est ce dont l’extension de navigateur a besoin pour joindre le serveur depuis un réseau que vous ne contrôlez pas.

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.txt

Un résultat correct se limite à la ligne hister_0.17.0_linux_amd64: OK. Une ligne FAILED indique que le téléchargement est endommagé ou a été altéré. Téléchargez-le de 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.yml

create-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.com

Gé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.yml

Exécuter le service 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.target

HISTER_CONFIG est la variable d’environnement documentée pour 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 y indexer des fichiers.

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 la dernière commande signifie que le processus est en écoute. curl: (7) Failed to connect signifie qu’il ne l’est pas, 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 release.

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:4433

Chaque clé de configuration possède une surcharge via une variable d’environnement de la forme HISTER__<SECTION>__<KEY>, avec deux underscores 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 à 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 les deux points concernés.

L’adresse à l’intérieur du 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 ce conteneur. Le port publié n’a alors aucune destination vers laquelle transmettre le trafic.

Le port publié doit être é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. La page Docker Compose sur un VPS couvre les autres aspects.

L’image par défaut s’exécute avec l’UID 1000 et le GID 1000. ./data doit donc être accessible en écriture pour 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 numéros ne vous sont pas familiers, consultez d’abord l’UID et le GID utilisés par un conteneur pour écrire des fichiers.

Pourquoi un index de recherche personnel est la pire chose à exposer

Hister écoute par défaut sur 127.0.0.1:4433, et ce choix est délibéré. Pensez 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 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 mots de passe divulguée doit encore être cassée. Un index personnel divulgué est en clair et déjà interrogeable. Il exige donc davantage de précautions que la petite application auto-hébergée à laquelle il ressemble.

Deux faits en découlent. Hister n’exige aucune authentification par défaut. Un reverse proxy suffit donc à publier une copie interrogeable de vos lectures à quiconque connaît le nom d’hôte. Le endpoint MCP est également servi par défaut sur /mcp. Sans token, tout client qui peut l’atteindre 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é transmis 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.yml

La commande demande un mot de passe d’au moins 8 caractères. Chaque compte possède ses propres documents et un token API personnel. Le propriétaire peut régénérer ce token depuis la page de profil ou avec l’option --regen-token sur hister update-user. La génération d’un nouveau token invalide immédiatement l’ancien. 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 les recherches sans authentification, les aperçus, le service de fichiers et les recherches MCP, tout en bloquant les écritures, l’accès à l’historique et les opérations d’administration.

Reverse proxy, TLS et pare-feu

Hister ne sert pas directement le HTTPS. Terminez donc le TLS (transport layer security) en amont. Caddy est la solution la plus simple, car il demande et renouvelle automatiquement les certificats via ACME (automatic certificate management environment).

hister.example.com {
    reverse_proxy 127.0.0.1:4433
}

Rechargez sa configuration avec sudo systemctl reload caddy. Deux conditions doivent être réunies pour qu’un certificat puisse être délivré : l’enregistrement A de hister.example.com doit pointer vers ce serveur et le port 80 doit être ouvert, car le challenge HTTP-01 reçoit sa réponse sur ce port. Si l’une de ces conditions n’est pas remplie, le navigateur affiche une erreur TLS au lieu de la page et le journal 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 status

Le port 4433 est volontairement absent de cette liste.

server.base_url doit correspondre exactement à l’adresse saisie dans le navigateur, schéma inclus. Si ce n’est pas le cas, l’interface se charge avec du texte sans mise en forme et des images manquantes, car le serveur construit les liens vers ses ressources à partir de base_url, puis le navigateur les demande à une origine qui ne répond pas. Utilisez cette même URL 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 access token. 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 est effectuée côté client, dans le navigateur. L’extension ne contacte aucun service tiers ; la seule requête externe qu’elle effectue concerne le favicon de la page.

L’extraction côté client rend possible 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 des identifiants. Cela signifie aussi que tout ce que vous consultez peut être ajouté à l’index. C’est pourquoi il faut définir les règles d’exclusion 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 qui sont comparées à l’URL complète :

^https://mail\.example\.com
^https://bank\.example\.com
.*?utm_source=

Un motif comme ^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 pendant 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 laptop 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 un job pouvant être repris, nommé browser-import-YYYY-MM-DD. Vous pouvez donc l’interrompre et le relancer plus tard. Les services de signets sont importés de la même manière, 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 dans leur intégralité. 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, prise en charge 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:-visits

Donnez à un agent de code l’accès à 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 à 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.

{
  "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 des recherches effectuées par l’agent. Une recherche web ouverte renvoie les résultats les mieux classés aujourd’hui. Pour les logiciels qui évoluent rapidement, il s’agit souvent de la documentation d’une version que vous n’exécutez pas. Votre propre index renvoie la page que vous avez déjà lue 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 devient inaccessible. Donnez les deux sources à l’agent si vous voulez aussi des résultats publics : une compétence de recherche dans un navigateur alimentée par SearXNG ajoute le web ouvert comme outil distinct. Dès que vous exécutez plusieurs de ces endpoints, héberger des serveurs MCP sur un VPS mérite votre attention, car chacun présente le même 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. Il n’existe aucun système de quotas. 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 à connaître. hister reindex reconstruit les index de recherche. Cette opération est nécessaire après toute modification des paramètres de l’indexeur. Si l’utilisation mémoire augmente pendant un import volumineux, définissez detect_languages: false dans la section indexer, puis reconstruisez les index. hister cleanup supprime les fichiers d’aperçu et de favicon orphelins laissés par les suppressions.

La suppression s’effectue avec une requête. Exécutez-la d’abord en mode dry run :

hister delete 'domain:example.com' --dry --verbose

Une page supprimée réapparaît si un collecteur continue de l’envoyer. 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 inchangée pour vous-même n’impose aucune obligation. Si vous modifiez Hister et permettez à d’autres personnes d’utiliser votre version sur un réseau, la licence vous oblige à leur fournir 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 quel processus utilise le port, et journalctl -u hister -n 50 --no-pager affiche l’erreur d’analyse.

L’interface se charge, mais s’affiche mal. Un texte brouillé et des images manquantes indiquent que server.base_url ne correspond pas à l’URL affichée dans la barre d’adresse. Une barre oblique finale 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. Un firewall intermédiaire peut aussi bloquer la connexion sans afficher de message dans la page. Firefox conserve les journaux des extensions séparément de 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 signifie que le répertoire appartient à un UID différent de 1000, qui est le compte utilisé dans l’image par défaut.

403 Forbidden sur une route d’administration. POST /api/reindex et POST /api/cleanup sont réservées aux administrateurs lorsque la gestion des utilisateurs est activée. Un compte ordinaire y est donc refusé.

La 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 est-il différent de SearXNG ?

SearXNG est un proxy de métarecherche : 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 full-text des pages que vous avez consultées et des fichiers que vous gardez. Il répond à la question « où ai-je lu cela ? », tandis que SearXNG répond à la question « que dit le Web ? ». Ces outils répondent à des besoins différents. De nombreuses personnes les exécutent sur le même serveur.

Est-il sûr de stocker tout mon historique de navigation sur un VPS ?

Uniquement après avoir correctement configuré l’exposition du service. Hister est lié à 127.0.0.1:4433 et ne demande aucune authentification par défaut. Définissez app.access_token ou user_handling: true, placez un reverse proxy avec TLS devant le service et laissez le port 4433 fermé sur le firewall. Un index full-text de vos lectures est du texte brut. Toute personne qui peut atteindre 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’import constitue un backfill unique. Il lit la propre base de données d’historique du navigateur. Il 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 également les pages derrière une authentification, car elle en extrait le contenu dans le navigateur après le rendu de la page. Une configuration courante consiste à effectuer un import, 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 à POST /mcp sur votre URL de base. Il expose search, get_preview et get_history. Configurez le client avec https://your-host/mcp et un en-tête Authorization: Bearer contenant votre access token. L’agent peut alors rechercher dans la documentation que vous avez réellement lue, dans la version que vous avez consultée, au lieu d’utiliser les résultats actuellement mis en avant par un moteur de recherche public.