SSD Nodes Learn 🎉 VPS da $5.50/mese
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-21

Hister self-hosted: il tuo motore di ricerca personale

Installa Hister su un VPS per cercare pagine e file indicizzati: guida a binario e Docker, TLS, login e endpoint MCP per l’accesso via AI.

Cos’è Hister e cosa non è

Hister è un motore di ricerca personale che ospiti autonomamente. Indicizza il testo completo delle pagine visitate e dei file archiviati, quindi consente di cercare 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 domanda: dove ho letto questa informazione.

Molti lettori conoscono questo concetto tramite SearXNG, ma i due strumenti non sono equivalenti. SearXNG è un proxy di metaricerca. La query viene inviata a SearXNG, che interroga altri motori per conto dell’utente e restituisce i relativi risultati senza gli elementi di tracciamento. L’indice appartiene a quei motori. Hister crea un proprio indice a partire dai contenuti forniti dall’utente: pagine acquisite da un’estensione del browser, cronologia del browser importata, URL sottoposti a crawling e file presenti nelle directory indicate. Un’istanza SearXNG self-hosted consente di accedere in modo privato al web pubblico. Hister consente di cercare i contenuti letti dall’utente. Le funzioni sono diverse, quindi è normale eseguire entrambi sullo stesso server.

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 elemento, controlla la pagina delle release per verificare il tag corrente, quindi fissa il tag trovato.

Perché eseguire Hister in hosting autonomo 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 che aprite sul telefono durante quel periodo non lo raggiungono e un'importazione notturna non viene avviata. Un VPS (server privato virtuale) rimane attivo, quindi ogni dispositivo che possedete invia i dati allo stesso indice e il crawler continua a funzionare mentre dormite.

Il secondo motivo è la separazione. L'impostazione di user_handling: true nella sezione app assegna a ogni account credenziali e raccolta di documenti proprie, all'interno di una singola istanza. Un unico server può quindi gestire una famiglia o un piccolo team senza che nessuno possa cercare nei contenuti letti dagli altri.

Il terzo motivo riguarda la connettività. Il VPS dispone già di un hostname pubblico e di un certificato, elementi necessari all'estensione del browser per raggiungere il server da una rete che non controllate.

Percorso di installazione 1: il binario della release

Hister distribuisce un binario per ogni piattaforma. Scaricalo insieme al file dei checksum e verifica 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 è la singola riga hister_0.17.0_linux_amd64: OK. Una riga FAILED indica che il download è danneggiato o alterato. Scaricalo nuovamente invece di installarlo.

Installa il binario, quindi crea 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 in questo punto con cannot execute binary file: Exec format error.

Modifica le poche impostazioni rilevanti. 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

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

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

Esegui il servizio tramite 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, quindi l'unità non dipende dalla directory home dell'account hister. ProtectSystem=strict rende di sola lettura l'intero filesystem per questo servizio; per questo ReadWritePaths deve indicare la directory dei dati. ProtectHome=yes nasconde /home al servizio, quindi una directory monitorata sotto /home risulterebbe vuota per l'indicizzatore. Rimuovi quella riga se devi indicizzare i file presenti in tale 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 significa che non lo è, mentre journalctl -u hister -n 50 --no-pager indica il motivo.

Percorso di installazione due: Docker Compose

L’immagine è pubblicata nel registro dei container 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, il deployment del 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 e vale la pena comprenderle entrambe.

L’indirizzo all’interno del container deve essere 0.0.0.0:4433. Un container dispone di un proprio namespace di rete. 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 va scritta come 127.0.0.1:4433:4433, non come 4433:4433. Docker pubblica le porte inserendo regole netfilter proprie. Queste regole vengono valutate prima delle regole ufw. Di conseguenza, un semplice 4433:4433 resta raggiungibile da Internet anche su un host in cui ufw status mostra la porta come chiusa. Associando il lato host a 127.0.0.1, il reverse proxy resta l’unico punto di accesso. Lo stesso problema riguarda ogni container del server; Docker Compose su un VPS tratta il resto dell’argomento.

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

Perché esporre un indice di ricerca personale è la scelta peggiore

Per impostazione predefinita, Hister è in ascolto su 127.0.0.1:4433. Questa impostazione predefinita è intenzionale. Dopo un mese di utilizzo, l'indice può contenere pagine wiki interne, fatture, ticket di supporto aperti durante una sessione autenticata, pagine per la reimpostazione delle password e il testo completo di tutto ciò che hai letto. La documentazione del progetto lo afferma chiaramente: "Hister trasmette tutta la cronologia di navigazione, incluso il contenuto delle pagine, da e verso il server."

Un database di password divulgato deve ancora essere sottoposto a cracking. Un indice personale divulgato è già in chiaro e consente subito le ricerche, quindi richiede più attenzione rispetto alla piccola applicazione self-hosted a cui assomiglia.

Da questi fatti derivano due conseguenze. Hister non richiede autenticazione per impostazione predefinita. Pertanto, un reverse proxy da solo pubblica una copia ricercabile dei contenuti letti a chiunque scopra il nome host. Anche l'endpoint MCP viene pubblicato per impostazione predefinita su /mcp. Senza un token, qualsiasi client che lo raggiunga può eseguire ricerche 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, inviato dall'estensione del browser, dal client da terminale e da qualsiasi client MCP. Per più persone, 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 dei propri documenti e di un token API personale, che il proprietario può rigenerare dalla pagina del profilo o con il flag --regen-token su hister update-user. La generazione di un nuovo token invalida immediatamente quello precedente. Di conseguenza, è necessario aggiornare ogni dispositivo utilizzato da quell'account.

