Как установить Hister: персональный поиск для своих данных
Руководство по развертыванию Hister на VPS для индексации локальных файлов и истории браузера. Инструкция охватывает установку Docker, настройку TLS и подключение MCP.
Что такое Hister и чем он не является
Hister — это персональная поисковая система для самостоятельного хостинга. Она индексирует полный текст посещенных вами страниц и хранимых файлов, позволяя выполнять поиск по этой коллекции через веб-интерфейс, терминальный клиент, HTTP API или помощника на базе ИИ (искусственного интеллекта). Hister отвечает на один вопрос: где я это читал.
Большинство читателей знакомятся с этой идеей через SearXNG, но это не один и тот же инструмент. Если вы знаете более старый Searx, в этом проекте с 2023 года не было коммитов, а SearXNG продолжает его развитие, поэтому новый экземпляр, который вы разворачиваете сегодня, в любом случае будет SearXNG. SearXNG — это прокси метапоиска. Вы отправляете ему запрос, он от вашего имени обращается к другим поисковым системам и возвращает их результаты без отслеживания. Индекс принадлежит этим поисковым системам. Hister создаёт собственный индекс из переданных ему данных: страниц, сохранённых расширением браузера, импортированной истории браузера, просканированных URL и файлов в указанных вами каталогах. Самостоятельно размещённый экземпляр SearXNG предоставляет закрытый доступ к общедоступной сети. Hister позволяет искать по вашим собственным материалам. Эти задачи различаются, поэтому запускать оба сервиса на одном сервере нормально. Если вы так делаете, полезно знать какую часть ваших поисковых запросов SearXNG действительно скрывает, поскольку в поисковых системах он подменяет ваш IP-адрес адресом сервера, а не скрывает сами запросы.
Hister — это свободное программное обеспечение, распространяемое по лицензии AGPLv3 (GNU Affero General Public License, версия 3) или более поздней. В нем нет телеметрии, и оно не требует облачных сервисов. В данном руководстве зафиксирована версия v0.17.0, которая была актуальным релизом на 2026-07-28. Перед копированием команд проверьте страницу релизов, чтобы узнать текущий тег, и используйте его.
Почему стоит разместить Hister на собственном VPS
Индекс полезен только тогда, когда он полон, а полным он будет лишь в том случае, если сервер работал во время вашего чтения. Ноутбук находится в спящем режиме половину дня. Страницы, которые вы открываете на телефоне в это время, не попадают в индекс, а ночной импорт не запускается. VPS (виртуальный выделенный сервер) работает постоянно, поэтому каждое ваше устройство отправляет данные в один и тот же индекс, а индексатор продолжает работу, пока вы спите.
Вторая причина — изоляция. Настройка user_handling: true в секции app предоставляет каждой учетной записи собственные учетные данные и собственную коллекцию документов на одном экземпляре. Таким образом, один сервер может обслуживать семью или небольшую команду, при этом никто не сможет искать в истории чтения других пользователей.
Третья причина — инфраструктура. На VPS уже есть публичное имя хоста и сертификат. Они нужны расширению браузера, чтобы подключаться к серверу из сети, которой вы не управляете. На этом сервере та же пара выполняет и другую задачу: openGym регистрирует первый passkey для имени хоста, доступного в этот момент. Поэтому имя хоста и сертификат нужно настроить до создания первой учётной записи.
Установка первым способом: релизный бинарный файл
Hister поставляет один бинарный файл для каждой платформы. Загрузите его вместе с файлом контрольных сумм и выполните проверку перед установкой.
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Успешным результатом является одна строка hister_0.17.0_linux_amd64: OK. Строка FAILED означает, что файл поврежден или изменен, поэтому загрузите его заново, прежде чем приступать к установке.
Установите бинарный файл, затем создайте системную учетную запись и каталоги, которые он будет использовать.
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 создает файл конфигурации по умолчанию и подтверждает, что бинарный файл запускается на данной машине. Если загружена версия для неверной архитектуры, выполнение завершится ошибкой cannot execute binary file: Exec format error.
Отредактируйте несколько важных параметров. Остальную часть созданного файла можно оставить без изменений.
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Сгенерируйте токен с помощью openssl rand -hex 32. Теперь файл содержит учетные данные, поэтому ограничьте права доступа к нему до запуска службы.
sudo chown root:hister /etc/hister/config.yml
sudo chmod 640 /etc/hister/config.ymlЗапуск под управлением systemd
Создайте файл /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 — это документированная переменная окружения для пути к конфигурации, поэтому юнит не зависит от домашнего каталога учетной записи hister. Параметр ProtectSystem=strict делает всю файловую систему доступной только для чтения для этого сервиса, поэтому в ReadWritePaths необходимо указать каталог с данными. Параметр ProtectHome=yes скрывает /home от сервиса, поэтому отслеживаемый каталог внутри /home будет выглядеть пустым для индексатора. Удалите эту строку, если вам нужно индексировать файлы в этом месте.
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/Любой HTTP-код ответа, выведенный последней командой, означает, что процесс прослушивает порт. curl: (7) Failed to connect означает, что это не так, а journalctl -u hister -n 50 --no-pager укажет причину.
Вариант установки два: Docker Compose
Образ опубликован в GitHub container registry, по одному тегу на каждый релиз.
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Каждый параметр конфигурации можно переопределить через переменную окружения в формате HISTER__<SECTION>__<KEY>, где два символа подчеркивания выступают в роли разделителя. Таким образом, для развертывания контейнера не требуется монтировать файл конфигурации. Храните HISTER_ACCESS_TOKEN в файле .env рядом с файлом compose. Если вы предпочитаете редактировать файл, команда docker run --rm ghcr.io/asciimoo/hister:v0.17.0 create-config > config.yml выведет значения по умолчанию.
Две строки выше легко настроить неверно, поэтому важно понимать их назначение.
Адрес внутри контейнера должен быть 0.0.0.0:4433. Контейнер обладает собственным сетевым пространством имен, поэтому процесс, привязанный к 127.0.0.1 внутри него, доступен только изнутри этого контейнера, и опубликованному порту нечего будет перенаправлять.
Опубликованный порт записывается как 127.0.0.1:4433:4433, а не 4433:4433. Docker публикует порты, добавляя собственные правила netfilter. Эти правила обрабатываются раньше правил ufw, поэтому обычный 4433:4433 остается доступным из интернета даже на сервере, где ufw status показывает, что порт закрыт. Привязка хостовой стороны к 127.0.0.1 оставляет reverse proxy единственным способом доступа. Эта же ловушка актуальна для любого контейнера на сервере, а в Docker Compose на VPS разобраны остальные аспекты этой темы.
Образ по умолчанию запускается от имени пользователя с UID 1000 и GID 1000, поэтому директория ./data должна быть доступна для записи этой учетной записи, иначе контейнер остановится при запуске с ошибкой доступа. Команда sudo chown -R 1000:1000 ./data исправляет это. Если эти числа вам незнакомы, сначала прочитайте от имени какого UID и GID контейнер записывает файлы.
Почему личный поисковый индекс — это худшее, что можно выставить наружу
Hister по умолчанию слушает 127.0.0.1:4433, и этот выбор сделан намеренно. Подумайте, что содержит индекс после месяца использования: страницы внутренней вики, счета, тикеты в техподдержку, открытые в авторизованном состоянии, страницы сброса паролей и полный текст всего остального, что вы читали. Документация проекта прямо указывает: "Hister передает всю вашу историю браузера, включая содержимое страниц, на сервер и обратно".
Утекшую базу паролей еще нужно взломать. Утекший личный индекс — это открытый текст, который уже готов к поиску, поэтому он требует больше защиты, чем небольшое self-hosted приложение, на которое он похож.
Из этого следуют два факта. Hister «из коробки» не требует аутентификации, поэтому reverse proxy сам по себе делает доступной копию вашей истории чтения для любого, кто узнает имя хоста. MCP endpoint также по умолчанию обслуживается по адресу /mcp, и без токена любой клиент, имеющий доступ к нему, может выполнить поиск по индексу.
Настройте аутентификацию до того, как сервис впервые выйдет за пределы localhost. Одному пользователю достаточно app.access_token — общего секретного ключа, который передается расширением браузера, терминальным клиентом и любым MCP-клиентом. Для нескольких пользователей настройте user_handling: true и создайте учетные записи:
sudo -u hister hister create-user alice --admin --config /etc/hister/config.ymlКоманда запросит пароль длиной не менее 8 символов. Каждая учетная запись получает свои документы и личный API-токен, который владелец может пересоздать на странице профиля или с помощью флага --regen-token в hister update-user. Генерация нового токена немедленно аннулирует предыдущий, поэтому после этого потребуется обновить все устройства, использующие эту учетную запись.
Не меняйте app.public, если не уверены в необходимости этого действия. Публичный режим разрешает неаутентифицированный поиск, просмотр превью, раздачу файлов и поиск через MCP, при этом блокируя запись, доступ к истории и административные операции.
Reverse proxy, TLS и межсетевой экран
Hister не поддерживает HTTPS самостоятельно, поэтому завершайте TLS (transport layer security) перед ним. Caddy — самый простой путь, так как он запрашивает и обновляет сертификаты автоматически через ACME (automatic certificate management environment).
hister.example.com {
reverse_proxy 127.0.0.1:4433
}Перезагрузите его с помощью sudo systemctl reload caddy. Перед выдачей сертификата должны быть выполнены два условия: A-запись для hister.example.com должна указывать на этот сервер, а порт 80 должен быть открыт, так как через него проходит проверка HTTP-01 challenge. Если хотя бы одно из условий не выполняется, браузер выдаст ошибку TLS вместо страницы, а в логах Caddy будут повторяться сообщения о неудачной проверке.
Затем закройте все остальные порты.
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw statusПорт 4433 намеренно отсутствует в этом списке. Публичное доменное имя — не единственный способ доступа, и onion-сервис, направленный на тот же loopback-порт, позволяет получить доступ к индексу с ваших устройств без DNS-записей и открытых входящих портов.
server.base_url должен совпадать с адресом, который вы вводите в браузере, включая схему. Если они не совпадают, интерфейс загрузится без стилей и изображений, так как сервер формирует ссылки на ресурсы на основе base_url, а браузер пытается запросить их у источника, который не отвечает. Этот же URL указывается в расширении для браузера.
Наполнение индекса
Браузерное расширение является основным инструментом сбора данных. Установите его из Mozilla Add-ons или Chrome Web Store, откройте страницу настроек, укажите URL сервера в https://hister.example.com и вставьте токен доступа. После этого расширение будет захватывать заголовок, полный текст, HTML-код и фавикон каждой посещаемой страницы и отправлять их на ваш сервер. Извлечение данных происходит на стороне клиента, внутри браузера. Расширение не взаимодействует со сторонними сервисами; единственный внешний запрос, который оно выполняет — это запрос фавикона страницы.
Извлечение данных на стороне клиента — это то, что делает возможным создание приватного индекса. Расширение видит страницу точно так же, как и вы: после авторизации и отрисовки контента. Благодаря этому внутренняя вики-страница или платная статья индексируются корректно, а серверу не требуются ваши учетные данные. Это также означает, что всё просматриваемое вами содержимое является кандидатом на индексацию, поэтому правила исключения (skip rules) имеют приоритет над добавлением контента.
Правила исключения хранятся в rules.json при однопользовательской установке или в базе данных для каждого пользователя. Вкладка Rules в веб-интерфейсе — самый простой способ их редактирования. Это регулярные выражения Go, которые сопоставляются с полным URL:
^https://mail\.example\.com
^https://bank\.example\.com
.*?utm_source=Шаблон вида ^mail.example.com никогда не сработает, так как проверяемая строка начинается с https://. Завершающий символ $ также приведет к ошибке для любого URL с параметрами запроса, так как параметры сохраняются при сопоставлении.
Импорт существующей истории выполняется путем чтения собственной базы данных браузера. Эта команда запускается на машине, где находится профиль браузера — то есть на вашем ноутбуке, а не на VPS. Установите тот же бинарный файл локально и укажите адрес сервера:
export HISTER_TOKEN='your-access-token'
hister import browser firefox -u https://hister.example.com -t "$HISTER_TOKEN"Импорт запускается как возобновляемая задача с именем browser-import-YYYY-MM-DD, поэтому вы можете прервать её и запустить снова позже. Сервисы закладок импортируются аналогичным образом, включая Linkwarden, Karakeep, Wallabag, Linkding, Readeck и Shaarli. Повторный импорт извлекает только те данные, которые новее предыдущего.
Файлы на сервере индексируются путем указания директорий в конфигурации:
indexer:
directories:
- path: '/var/lib/hister/documents'
label: 'documents'
filetypes: ['pdf', 'docx', 'md', 'txt']PDF, DOCX, Markdown, Org mode и текстовые файлы в кодировке UTF-8 считываются как полнотекстовые документы. Фотографии и видео в этот список не входят, поэтому для библиотеки изображений требуется сервер, который индексирует лица, места и даты, а не текст. Для этих целей обычно сравнивают PhotoPrism и Immich. Отдельная страница добавляется с помощью hister index https://example.com. Преобразование целых сайтов в чистый текст для других инструментов — это отдельная задача, которую решают самохостируемые краулеры, преобразующие страницы в чистый текст.
Поиск основан на полях, поэтому изучение языка запросов займет не более десяти минут:
"connection reset" domain:github.com added:<30d
title:(wireguard|nftables) -tutorial sort:-visitsИспользование агента для работы с собственным индексом через MCP
MCP (model context protocol) — это интерфейс, который ассистент использует для вызова инструментов на сервере. Hister предоставляет его по адресу POST /mcp с тем же базовым URL через транспорт streamable HTTP и открывает search, get_preview и get_history. Для аутентификации используется тот же bearer token, что и для остального API. Если вызов инструментов для вас в новинку, написать небольшой agent loop самостоятельно — самый быстрый способ понять, какие данные такой endpoint фактически передаёт ассистенту.
{
"mcpServers": {
"hister": {
"url": "https://hister.example.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
}
}
}Заголовок X-Access-Token работает как альтернатива Authorization.
Ценность этого подхода заключается в том, что именно ищет агент. Открытый веб-поиск возвращает то, что имеет высокий рейтинг сегодня, что для быстро развивающегося ПО часто означает документацию для версии, которую вы не используете. Ваш собственный индекс возвращает страницу, которую вы уже прочитали и решили сохранить, а get_preview предоставляет сохраненную копию, поэтому ответ остается доступным, даже если исходная страница исчезнет из сети. Предоставьте агенту оба источника, если вам нужны и публичные результаты: инструмент поиска в браузере на базе SearXNG добавляет открытый веб в качестве отдельного инструмента. Как только вы запустите более одного из этих эндпоинтов, стоит прочитать размещение MCP-серверов на VPS, так как каждый из них сталкивается с этой проблемой доступа.
Диск, резервное копирование и обслуживание
Документация оценивает одну индексированную страницу примерно в 100 KB, включая сжатый превью, поэтому сто тысяч страниц занимают около 10 GB. Система квот отсутствует. Две настройки часто путают друг с другом: indexer.max_file_size_mb (по умолчанию 1 MiB) ограничивает размер одного отслеживаемого файла, а server.max_batch_body_size (по умолчанию 40 MiB) ограничивает размер одного API-запроса.
В директории, указанной в app.directory, хранятся index.db с индексными файлами для каждого языка, db.sqlite3 для учетных записей и заданий, data/html/ для превью и rules.json. Резервная копия создается путем остановки службы и копирования всей этой директории вместе с файлом конфигурации. hister export backup.json экспортирует документы в формате JSON для миграции, это не является резервной копией сервера.
Следует знать две команды для обслуживания. hister reindex перестраивает поисковые индексы, что необходимо после изменения настроек индексатора. Если при импорте большого объема данных потребление памяти растет, установите detect_languages: false в секции indexer и выполните переиндексацию. hister cleanup удаляет осиротевшие файлы превью и иконок (favicon), оставшиеся после удаления страниц.
Удаление выполняется через запрос, поэтому сначала запустите его в режиме имитации (dry mode):
hister delete 'domain:example.com' --dry --verboseУдаленная страница восстановится, если сборщик продолжит её отправлять, поэтому добавьте правило пропуска перед удалением.
Лицензия AGPLv3 имеет значение только в том случае, если вы вносите изменения в код. Использование неизмененной копии для собственных нужд не накладывает никаких обязательств. Если вы модифицируете Hister и предоставляете другим пользователям доступ к своей версии по сети, лицензия требует предоставить им исходный код ваших изменений.
Типичные сбои и сообщения об ошибках
Сервер не запускается. Либо порт 4433 уже занят, либо в файле конфигурации допущена синтаксическая ошибка YAML. Команда sudo ss -lntp | grep 4433 покажет, какой процесс использует порт, а journalctl -u hister -n 50 --no-pager выведет ошибку парсинга.
Интерфейс загружается, но отображается некорректно. Искаженный текст и отсутствие изображений означают, что server.base_url не совпадает с URL в адресной строке. Завершающий слэш также считается несовпадением.
Расширение не подключается. URL сервера в настройках расширения должен в точности соответствовать base_url, сервер должен быть запущен и обновлен, а сетевой экран между ними может блокировать соединение без вывода сообщений на странице. Firefox не выводит логи расширений в обычную консоль: откройте about:debugging#/runtime/this-firefox и изучите логи расширения Hister.
Контейнер завершает работу при запуске. Ошибка прав доступа к ./data означает, что владельцем директории является UID, отличный от 1000, который используется учетной записью внутри стандартного образа.
Ошибка 403 Forbidden при обращении к административному маршруту. POST /api/reindex и POST /api/cleanup доступны только администраторам при включенной обработке пользователей, поэтому обычным учетным записям доступ к ним запрещен.
Потребление памяти растет во время импорта. Обычно это вызвано определением языка в большой истории. Установите detect_languages: false и после этого выполните hister reindex.
FAQ
Чем Hister отличается от SearXNG?
SearXNG — это метапоисковый прокси: он перенаправляет ваш запрос к публичным поисковым системам и возвращает их результаты, удаляя трекеры, поэтому индекс принадлежит этим системам. Hister хранит собственный полнотекстовый индекс страниц, которые вы посетили, и файлов, которые вы сохранили, поэтому он отвечает на вопрос «где я это читал», в то время как SearXNG отвечает на вопрос «что об этом говорит интернет». Они решают разные задачи, и многие пользователи запускают оба сервиса на одном сервере.
Безопасно ли хранить всю историю браузера на VPS?
Только при условии предварительной настройки доступа. Hister привязывается к 127.0.0.1:4433 и по умолчанию не требует аутентификации. Установите app.access_token или user_handling: true, настройте перед ним reverse proxy с TLS и закройте порт 4433 на межсетевом экране. Полнотекстовый индекс вашей истории — это обычный текст, поэтому любой, кто получит доступ к порту, сможет прочитать всё без необходимости взлома.
Нужно ли мне расширение для браузера или достаточно импортировать историю?
Импорт — это разовая операция по заполнению данных. Он считывает собственную базу данных истории браузера, поэтому выполняется на компьютере, где находится профиль браузера, а не на сервере. Расширение поддерживает индекс в актуальном состоянии после импорта и захватывает страницы, требующие авторизации, так как извлекает контент в браузере после отрисовки страницы. Стандартная схема использования: один импорт, затем работа через расширение.
Может ли агент для программирования искать по моему индексу Hister?
Да. Hister является MCP-сервером (model context protocol) по адресу POST /mcp относительно вашего базового URL, предоставляя методы search, get_preview и get_history. Укажите клиенту адрес https://your-host/mcp с заголовком Authorization: Bearer, содержащим ваш токен доступа. После этого агент сможет искать по документации, которую вы действительно читали, и именно в той версии, которую вы изучали, а не по результатам, которые сегодня выдает публичный поисковик.