Безопасность¶
Токен — единственный секрет приложения¶
Всё, что защищает доступ к агенту, — это токен. Никаких паролей, аккаунтов и подписей в
протоколе нет: неверный токен → кадр 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.- Либо штатный
pairingHermes: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, но это не заменяет здравого смысла: содержимое сообщений — обычный ввод агента.