Cómo alojar Hister, tu buscador personal
Aloja Hister en un VPS para buscar páginas y archivos por texto completo. Incluye instalación binaria y Docker, TLS, autenticación y el 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í eso.
La mayoría de los lectores conocen esta idea a través de SearXNG, pero no son la misma herramienta. Si el nombre que conoce es el antiguo Searx, ese proyecto no ha recibido ningún commit de código desde 2023 y SearXNG lo mantiene, por lo que una instancia nueva que configure hoy será SearXNG en cualquier caso. 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 le proporciona: páginas capturadas por una extensión del navegador, historial del navegador importado, URL rastreadas y archivos de los directorios que le indique. Una instancia de SearXNG autoalojada le proporciona acceso privado a la web pública. Hister permite buscar en sus propias lecturas. Son funciones distintas, por lo que es normal ejecutar ambos en el mismo servidor. Si lo hace, conviene saber hasta qué punto SearXNG oculta realmente sus búsquedas, porque sustituye su IP por la del servidor ante los motores, pero no oculta las consultas en sí.
Hister es software libre bajo la AGPLv3 (GNU Affero General Public License, versión 3) o posterior. No incluye telemetría y no necesita ningún 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 funcionando mientras leías. Un portátil entra en suspensión durante buena parte 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 contenido 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 infraestructura de red. El VPS ya tiene un nombre de host público y un certificado, que son lo que la extensión del navegador necesita para conectarse al servidor desde una red que no controla. El mismo par también se utiliza en otra parte del servidor, porque openGym registra su primera passkey contra el nombre de host activo en ese momento, lo que significa que el nombre y el certificado deben estar definidos antes de crear la primera cuenta.
Ruta de instalación uno: el binario de la versión
Hister publica 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 se ha modificado. Vuelva a descargarla en lugar de instalarla.
Instale el binario. Después, cree una 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 demuestra 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 sólo los parámetros necesarios. 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 ahora contiene una credencial, así que restrinja sus permisos antes de iniciar el servicio.
sudo chown root:hister /etc/hister/config.yml
sudo chmod 640 /etc/hister/config.ymlEjecútelo 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 personal 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 situado bajo /home aparecería vacío para el indexador. Quite 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 indica que el proceso está escuchando. curl: (7) Failed to connect indica que no lo está, y journalctl -u hister -n 50 --no-pager mostrará la causa.
Ruta de instalación dos: 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 sobreescritura de entorno con el formato HISTER__<SECTION>__<KEY>, donde se usan dos guiones bajos como separador. Por tanto, una implementación en contenedor no necesita montar un 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.
Las dos líneas anteriores pueden configurarse fácilmente de forma incorrecta. 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 allí sólo es accesible desde dentro de ese contenedor, y no hay ningún destino al que el puerto publicado pueda 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. Esas reglas se evalúan antes que las reglas de ufw. Por tanto, un 4433:4433 simple sigue siendo accesible desde Internet incluso en un equipo donde ufw status muestra el puerto como cerrado. Enlazar el lado del host a 127.0.0.1 deja al proxy inverso 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 ser escribible por 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 esos números no le resultan familiares, lea primero con qué UID y GID escribe archivos un contenedor.
Por qué exponer un índice de búsqueda personal es una mala idea
Hister escucha en 127.0.0.1:4433 de forma predeterminada, y este valor predeterminado es deliberado. Piense en lo que contiene el índice después de un mes de uso: páginas de la wiki interna, 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 plano y ya permite búsquedas, por lo que requiere más cuidado que la pequeña aplicación autoalojada a la que se parece.
De esto se desprenden dos hechos. Hister no requiere autenticación de forma predeterminada, por lo que un reverse proxy por sí solo publica una copia consultable de lo que lee ante cualquiera 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 búsquedas 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 las 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 indicador --regen-token en hister update-user. Al generar un token nuevo, el anterior se invalida de inmediato, por lo que después debe actualizar todos los dispositivos que use esa cuenta.
No modifique app.public salvo que sea intencionado. El modo público permite las búsquedas sin autenticación, las vistas previas, la publicación de archivos y las 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 por sí mismo, así que debe terminar TLS (seguridad de la capa de transporte) delante de él. Caddy es la opción más sencilla, porque solicita y renueva los certificados automáticamente 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. Deben cumplirse dos condiciones antes de emitir un certificado: 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 falta de esa lista intencionadamente. Un nombre de host público no es la única vía de acceso, y un servicio onion que apunte al mismo puerto de loopback permite acceder a su índice desde sus propios dispositivos sin un registro DNS ni ningún puerto entrante abierto.
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 construye los enlaces a sus recursos a partir de base_url y el navegador los solicita después 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 recopilador 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. Después captura el título, el texto completo, el HTML y el favicon de cada página que visite, 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 petición 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 puede indexar correctamente una página de una wiki interna o un artículo de pago, sin que el servidor necesite las credenciales. Esto también significa que todo lo que consulte puede incluirse 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 analizada 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 se ejecuta en la máquina que contiene el perfil del navegador, es decir, en su portátil y no en 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 tarde. Los servicios de marcadores se importan de la misma forma, incluidos Linkwarden, Karakeep, Wallabag, Linkding, Readeck y Shaarli. Una importación repetida sólo recupera los elementos más recientes que la última importación.
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 ese trabajo. Una sola página se añade con hister index https://example.com. Convertir sitios completos en texto limpio para otras herramientas es un trabajo independiente, gestionado 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 consultas:
"connection reset" domain:github.com added:<30d
title:(wireguard|nftables) -tutorial sort:-visitsDirija un agente de programación a su propio índice mediante MCP
MCP (model context protocol) es la interfaz que usa un asistente para llamar a herramientas en un servidor. Hister la 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. Si la invocación de herramientas es un tema nuevo para usted, escribir su propio bucle de agente pequeño es la forma más rápida de ver qué recibe realmente un asistente de un endpoint como este.
{
"mcpServers": {
"hister": {
"url": "https://hister.example.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
}
}
}El encabezado X-Access-Token funciona como alternativa a Authorization.
El valor está en lo que busca el agente. La búsqueda web abierta devuelve lo que ocupa los primeros puestos en ese momento y, en el caso del software que cambia rápidamente, a menudo muestra la 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, por lo que 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 un único archivo supervisado, mientras que server.max_batch_body_size (40 MiB de forma predeterminada) limita una 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 todo ese directorio 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, algo necesario 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 tras las eliminaciones.
La eliminación se ejecuta mediante una consulta, por lo que debe probarla primero en modo de simulación:
hister delete 'domain:example.com' --dry --verboseUna página eliminada vuelve a aparecer si algún recolector todavía la envía. Añada la regla de exclusión antes de eliminarla.
La AGPLv3 sólo se aplica 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 verá
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 usa 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 ausentes indican que server.base_url no coincide con la URL de la barra de direcciones. Una barra final también cuenta como una discrepancia.
La extensión no se conecta. La URL del servidor configurada en la extensión debe coincidir con 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 durante el arranque. Un error de permisos en ./data indica que el directorio pertenece a un UID distinto de 1000, que es la cuenta utilizada dentro de la imagen predeterminada.
403 Forbidden desde una ruta de administración. POST /api/reindex y POST /api/cleanup sólo están disponibles para administradores cuando está habilitada la gestión de usuarios. Por eso se rechaza una cuenta normal.
El consumo de memoria aumenta durante una importación. La causa habitual es la detección de idioma en un historial grande. Establezca 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 el 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 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 guardar 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 cerrado el puerto 4433 en el firewall. Un índice de texto completo de sus lecturas 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 y no en el servidor. A partir de ese momento, la extensión mantiene actualizado el índice. También captura las 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 en su 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. De este modo, el agente busca en la documentación que realmente ha leído, en la versión que leyó, en lugar de consultar lo que actualmente aparece mejor posicionado en un motor de búsqueda público.