SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-28

Hister self-hosted: il tuo motore di ricerca personale

Installa Hister su un VPS per cercare il testo completo delle pagine visitate e dei file: guida a binari, Docker, TLS, login ed endpoint MCP.

Cos’è Hister e cosa non è

Hister è un motore di ricerca personale che puoi gestire sul tuo server. Indicizza il testo completo delle pagine che hai visitato e dei file che conservi, quindi ti permette di cercare in questa raccolta tramite un’interfaccia web, un client da terminale, un’API HTTP o un assistente basato sull’AI (intelligenza artificiale). Hister risponde a una sola domanda: dove l’ho letto.

Molti lettori incontrano questo concetto attraverso SearXNG, ma i due strumenti non coincidono. Se il nome che conoscete è il vecchio Searx, quel progetto non riceve commit di codice dal 2023 e SearXNG ne porta avanti lo sviluppo, quindi una nuova istanza che configurate oggi è comunque SearXNG. SearXNG è un proxy di metaricerca. La query viene inviata a SearXNG, che interroga altri motori per vostro conto e restituisce i risultati dopo aver rimosso il tracciamento. L'indice appartiene a quei motori. Hister crea il proprio indice a partire dai contenuti che gli fornite: pagine acquisite da un'estensione del browser, cronologia del browser importata, URL sottoposti a crawling e file nelle directory che indicate. Un'istanza SearXNG self-hosted vi offre accesso privato al web pubblico. Hister vi consente di cercare nei contenuti che avete letto. Le funzioni sono diverse, quindi è normale eseguirli entrambi sullo stesso server. In tal caso, è utile sapere quanto delle vostre ricerche SearXNG nasconde realmente, perché sostituisce il vostro IP con quello del server presso i motori, invece di nascondere le query stesse.

Hister è software libero distribuito con licenza AGPLv3 (GNU Affero General Public License, versione 3) o successiva. Non raccoglie dati di telemetria e non richiede servizi cloud. Questa guida fissa la versione v0.17.0, che era la release corrente il 2026-07-28. Prima di copiare qualsiasi comando, controlla la pagina delle release per verificare il tag corrente, quindi fissa il tag che trovi.

Perché ospitare autonomamente Hister su un VPS

Un indice è utile solo se è completo, e può essere completo soltanto se il server era in esecuzione mentre leggevate. Un laptop entra in sospensione per metà della giornata. Le pagine aperte sul telefono durante quel periodo non lo raggiungono mai e un'importazione notturna non viene avviata. Un VPS (virtual private server) resta attivo, quindi ogni dispositivo che possedete invia i contenuti allo stesso indice e il crawler continua a funzionare mentre dormite.

Il secondo motivo è la separazione. Impostare user_handling: true nella sezione app assegna a ogni account le proprie credenziali e la propria raccolta di documenti su una singola istanza. Un solo server può quindi ospitare un nucleo familiare o un piccolo team, senza che qualcuno possa cercare nei contenuti di lettura degli altri.

Il terzo motivo riguarda l’infrastruttura di base. Il VPS dispone già di un hostname pubblico e di un certificato, necessari all’estensione del browser per raggiungere il server da una rete che non controlli. La stessa coppia svolge un ruolo anche in altri punti del server, perché openGym registra la prima passkey associandola all'hostname attivo in quel momento, quindi il nome e il certificato devono essere definiti prima di creare il primo account.

Metodo di installazione 1: il binario della release

Hister distribuisce un binario per ogni piattaforma. Scaricatelo insieme al file dei checksum e verificate il download prima dell'installazione.

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 risultato corretto è costituito dalla sola riga hister_0.17.0_linux_amd64: OK. La presenza di una riga FAILED indica che il download è danneggiato o alterato. Scaricatelo nuovamente invece di installarlo.

Installate il binario, quindi create un account di sistema e le directory che utilizzerà.

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 scrive un file di configurazione predefinito e dimostra anche che il binario può essere eseguito su questa macchina. Un download per l'architettura errata fallisce proprio a questo punto, con cannot execute binary file: Exec format error.

Modificate le poche impostazioni importanti. Il resto del file generato può rimanere invariato.

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

