Cómo alojar Hister, tu buscador personal en un VPS
Instala Hister en un VPS para buscar el texto completo de páginas visitadas y archivos propios. Incluye binario, Docker, TLS, inicio de sesión y endpoint MCP.
Qué es Hister y qué no es
Hister es un motor de búsqueda personal que usted aloja por su cuenta. Indexa el texto completo de las páginas que ha visitado y de los archivos que conserva. Después permite buscar en esa colección desde una interfaz web, un cliente de terminal, una API HTTP o un asistente de IA (inteligencia artificial). Hister responde a una pregunta: dónde leí esto.
La mayoría de los lectores conoce esta idea a través de SearXNG, pero no son la misma herramienta. SearXNG es un proxy de metabúsqueda. La consulta se envía a SearXNG, que consulta otros motores en su nombre y devuelve sus resultados sin el seguimiento. El índice pertenece a esos motores. Hister crea su propio índice a partir del contenido que usted le proporciona: páginas capturadas por una extensión del navegador, historial del navegador importado, URL rastreadas y archivos de los directorios que indique. Una instancia de SearXNG alojada por usted le proporciona acceso privado a la web pública. Hister le permite buscar en sus propias lecturas. Las funciones son diferentes, por lo que es normal ejecutar ambos en el mismo servidor.
Hister es software libre bajo la AGPLv3 (Licencia Pública General de GNU Affero, versión 3) o posterior. No incluye telemetría ni necesita un servicio en la nube. Esta guía fija la versión v0.17.0, que era la versión actual el 2026-07-28. Consulte la página de releases para comprobar el tag actual antes de copiar nada y, después, fije el tag que encuentre allí.
Por qué alojar Hister en un VPS
Un índice sólo es útil cuando está completo, y sólo está completo si el servidor estaba en ejecución mientras leías. Un portátil permanece suspendido la mitad del día. Las páginas que abres en el teléfono durante ese tiempo nunca llegan a él, y una importación nocturna nunca se inicia. Un VPS (servidor privado virtual) permanece activo, por lo que todos tus dispositivos envían datos al mismo índice y el crawler sigue funcionando mientras duermes.
La segunda razón es la separación. Configurar user_handling: true en la sección app proporciona a cada cuenta sus propias credenciales y su propia colección de documentos en una sola instancia. Así, un servidor puede alojar a una familia o a un equipo pequeño sin que nadie busque en las lecturas de otra persona.
La tercera razón es la conectividad. El VPS ya tiene un nombre de host público y un certificado, que es lo que la extensión del navegador necesita para conectarse al servidor desde una red que no controlas.
Ruta de instalación uno: el binario de la versión
Hister distribuye un binario por plataforma. Descárguelo junto con el archivo de sumas de comprobación y verifíquelo antes de instalarlo.
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.txtEl resultado correcto es la única línea hister_0.17.0_linux_amd64: OK. Una línea FAILED indica que la descarga está dañada o ha sido alterada. Vuelva a descargarla en lugar de instalarla.
Instale el binario y cree la cuenta de sistema y los directorios que utilizará.
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 escribe un archivo de configuración predeterminado y también confirma que el binario se ejecuta en esta máquina. Una descarga para la arquitectura incorrecta falla en este punto con cannot execute binary file: Exec format error.
Edite los pocos parámetros relevantes. El resto del archivo generado puede mantenerse sin cambios.
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.comGenere el token con openssl rand -hex 32. El archivo contiene ahora una credencial. Restrinja sus permisos antes de iniciar el servicio.
sudo chown root:hister /etc/hister/config.yml
sudo chmod 640 /etc/hister/config.ymlEjecutarlo con systemd
Escriba /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 es la variable de entorno documentada para la ruta de configuración, por lo que la unidad no depende del directorio de inicio de la cuenta hister. ProtectSystem=strict hace que todo el sistema de archivos sea de solo lectura para este servicio. Por eso ReadWritePaths debe indicar el directorio de datos. ProtectHome=yes oculta /home al servicio, por lo que un directorio supervisado dentro de /home aparecería vacío para el indexador. Elimine esa línea si necesita indexar archivos allí.
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/Cualquier código de estado HTTP que muestre el último comando significa que el proceso está escuchando. curl: (7) Failed to connect indica que no lo está y journalctl -u hister -n 50 --no-pager mostrará el motivo.
Ruta de instalación 2: Docker Compose
La imagen se publica en el registro de contenedores de GitHub, con una etiqueta por cada versión.
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:4433Cada clave de configuración tiene una variable de entorno de sustitución con el formato HISTER__<SECTION>__<KEY>, donde se usan dos guiones bajos como separador. Por tanto, una implementación en contenedor no necesita montar ningún archivo de configuración. Mantenga HISTER_ACCESS_TOKEN en un archivo .env junto al archivo de Compose. Si prefiere editar un archivo, docker run --rm ghcr.io/asciimoo/hister:v0.17.0 create-config > config.yml muestra los valores predeterminados.
Es fácil equivocarse en las dos líneas anteriores. Conviene entender ambas.
La dirección dentro del contenedor debe ser 0.0.0.0:4433. Un contenedor tiene su propio espacio de nombres de red. Por tanto, un proceso enlazado a 127.0.0.1 dentro del contenedor sólo es accesible desde ese contenedor. El puerto publicado no tendría ningún servicio al que reenviar las conexiones.
El puerto publicado se escribe 127.0.0.1:4433:4433, no 4433:4433. Docker publica los puertos insertando sus propias reglas de netfilter. Estas reglas se evalúan antes que las reglas de ufw. Por eso, un 4433:4433 simple sigue siendo accesible desde Internet aunque ufw status indique que el puerto está cerrado. Enlazar el lado del host a 127.0.0.1 deja al reverse proxy como única vía de acceso. El mismo problema se aplica a todos los contenedores del servidor. Docker Compose en un VPS explica el resto.
La imagen predeterminada se ejecuta con UID 1000 y GID 1000. Por tanto, ./data debe admitir escritura para esa cuenta. De lo contrario, el contenedor se detiene durante el arranque con un error de permisos. sudo chown -R 1000:1000 ./data lo corrige. Si no conoce esos números, consulte primero con qué UID y GID escribe archivos un contenedor.
Por qué un índice de búsqueda personal es lo peor que se puede exponer
Hister escucha en 127.0.0.1:4433 de forma predeterminada, y este valor predeterminado es intencionado. Piense en todo lo que contiene el índice después de un mes de uso: páginas internas de la wiki, facturas, tickets de soporte que abrió mientras tenía la sesión iniciada, páginas de restablecimiento de contraseñas y el texto completo de todo lo demás que leyó. La documentación del proyecto lo indica directamente: "Hister transmits your entire browsing history, with page contents, to and from the server."
Una base de datos de contraseñas filtrada todavía debe descifrarse. Un índice personal filtrado está en texto sin formato y ya permite realizar búsquedas, por lo que requiere más protección que la pequeña aplicación autoalojada a la que se parece.
De ahí se derivan dos hechos. Hister no requiere autenticación de forma predeterminada, por lo que un reverse proxy, por sí solo, publica una copia consultable de todo lo que lee cualquier persona que conozca el nombre de host. El endpoint MCP también se sirve de forma predeterminada en /mcp y, sin un token, cualquier cliente que pueda acceder a él puede ejecutar una búsqueda en el índice.
Configure la autenticación antes de que el servicio deje localhost por primera vez. Un solo usuario sólo necesita app.access_token: un secreto compartido que envían la extensión del navegador, el cliente de terminal y cualquier cliente MCP. Para varias personas, establezca user_handling: true y cree cuentas:
sudo -u hister hister create-user alice --admin --config /etc/hister/config.ymlEl comando solicita una contraseña de al menos 8 caracteres. Cada cuenta obtiene sus propios documentos y un token de API personal, que el propietario puede regenerar desde la página de perfil o con el flag --regen-token en hister update-user. La generación de un token nuevo invalida inmediatamente el anterior, por lo que después debe actualizarse cada dispositivo que utilice esa cuenta.
No modifique app.public salvo que sea intencionado. El modo público permite realizar búsquedas sin autenticación, consultar vistas previas, servir archivos y ejecutar búsquedas MCP, pero sigue bloqueando las escrituras, el acceso al historial y las operaciones administrativas.
Proxy inverso, TLS y el firewall
Hister no sirve HTTPS directamente, por lo que debe terminar TLS (seguridad de la capa de transporte) delante de él. Caddy es la opción más directa, porque solicita y renueva los certificados por su cuenta mediante ACME (entorno de gestión automática de certificados).
hister.example.com {
reverse_proxy 127.0.0.1:4433
}Vuelva a cargarlo con sudo systemctl reload caddy. Antes de emitir un certificado, deben cumplirse dos condiciones: el registro A de hister.example.com debe apuntar a este servidor y el puerto 80 debe estar abierto, porque allí se responde al desafío HTTP-01. Si falta cualquiera de las dos, el navegador muestra un error de TLS en lugar de la página y el registro de Caddy repite el desafío fallido.
Después, cierre todo lo demás.
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw statusEl puerto 4433 se omite de esa lista intencionadamente.
server.base_url debe coincidir con la dirección que escribe en el navegador, incluido el esquema. Si no coincide, la interfaz se carga con texto sin estilos e imágenes ausentes, porque el servidor genera los enlaces de sus recursos a partir de base_url y el navegador intenta solicitarlos desde un origen que no responde. Esa misma URL se introduce en la extensión del navegador.
Rellenar el índice
La extensión del navegador es el colector principal. Instálela desde Mozilla Add-ons o Chrome Web Store, abra su página de opciones, establezca la URL del servidor en https://hister.example.com y pegue el token de acceso. A continuación, captura el título, el texto completo, el HTML y el favicon de cada página que visita, y los envía al servidor. La extracción se realiza en el cliente, dentro del navegador. La extensión no contacta con terceros; la única solicitud externa que realiza es la del favicon de la página.
La extracción en el cliente permite crear un índice privado. La extensión ve una página exactamente como usted la ve, después del inicio de sesión y del renderizado. Por eso, una página de una wiki interna o un artículo de pago se indexa correctamente y el servidor nunca necesita las credenciales. Esto también significa que todo lo que consulta puede entrar en el índice. Por eso las reglas de exclusión deben configurarse antes de añadir más contenido.
Las reglas de exclusión se almacenan en rules.json en una instalación para un solo usuario, o por usuario en la base de datos. La pestaña Rules de la interfaz web es la forma más sencilla de editarlas. Son expresiones regulares de Go que se comparan con la URL completa:
^https://mail\.example\.com
^https://bank\.example\.com
.*?utm_source=Un patrón como ^mail.example.com nunca coincide, porque la cadena que se prueba empieza por https://. Un $ final también falla en cualquier URL que incluya una cadena de consulta, porque los parámetros de consulta se conservan durante la comparación.
El historial existente se importa leyendo la base de datos del propio navegador. Por tanto, el comando debe ejecutarse en el equipo que contiene el perfil del navegador: su portátil, no el VPS. Instale allí el mismo binario y apúntelo al servidor:
export HISTER_TOKEN='your-access-token'
hister import browser firefox -u https://hister.example.com -t "$HISTER_TOKEN"La importación se ejecuta como un trabajo reanudable llamado browser-import-YYYY-MM-DD. Puede interrumpirlo y volver a iniciarlo más adelante. Los servicios de marcadores se importan de la misma forma, incluidos Linkwarden, Karakeep, Wallabag, Linkding, Readeck y Shaarli. Una importación repetida sólo obtiene lo que sea más reciente que la anterior.
Los archivos del servidor se indexan indicando los directorios en la configuración:
indexer:
directories:
- path: '/var/lib/hister/documents'
label: 'documents'
filetypes: ['pdf', 'docx', 'md', 'txt']Los archivos PDF, DOCX, Markdown, Org mode y de texto UTF-8 válido se leen como texto completo. Las fotos y los vídeos no están incluidos en esa lista. Por tanto, una biblioteca de imágenes necesita un servidor que indexe rostros, lugares y fechas en lugar de texto. PhotoPrism e Immich son las dos opciones que suelen compararse para esa función. Una página individual se añade con hister index https://example.com. Convertir sitios completos en texto limpio para otras herramientas es una tarea distinta, gestionada por rastreadores autoalojados que convierten las páginas en texto limpio.
La búsqueda se basa en campos. Por eso, conviene dedicar diez minutos a leer el lenguaje de consulta:
"connection reset" domain:github.com added:<30d
title:(wireguard|nftables) -tutorial sort:-visitsSeñale su propio índice a un agente de programación mediante MCP
MCP (model context protocol) es la interfaz que usa un asistente para llamar a herramientas en un servidor. Hister lo ofrece en POST /mcp bajo la misma URL base, mediante el transporte HTTP con streaming, y expone search, get_preview y get_history. La autenticación usa el mismo token bearer que el resto de la API.
{
"mcpServers": {
"hister": {
"url": "https://hister.example.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
}
}
}Un encabezado X-Access-Token funciona como alternativa a Authorization.
El valor está en lo que busca el agente. Una búsqueda web abierta devuelve lo que tiene mejor posición en ese momento. En el caso del software que cambia con rapidez, a menudo devuelve documentación de una versión que no está ejecutando. Su propio índice devuelve la página que ya leyó y decidió conservar, y get_preview sirve la copia almacenada. Así, la respuesta sigue disponible aunque la página original deje de estarlo. Proporcione ambas fuentes al agente si también quiere resultados públicos: una skill de búsqueda en el navegador respaldada por SearXNG añade la web abierta como una herramienta independiente. Cuando ejecute más de uno de estos endpoints, conviene leer alojar servidores MCP en un VPS, porque todos comparten este problema de exposición.
Disco, copias de seguridad y mantenimiento
La documentación calcula unas 100 KB por página indexada, incluida la vista previa comprimida, por lo que cien mil páginas ocupan aproximadamente 10 GB. No existe un sistema de cuotas. Hay dos ajustes que suelen confundirse: indexer.max_file_size_mb (1 MiB de forma predeterminada) limita el tamaño de un solo archivo supervisado, y server.max_batch_body_size (40 MiB de forma predeterminada) limita una única solicitud de API.
El directorio indicado por app.directory contiene index.db, con los archivos de índice de cada idioma; db.sqlite3, para las cuentas y los trabajos; data/html/, para las vistas previas; y rules.json. Una copia de seguridad consiste en detener el servicio y copiar ese directorio completo junto con el archivo de configuración. hister export backup.json escribe los documentos en formato JSON para la migración, pero no es una copia de seguridad del servidor.
Conviene conocer dos comandos de mantenimiento. hister reindex reconstruye los índices de búsqueda. Es necesario ejecutarlo después de cambiar los ajustes del indexador. Si el uso de memoria aumenta durante una importación grande, establezca detect_languages: false en la sección indexer y vuelva a indexar. hister cleanup elimina los archivos huérfanos de vistas previas y favicons que quedan después de borrar elementos.
El borrado se realiza mediante una consulta, por lo que debe ejecutarla primero en modo de prueba:
hister delete 'domain:example.com' --dry --verboseUna página borrada vuelve a aparecer si un recopilador todavía la envía. Añada la regla de exclusión antes de borrarla.
La AGPLv3 sólo es relevante si modifica el código. Ejecutar una copia sin modificar para uso propio no implica ninguna obligación. Si modifica Hister y permite que otras personas utilicen su versión a través de una red, la licencia le exige ofrecerles el código fuente modificado.
Modos de fallo y mensajes que aparecerán
El servidor no se inicia. El puerto 4433 ya está ocupado o el archivo de configuración contiene un error de sintaxis YAML. sudo ss -lntp | grep 4433 muestra qué proceso ocupa el puerto y journalctl -u hister -n 50 --no-pager imprime el error de análisis.
La interfaz se carga, pero aparece dañada. El texto desordenado y las imágenes que faltan indican que server.base_url no coincide con la URL de la barra de direcciones. Una barra final también se considera una discrepancia.
La extensión no se conecta. La URL del servidor configurada en la extensión debe ser igual a base_url. El servidor debe estar en ejecución y actualizado. Además, un firewall intermedio puede bloquear la conexión sin mostrar ningún mensaje en la página. Firefox mantiene los registros de las extensiones fuera de la consola normal: abra about:debugging#/runtime/this-firefox y revise la extensión Hister.
El contenedor se cierra al iniciar. Un error de permisos en ./data indica que el directorio pertenece a un UID distinto de 1000, que es la cuenta dentro de la imagen predeterminada.
403 Forbidden en una ruta administrativa. POST /api/reindex y POST /api/cleanup sólo están disponibles para administradores cuando la gestión de usuarios está activada. Por eso se rechaza el acceso de una cuenta normal.
El uso de memoria aumenta durante una importación. La causa habitual es la detección de idioma en un historial grande. Configure detect_languages: false y ejecute hister reindex después.
FAQ
¿En qué se diferencia Hister de SearXNG?
SearXNG es un proxy de metabúsqueda: reenvía la consulta a motores públicos y devuelve sus resultados sin los elementos de seguimiento, por lo que el índice pertenece a esos motores. Hister mantiene su propio índice de texto completo de las páginas que ha visitado y de los archivos que conserva. Por eso responde a «¿dónde leí eso?», mientras que SearXNG responde a «¿qué dice la web?». Resuelven problemas distintos y muchas personas ejecutan ambos en un mismo servidor.
¿Es seguro poner todo mi historial de navegación en un VPS?
Sólo después de configurar correctamente la exposición. Hister se enlaza a 127.0.0.1:4433 y no requiere autenticación de forma predeterminada. Configure app.access_token o user_handling: true, coloque un reverse proxy con TLS delante y mantenga el puerto 4433 cerrado en el firewall. Un índice de texto completo de su actividad de lectura está en texto plano. Cualquiera que acceda al puerto puede leerlo todo sin descifrar nada.
¿Necesito la extensión del navegador o puedo importar directamente mi historial?
La importación es una carga inicial que se realiza una sola vez. Lee la base de datos del historial del propio navegador, por lo que se ejecuta en el equipo que contiene el perfil del navegador, no en el servidor. A partir de entonces, la extensión mantiene actualizado el índice y captura páginas protegidas por un inicio de sesión porque extrae el contenido en el navegador después de que se renderiza la página. Una configuración habitual consiste en realizar una importación y después instalar la extensión.
¿Puede un agente de programación buscar en mi índice de Hister?
Sí. Hister es un servidor MCP (model context protocol) disponible en POST /mcp de la URL base y expone search, get_preview y get_history. Configure el cliente para usar https://your-host/mcp con una cabecera Authorization: Bearer que contenga su token de acceso. Así, el agente busca en la documentación que realmente ha leído y en la versión que leyó, en lugar de consultar lo que un motor de búsqueda público coloque hoy en las primeras posiciones.