Перейти к содержанию

Безопасность

Токен — единственный секрет приложения

Всё, что защищает доступ к агенту, — это токен. Никаких паролей, аккаунтов и подписей в протоколе нет: неверный токен → кадр error (auth_failed) и закрытие соединения кодом 1008.

  • Храните токен в Keychain (iOS) / Keystore (Android), не в открытом виде, не в исходниках и не в логах.
  • Сервер никогда не пишет токен в логи — он маскируется (protocol.redact_token). Делайте так же.
  • Платформа без MOBILE_TOKEN не поднимается вообще (config_missing): адаптер не бывает «открытым». Пустой список разрешённых токенов — отказ.

Почему нельзя вшивать общий токен в сборку

Для публичного приложения (APK/IPA раздают другим людям) один вшитый токен означает: любой владелец сборки получает доступ к агенту. Достаточно распаковать приложение и вытащить строку.

Публичная сборка должна быть клиентом без секретов: пользователь вводит свой адрес и свой токен. Владелец агента выдаёт токены по устройствам.

  • Свой агент (вы владеете и агентом, и приложением) — можно вшить токен, если сборка не уходит наружу.
  • Приложение для чужих агентов — вшивать нельзя ни в каком виде.

Свой токен на каждое устройство

Выдавайте отдельный токен каждому устройству/разработчику:

  • MOBILE_TOKEN — первый токен; MOBILE_EXTRA_TOKENS=tok2,tok3 — остальные (через запятую или перевод строки). Элемент вида device_id=token привязывает токен к устройству: MOBILE_EXTRA_TOKENS=tel-1=tok2,tablet-2=tok3.
  • Либо штатный pairing Hermes: hermes pairing list → hermes pairing approve mobile <id>.

Тогда отзыв одного токена не задевает остальных, а в логах видно, кто именно подключался (по device.id). Сессии разделяются по device.id — диалоги разных устройств не смешиваются.

Привязка токена к устройству

Общий токен сам по себе не мешает клиенту заявить чужой device.id и перехватить чужую сессию, ответы и карточки одобрения. Поэтому MOBILE_EXTRA_TOKENS принимает элементы device_id=token:

  • занятый device.id нельзя перехватить — подключение с тем же id и другим токеном получает error{code:"forbidden"} и закрытие 1008;
  • то же устройство с тем же токеном — это переподключение: старое соединение закрывается, новое занимает сессию (штатное поведение при переустановке/смене сети).

Только wss

Наружу — только wss:// (TLS на reverse-proxy). ws:// без шифрования передаёт текст, медиа и голос открыто.

  • ws:// допустим только в локальной сети или при разработке (adb reverse tcp:8787 tcp:8787).
  • Токен передаётся только полем token в кадре hello — приём через ?token= в URL удалён: query-строка попадает в логи reverse-proxy, тело кадра по TLS защищено.
  • По умолчанию адаптер слушает loopback 127.0.0.1:8787/ws и снаружи недоступен вовсе. Удалённый доступ — осознанный шаг: MOBILE_HOST=0.0.0.0, закрытый wss-прокси (TLS) и firewall; «как есть» порт 8787 наружу не выставляйте.

Отзыв доступа

Отзыв токена = убрать его из .env (MOBILE_TOKEN или MOBILE_EXTRA_TOKENS) и перезапустить шлюз (hermes gateway restart). Активное соединение закрывать вручную не нужно: без валидного токена переподключение получит auth_failed и код 1008.

Если нужно ограничить, какие именно устройства подключаются, — используйте MOBILE_ALLOWED_DEVICES (список device.id). Кто из пользователей допущен — решает штатная авторизация Hermes (MOBILE_ALLOWED_USERS / pairing). Подробнее о разнице переменных — в разделе Как всё устроено.

Одобрение опасных команд

Ответ на карточку-вопрос валидируется на стороне адаптера: недопустимый option_id заменяется на безопасное значение (deny для одобрения команды, cancel для слэш-команды). «Подделать» одобрение со стороны клиента нельзя — сервер не выполнит ничего лишнего из выдуманного option_id.

Карточку (exec_approval) может закрыть только то устройство, которому она показана: ответ с другого соединения (тот же общий токен, но другой device.id) получает error{code:"forbidden"}. Для exec_approval сервер шлёт отдельные поля command и description — приложение обязано показать точную команду рядом с кнопками, чтобы человек видел, что именно одобряет.

Служебная доставка от cron

Кадр deliver (cron/hermes send → устройство) — служебный: его принимает только соединение с loopback-адреса (127.0.0.0/8, ::1) или соединение с токеном из MOBILE_DELIVER_TOKENS. Всем остальным сервер отвечает error{code:"forbidden"} — обычный клиент не может разослать сообщение от лица cron. Задавайте MOBILE_DELIVER_TOKENS только для тех токенов, что действительно используются служебной доставкой вне loopback; в типовой схеме (cron на той же машине) переменная не нужна.

Что видит агент

Всё, что отправлено через протокол (текст, голос, медиа, подписи), — часть диалога с агентом, наравне с сообщениями из Telegram/CLI. Эти данные попадают в историю и память агента и могут быть отправлены в модель.

  • Не отправляйте через протокол то, что агент не должен видеть.
  • Медиа сохраняется в каталог профиля на машине агента (~/.hermes/mobile-media).
  • Плагин помечен pii_safe=True, но это не заменяет здравого смысла: содержимое сообщений — обычный ввод агента.