Generate il token con openssl rand -hex 32. Il file contiene ora una credenziale, quindi limitatene i permessi prima di avviare il servizio.

sudo chown root:hister /etc/hister/config.yml
sudo chmod 640 /etc/hister/config.yml

Esegui il servizio con systemd

Scrivi /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 è la variabile d'ambiente documentata per il percorso della configurazione. L'unità non dipende quindi dalla directory home dell'account hister. ProtectSystem=strict rende l'intero filesystem di sola lettura per questo servizio. Per questo ReadWritePaths deve indicare la directory dei dati. ProtectHome=yes nasconde /home al servizio. Una directory monitorata all'interno di /home risulterebbe quindi vuota per l'indicizzatore. Rimuovi quella riga se devi indicizzare file in quella directory.

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/

Qualsiasi codice di stato HTTP stampato dall'ultimo comando indica che il processo è in ascolto. curl: (7) Failed to connect indica che non lo è. journalctl -u hister -n 50 --no-pager spiegherà il motivo.

Percorso di installazione 2: Docker Compose

L’immagine è pubblicata nel registro dei container di GitHub, con un tag per ogni 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

Ogni chiave di configurazione dispone di un override tramite variabile d’ambiente nel formato HISTER__<SECTION>__<KEY>, con due caratteri di sottolineatura come separatore. Per questo, un deployment basato su container non richiede il montaggio di un file di configurazione. Mantieni HISTER_ACCESS_TOKEN in un file .env accanto al file Compose. Se preferisci modificare un file, docker run --rm ghcr.io/asciimoo/hister:v0.17.0 create-config > config.yml stampa i valori predefiniti.

Le due righe precedenti sono facili da configurare in modo errato, quindi è utile comprenderle entrambe.

L’indirizzo all’interno del container deve essere 0.0.0.0:4433. Un container ha un proprio network namespace. Di conseguenza, un processo associato a 127.0.0.1 al suo interno è raggiungibile soltanto da quel container e la porta pubblicata non ha alcun servizio a cui inoltrare le connessioni.

La porta pubblicata deve essere scritta come 127.0.0.1:4433:4433, non come 4433:4433. Docker pubblica le porte inserendo proprie regole netfilter. Queste regole vengono valutate prima delle regole di ufw. Di conseguenza, un semplice 4433:4433 resta raggiungibile da Internet anche su un host in cui ufw status indica che la porta è chiusa. Associando il lato host a 127.0.0.1, il reverse proxy rimane l’unico punto di accesso. Lo stesso problema riguarda ogni container presente sul server; Docker Compose su un VPS tratta il resto dell’argomento.

L’immagine predefinita viene eseguita con UID 1000 e GID 1000. Pertanto, ./data deve essere scrivibile da questo account, altrimenti il container si arresta all’avvio con un errore di autorizzazione. sudo chown -R 1000:1000 ./data risolve il problema. Se questi numeri non ti sono familiari, leggi prima quale UID e GID utilizza un container per scrivere i file.

Perché un indice di ricerca personale è la cosa peggiore da esporre

Per impostazione predefinita, Hister è in ascolto su 127.0.0.1:4433, e questa scelta è intenzionale. Considera cosa contiene l'indice dopo un mese di utilizzo: pagine wiki interne, fatture, ticket di supporto aperti durante una sessione autenticata, pagine per la reimpostazione delle password e il testo completo di tutto il resto che hai letto. La documentazione del progetto lo dichiara esplicitamente: "Hister transmits your entire browsing history, with page contents, to and from the server."

Un database di password sottratto deve ancora essere sottoposto a cracking. Un indice personale sottratto è già in formato testo normale e può essere interrogato subito, quindi richiede più attenzione della piccola applicazione self-hosted a cui assomiglia.

Da questi fatti derivano due conseguenze. Hister non richiede autenticazione per impostazione predefinita, quindi un reverse proxy, da solo, pubblica una copia ricercabile di ciò che hai letto a chiunque scopra il nome host. Anche l'endpoint MCP viene pubblicato per impostazione predefinita all'indirizzo /mcp e, senza un token, qualsiasi client che lo raggiunga può eseguire una ricerca nell'indice.