Non modificare app.public se non è una scelta intenzionale. La modalità pubblica consente ricerche non autenticate, anteprime, distribuzione di file e ricerche MCP, ma continua a bloccare le operazioni di scrittura, l'accesso alla cronologia e le operazioni amministrative.

Proxy inverso, TLS e firewall

Hister non gestisce direttamente HTTPS, quindi termina TLS (Transport Layer Security) davanti al servizio. Caddy è la soluzione più semplice, 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 ripete il fallimento della challenge.

Quindi 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 è omessa intenzionalmente da quell’elenco.

server.base_url deve corrispondere all’indirizzo che inserisci nel browser, incluso lo schema. Se non corrisponde, l’interfaccia viene caricata con testo privo di stile e immagini mancanti, perché il server costruisce i link alle risorse a partire da base_url e il browser le richiede quindi a un’origine che non risponde. Inserisci lo stesso URL nell’estensione del browser.

Compilazione dell'indice

L'estensione del browser è il componente principale per la raccolta. Installala da Mozilla Add-ons o dal Chrome Web Store, apri la 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 visualizzi, dopo l'accesso 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ò diventare candidato all'indicizzazione. Per questo le regole di esclusione devono essere definite prima di aggiungere altro contenuto.

Le regole di esclusione si trovano in rules.json nelle installazioni per un singolo utente, oppure nel database, con configurazione distinta 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 modello come ^mail.example.com non corrisponde mai, perché la stringa verificata inizia con https://. Anche un $ finale non funziona sugli 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 sul laptop 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 gestione dei segnalibri vengono importati nello stesso modo, inclusi Linkwarden, Karakeep, Wallabag, Linkding, Readeck e Shaarli. Una nuova importazione recupera soltanto gli elementi più recenti rispetto all'ultima esecuzione.

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 sono inclusi nell'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 normalmente confrontate per questo scopo. Puoi aggiungere una singola pagina con hister index https://example.com. La conversione 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. Per questo vale la pena dedicare dieci minuti alla lettura del linguaggio di interrogazione:

"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 gli strumenti disponibili su un server. Hister lo espone all’indirizzo POST /mcp usando lo stesso URL di base e il trasporto HTTP streamable; rende disponibili search, get_preview e get_history. L’autenticazione usa lo stesso bearer token del resto dell’API.

{
  "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 dipende dai contenuti cercati dall’agente. Una ricerca sul web restituisce i risultati che oggi hanno il ranking più alto; per il software in rapida evoluzione, spesso si tratta della documentazione relativa a una versione diversa da quella in esecuzione. Il tuo indice restituisce invece la pagina che hai già letto e scelto di conservare, mentre get_preview fornisce la copia archiviata. In questo modo la risposta resta 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 esegui più di uno di questi endpoint, conviene leggere ospitare server MCP su un VPS, perché tutti condividono questo problema di esposizione.

Disco, backup e manutenzione

La documentazione considera ogni pagina indicizzata pari a 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 le dimensioni di una singola richiesta API.

La directory indicata da app.directory contiene index.db, con i file di indice per 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 scrive i documenti in formato JSON per la migrazione, ma non costituisce un backup del server.

È utile conoscere due comandi di manutenzione. hister reindex ricostruisce gli indici di ricerca; questa operazione è necessaria dopo aver modificato le impostazioni dell'indicizzatore. Se l'utilizzo della memoria aumenta durante una grande importazione, imposta detect_languages: false nella sezione indexer ed esegui nuovamente l'indicizzazione. hister cleanup rimuove i file orfani delle anteprime e delle favicon 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.

AGPLv3 diventa rilevante solo se modifichi il codice. L'utilizzo personale di una copia non modificata non comporta alcun obbligo. Se modifichi Hister e consenti ad altre persone di utilizzare 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 ha un aspetto errato. Testo confuso e immagini mancanti indicano che server.base_url non corrisponde all'URL nella barra degli indirizzi. Anche una slash finale diversa è considerata una mancata corrispondenza.

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 e un firewall intermedio potrebbe bloccare la connessione senza visualizzare messaggi nella pagina. Firefox registra i log delle estensioni separatamente dalla console standard: apri about:debugging#/runtime/this-firefox e controlla l'estensione Hister.

Il container termina all'avvio. Un errore di permessi su ./data indica che la directory appartiene a un UID diverso da 1000, che è l'account usato all'interno dell'immagine predefinita.

403 Forbidden da una route amministrativa. POST /api/reindex e POST /api/cleanup sono disponibili solo agli amministratori quando la gestione degli utenti è attiva, quindi l'accesso viene negato agli account ordinari.

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 in seguito.

FAQ

In che modo Hister è diverso da SearXNG?

SearXNG è un proxy di metaricerca: inoltra la query ai motori pubblici e restituisce i relativi risultati rimuovendo il 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 molte persone li eseguono entrambi sullo stesso server.

È sicuro mettere 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 a Hister e mantieni chiusa la porta 4433 sul firewall. Un indice full-text delle pagine consultate è testo in chiaro: chiunque raggiunga la porta può leggere tutto senza dover violare alcuna cifratura.

È necessaria l’estensione del browser o 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 autenticazione, perché ne 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 coding 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 per usare https://your-host/mcp con un’intestazione Authorization: Bearer contenente il token di accesso. L’agente cerca quindi nella documentazione che hai effettivamente consultato, nella versione che hai letto, invece di usare i risultati attualmente classificati da un motore di ricerca pubblico.