Configura l'autenticazione prima che il servizio venga esposto al di fuori di localhost per la prima volta. Per un singolo utente è sufficiente app.access_token: un unico secret condiviso dall'estensione del browser, dal client terminale e da qualsiasi client MCP. Per più utenti, imposta user_handling: true e crea gli account:

sudo -u hister hister create-user alice --admin --config /etc/hister/config.yml

Il comando richiede una password di almeno 8 caratteri. Ogni account dispone di documenti propri e di un token API personale, che il proprietario può rigenerare dalla pagina del profilo oppure con il flag --regen-token su hister update-user. La generazione di un nuovo token invalida immediatamente quello precedente, quindi in seguito è necessario aggiornare ogni dispositivo utilizzato da quell'account.

Non modificare app.public senza un motivo preciso. La modalità pubblica consente la ricerca senza autenticazione, l'anteprima, la distribuzione dei file e la ricerca MCP, ma continua a bloccare le operazioni di scrittura, l'accesso alla cronologia e le operazioni amministrative.

Reverse proxy, TLS e firewall

Hister non gestisce direttamente HTTPS, quindi termina TLS (transport layer security) davanti al servizio. Caddy è la soluzione più rapida, perché richiede e rinnova autonomamente i certificati tramite ACME (automatic certificate management environment).

hister.example.com {
    reverse_proxy 127.0.0.1:4433
}

Ricarica la configurazione con sudo systemctl reload caddy. Prima di poter emettere un certificato devono essere soddisfatte due condizioni: il record A di hister.example.com deve puntare a questo server e la porta 80 deve essere aperta, perché la challenge HTTP-01 riceve la risposta su quella porta. Se manca una delle due condizioni, il browser mostra un errore TLS invece della pagina e il log di Caddy riporta ripetutamente il fallimento della challenge.

Poi chiudi tutte le altre porte.

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

La porta 4433 è esclusa dall’elenco intenzionalmente. Non è necessario usare un hostname pubblico per accedere al servizio: un servizio onion che punta alla stessa porta di loopback raggiunge il tuo indice dai tuoi dispositivi senza un record DNS e senza dover aprire alcuna porta in ingresso.

server.base_url deve corrispondere all’indirizzo digitato nel browser, incluso lo schema. Se non corrisponde, l’interfaccia viene caricata con testo senza stili e immagini mancanti, perché il server costruisce i collegamenti alle risorse usando base_url e il browser le richiede quindi a un’origine che non risponde. Lo stesso URL deve essere inserito nell’estensione del browser.

Compilazione dell'indice

L'estensione del browser è il principale strumento di raccolta. Installala da Mozilla Add-ons o dal Chrome Web Store, apri la relativa pagina delle opzioni, imposta l'URL del server su https://hister.example.com e incolla il token di accesso. L'estensione acquisisce quindi il titolo, il testo completo, l'HTML e la favicon di ogni pagina visitata e li invia al server. L'estrazione avviene lato client, all'interno del browser. L'estensione non contatta terze parti; l'unica richiesta esterna che effettua riguarda la favicon della pagina.

L'estrazione lato client rende possibile un indice privato. L'estensione vede la pagina esattamente come la vedi tu, dopo l'autenticazione e il rendering. In questo modo una pagina di una wiki interna o un articolo a pagamento viene indicizzato correttamente e il server non deve mai ricevere le credenziali. Questo significa anche che tutto ciò che visualizzi può essere incluso nell'indice. Per questo le regole di esclusione vengono prima delle altre impostazioni dei contenuti.

Le regole di esclusione si trovano in rules.json nelle installazioni per un solo utente, oppure nel database, con impostazioni specifiche per ogni utente. La scheda Rules dell'interfaccia web è il modo più semplice per modificarle. Sono espressioni regolari Go applicate all'URL completo:

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

Un pattern come ^mail.example.com non corrisponde mai, perché la stringa verificata inizia con https://. Anche un $ finale non corrisponde agli URL che contengono una stringa di query, perché i parametri della query vengono mantenuti durante la verifica.

La cronologia esistente viene importata leggendo il database del browser. Il comando deve quindi essere eseguito sul computer che contiene il profilo del browser, cioè il laptop e non il VPS. Installa lo stesso binario su quel computer e indirizzalo al server:

export HISTER_TOKEN='your-access-token'
hister import browser firefox -u https://hister.example.com -t "$HISTER_TOKEN"

L'importazione viene eseguita come job riprendibile denominato browser-import-YYYY-MM-DD. Puoi quindi interromperla e riavviarla in un secondo momento. I servizi di segnalibri vengono importati nello stesso modo, inclusi Linkwarden, Karakeep, Wallabag, Linkding, Readeck e Shaarli. Una nuova importazione recupera solo gli elementi più recenti rispetto all'importazione precedente.

I file presenti sul server vengono indicizzati specificando le directory nella configurazione:

indexer:
  directories:
    - path: '/var/lib/hister/documents'
      label: 'documents'
      filetypes: ['pdf', 'docx', 'md', 'txt']

I file PDF, DOCX, Markdown, Org mode e di testo UTF-8 valido vengono letti come testo completo. Le foto e i video non rientrano in questo elenco. Una libreria di immagini richiede quindi un server che indicizzi volti, luoghi e date invece del testo. PhotoPrism e Immich sono le due soluzioni generalmente confrontate per questo scopo. Una singola pagina viene aggiunta con hister index https://example.com. La trasformazione di interi siti in testo pulito per altri strumenti è un'attività distinta, gestita da crawler self-hosted che convertono le pagine in testo pulito.

La ricerca è basata sui campi. Vale quindi la pena dedicare dieci minuti alla lettura del linguaggio delle query:

"connection reset" domain:github.com added:<30d
title:(wireguard|nftables) -tutorial sort:-visits

Indirizza un agente di coding verso il tuo indice tramite MCP

MCP (model context protocol) è l’interfaccia che un assistente usa per chiamare strumenti su un server. Hister la espone in POST /mcp alla stessa URL di base, tramite il trasporto HTTP streamable, e rende disponibili search, get_preview e get_history. L’autenticazione utilizza lo stesso bearer token del resto dell’API. Se il tool calling è un concetto nuovo, scrivere autonomamente un semplice ciclo dell’agente è il modo più rapido per capire che cosa un endpoint come questo fornisce effettivamente a un assistente.

{
  "mcpServers": {
    "hister": {
      "url": "https://hister.example.com/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_ACCESS_TOKEN"
      }
    }
  }
}

L'header X-Access-Token è un'alternativa a Authorization.

Il vantaggio riguarda ciò che l'agente cerca. La ricerca web aperta restituisce i risultati con il ranking corrente, che per i software soggetti a frequenti aggiornamenti spesso corrispondono alla documentazione di una versione diversa da quella in uso. Il tuo indice restituisce la pagina che hai già letto e scelto di conservare, mentre get_preview fornisce la copia archiviata; la risposta resta quindi disponibile anche se la pagina originale non è più online. Se vuoi includere anche i risultati pubblici, fornisci all'agente entrambe le fonti: una skill di ricerca nel browser basata su SearXNG aggiunge il web aperto come strumento separato. Quando utilizzi più di uno di questi endpoint, vale la pena leggere ospitare server MCP su un VPS, perché tutti condividono questo problema di esposizione.

Disco, backup e manutenzione

La documentazione considera ogni pagina indicizzata di circa 100 KB, inclusa l’anteprima compressa; quindi centomila pagine occupano circa 10 GB. Non esiste un sistema di quote. Due impostazioni vengono spesso confuse: indexer.max_file_size_mb (1 MiB per impostazione predefinita) limita le dimensioni di un singolo file monitorato, mentre server.max_batch_body_size (40 MiB per impostazione predefinita) limita una singola richiesta API.

La directory indicata da app.directory contiene index.db, con i file di indice per ogni lingua, db.sqlite3 per gli account e i job, data/html/ per le anteprime e rules.json. Un backup consiste nell’arrestare il servizio e copiare l’intera directory insieme al file di configurazione. hister export backup.json esporta i documenti in formato JSON per la migrazione, ma non costituisce un backup del server.

Sono utili due comandi di manutenzione. hister reindex ricostruisce gli indici di ricerca ed è necessario dopo aver modificato le impostazioni dell’indicizzatore. Se l’uso della memoria aumenta durante una grande importazione, imposta detect_languages: false nella sezione indexer ed esegui nuovamente l’indicizzazione. hister cleanup rimuove i file di anteprima e favicon orfani lasciati dalle eliminazioni.

L’eliminazione viene eseguita tramite una query, quindi eseguila prima in modalità dry-run:

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

Una pagina eliminata ricompare se un collector continua a inviarla. Aggiungi quindi la regola di esclusione prima di eliminarla.

La licenza AGPLv3 diventa rilevante solo se modifichi il codice. L’esecuzione di una copia non modificata per uso personale non comporta obblighi. Se modifichi Hister e permetti ad altre persone di usare la tua versione tramite una rete, la licenza richiede che tu offra loro il codice sorgente modificato.

Modalità di errore e stringhe visualizzate

Il server non si avvia. La porta 4433 è già in uso oppure il file di configurazione contiene un errore di sintassi YAML. sudo ss -lntp | grep 4433 mostra quale processo occupa la porta e journalctl -u hister -n 50 --no-pager visualizza l’errore di analisi.

L’interfaccia viene caricata, ma appare danneggiata. Testo illeggibile e immagini mancanti indicano che server.base_url non corrisponde all’URL nella barra degli indirizzi. Anche una barra finale è considerata una differenza.

L’estensione non si connette. L’URL del server configurato nell’estensione deve essere uguale a base_url. Il server deve essere in esecuzione e aggiornato. Inoltre, un firewall intermedio può bloccare la connessione senza visualizzare messaggi nella pagina. Firefox mantiene i log delle estensioni separati dalla console normale: apri about:debugging#/runtime/this-firefox e controlla l’estensione Hister.

Il container si arresta all’avvio. Un errore di autorizzazione su ./data indica che la directory appartiene a un UID diverso da 1000, che è l’account interno dell’immagine predefinita.

403 Forbidden da una route amministrativa. POST /api/reindex e POST /api/cleanup sono accessibili soltanto agli amministratori quando la gestione degli utenti è attiva. Per questo motivo un account normale viene rifiutato.

L’uso della memoria aumenta durante un’importazione. La causa abituale è il rilevamento della lingua su una cronologia di grandi dimensioni. Imposta detect_languages: false ed esegui hister reindex al termine.

FAQ

In che modo Hister differisce da SearXNG?

SearXNG è un proxy di metaricerca: inoltra le query ai motori pubblici e restituisce i relativi risultati senza i dati di tracciamento, quindi l'indice appartiene a quei motori. Hister mantiene un proprio indice full-text delle pagine visitate e dei file conservati, quindi risponde alla domanda «dove l'ho letto?», mentre SearXNG risponde a «cosa dice il Web?». Risolvono problemi diversi e molti utenti li eseguono entrambi sullo stesso server.

È sicuro inserire tutta la cronologia di navigazione su un VPS?

Solo dopo aver configurato correttamente l'esposizione. Hister è in ascolto su 127.0.0.1:4433 e, per impostazione predefinita, non richiede autenticazione. Imposta app.access_token o user_handling: true, configura un reverse proxy con TLS davanti al servizio e mantieni chiusa la porta 4433 nel firewall. Un indice full-text delle pagine consultate è testo in chiaro: chiunque raggiunga la porta può leggere tutto senza dover violare alcuna protezione crittografica.

È necessaria l'estensione del browser oppure posso importare semplicemente la cronologia?

L'importazione esegue un backfill una tantum. Legge il database della cronologia del browser, quindi viene eseguita sul computer che contiene il profilo del browser, non sul server. Da quel momento l'estensione mantiene aggiornato l'indice e acquisisce anche le pagine protette da login, perché estrae il contenuto nel browser dopo il rendering della pagina. Una configurazione comune prevede un'importazione iniziale seguita dall'uso dell'estensione.

Un agente di programmazione può cercare nel mio indice Hister?

Sì. Hister è un server MCP (model context protocol) disponibile all'indirizzo POST /mcp sull'URL di base ed espone search, get_preview e get_history. Configura il client in modo che utilizzi https://your-host/mcp con un header Authorization: Bearer contenente il token di accesso. L'agente cerca quindi nella documentazione che hai effettivamente consultato, nella versione che hai letto, invece di utilizzare i risultati attualmente classificati da un motore di ricerca pubblico.