Вебхуки
Принимайте события от внешних сервисов (GitHub, GitLab, JIRA, Stripe и др.) и автоматически запускайте агента Hermes. Адаптер вебхуков запускает HTTP-сервер, который принимает POST-запросы, проверяет HMAC-подписи, преобразует полезные нагрузки в промпты агента и направляет ответы обратно источнику или на другую настроенную платформу.
Агент обрабатывает событие и может ответить, оставляя комментарии к PR, отправляя сообщения в Telegram/Discord или записывая результат в лог.
Видеоурок
Быстрый старт
-
Включите через
hermes gateway setupили переменные окружения -
Определите маршруты в
config.yamlили создайте их динамически с помощьюhermes webhook subscribe -
Укажите вашему сервису адрес
http://your-server:8644/webhooks/<route-name>
Настройка
Есть два способа включить адаптер вебхуков.
Через мастер настройки
hermes gateway setup
Следуйте подсказкам, чтобы включить вебхуки, указать порт и задать глобальный HMAC-секрет.
Через переменные окружения
Добавьте в ~/.hermes/.env:
WEBHOOK_ENABLED=true
WEBHOOK_PORT=8644 # default
WEBHOOK_SECRET=your-global-secret
Проверка сервера
Когда шлюз запущен:
curl http://localhost:8644/health
Ожидаемый ответ:
{"status": "ok", "platform": "webhook"}
Настройка маршрутов {#configuring-routes}
Маршруты определяют, как обрабатываются разные источники вебхуков. Каждый маршрут — это именованная запись в разделе platforms.webhook.extra.routes вашего config.yaml.
Свойства маршрута
| Свойство | Обязательно | Описание |
|---|---|---|
events |
Нет | Список типов событий для приёма (например, ["pull_request"]). Если пусто, принимаются все события. Тип события читается из X-GitHub-Event, X-GitLab-Event или event_type в полезной нагрузке. |
secret |
Да | HMAC-секрет для проверки подписи. Используется глобальный secret, если не задан на маршруте. Установите "INSECURE_NO_AUTH" только для тестирования (пропускает проверку). |
prompt |
Нет | Строка шаблона с доступом к полям через точечную нотацию (например, {pull_request.title}). Если опущено, полная JSON-полезная нагрузка помещается в промпт. |
skills |
Нет | Список названий навыков для загрузки при запуске агента. |
deliver |
Нет | Куда отправлять ответ: github_comment, telegram, discord, slack, signal, sms, whatsapp, matrix, mattermost, homeassistant, email, dingtalk, feishu, wecom, weixin, bluebubbles, qqbot или log (по умолчанию). |
deliver_extra |
Нет | Дополнительная конфигурация доставки — ключи зависят от типа deliver (например, repo, pr_number, chat_id). Значения поддерживают те же шаблоны {dot.notation}, что и prompt. |
deliver_only |
Нет | Если true, полностью пропустить агента — отрендеренный шаблон prompt становится буквальным сообщением для доставки. Нулевая стоимость LLM, доставка за доли секунды. См. Режим прямой доставки для примеров использования. Требует, чтобы deliver был реальной целью (не log). |
Полный пример
platforms:
webhook:
enabled: true
extra:
port: 8644
secret: "global-fallback-secret"
routes:
github-pr:
events: ["pull_request"]
secret: "github-webhook-secret"
prompt: |
Review this pull request:
Repository: {repository.full_name}
PR #{number}: {pull_request.title}
Author: {pull_request.user.login}
URL: {pull_request.html_url}
Diff URL: {pull_request.diff_url}
Action: {action}
skills: ["github-code-review"]
deliver: "github_comment"
deliver_extra:
repo: "{repository.full_name}"
pr_number: "{number}"
deploy-notify:
events: ["push"]
secret: "deploy-secret"
prompt: "New push to {repository.full_name} branch {ref}: {head_commit.message}"
deliver: "telegram"
Шаблоны промптов
Промпты используют точечную нотацию для доступа к вложенным полям полезной нагрузки вебхука:
-
{pull_request.title}соответствуетpayload["pull_request"]["title"] -
{repository.full_name}соответствуетpayload["repository"]["full_name"] -
{__raw__}— специальный токен, который выводит всю полезную нагрузку в виде JSON с отступами (обрезается до 4000 символов). Полезно для мониторинга оповещений или общих вебхуков, где агенту нужен полный контекст. -
Отсутствующие ключи остаются как литеральная строка
{key}(без ошибки) -
Вложенные словари и списки сериализуются в JSON и обрезаются до 2000 символов
Вы можете смешивать {__raw__} с обычными переменными шаблона:
prompt: "PR #{pull_request.number} by {pull_request.user.login}: {__raw__}"
Если для маршрута не настроен шаблон prompt, вся полезная нагрузка выводится в виде JSON с отступами (обрезается до 4000 символов).
Те же шаблоны с точечной нотацией работают и в значениях deliver_extra.
Доставка в топики форума
При доставке ответов вебхуков в Telegram вы можете указать конкретный топик форума, включив message_thread_id (или thread_id) в deliver_extra:
webhooks:
routes:
alerts:
events: ["alert"]
prompt: "Alert: {__raw__}"
deliver: "telegram"
deliver_extra:
chat_id: "-1001234567890"
message_thread_id: "42"
Если chat_id не указан в deliver_extra, доставка использует домашний канал, настроенный для целевой платформы.
Ревью GitHub PR (пошагово) {#github-pr-review}
Эта инструкция настраивает автоматическое ревью кода на каждый pull request.
1. Создание вебхука в GitHub
-
Перейдите в репозиторий → Settings → Webhooks → Add webhook
-
Установите Payload URL в
http://your-server:8644/webhooks/github-pr -
Установите Content type в
application/json -
Установите Secret, соответствующий конфигурации вашего маршрута (например,
github-webhook-secret) -
В разделе Which events? выберите Let me select individual events и отметьте Pull requests
-
Нажмите Add webhook
2. Добавление конфигурации маршрута
Добавьте маршрут github-pr в ваш ~/.hermes/config.yaml, как показано в примере выше.
3. Убедитесь, что CLI gh аутентифицирован
Тип доставки github_comment использует GitHub CLI для публикации комментариев:
gh auth login
4. Тестирование
Откройте pull request в репозитории. Вебхук срабатывает, Hermes обрабатывает событие и публикует ревью-комментарий в PR.
Настройка вебхука GitLab {#gitlab-webhook-setup}
Вебхуки GitLab работают аналогично, но используют другой механизм аутентификации. GitLab отправляет секрет в виде обычного заголовка X-Gitlab-Token (точное совпадение строк, не HMAC).
1. Создание вебхука в GitLab
-
Перейдите в проект → Settings → Webhooks
-
Установите URL в
http://your-server:8644/webhooks/gitlab-mr -
Введите ваш Secret token
-
Выберите Merge request events (и любые другие события, которые вам нужны)
-
Нажмите Add webhook
2. Добавление конфигурации маршрута
platforms:
webhook:
enabled: true
extra:
routes:
gitlab-mr:
events: ["merge_request"]
secret: "your-gitlab-secret-token"
prompt: |
Review this merge request:
Project: {project.path_with_namespace}
MR !{object_attributes.iid}: {object_attributes.title}
Author: {object_attributes.last_commit.author.name}
URL: {object_attributes.url}
Action: {object_attributes.action}
deliver: "log"
Варианты доставки {#delivery-options}
Поле deliver определяет, куда направляется ответ агента после обработки события вебхука.
| Тип доставки | Описание |
|---|---|
log |
Записывает ответ в вывод лога шлюза. Это значение по умолчанию, полезно для тестирования. |
github_comment |
Публикует ответ как комментарий к PR/issue через CLI gh. Требует deliver_extra.repo и deliver_extra.pr_number. CLI gh должен быть установлен и аутентифицирован на хосте шлюза (gh auth login). |
telegram |
Направляет ответ в Telegram. Использует домашний канал или укажите chat_id в deliver_extra. |
discord |
Направляет ответ в Discord. Использует домашний канал или укажите chat_id в deliver_extra. |
slack |
Направляет ответ в Slack. Использует домашний канал или укажите chat_id в deliver_extra. |
signal |
Направляет ответ в Signal. Использует домашний канал или укажите chat_id в deliver_extra. |
sms |
Направляет ответ в SMS через Twilio. Использует домашний канал или укажите chat_id в deliver_extra. |
whatsapp |
Направляет ответ в WhatsApp. Использует домашний канал или укажите chat_id в deliver_extra. |
matrix |
Направляет ответ в Matrix. Использует домашний канал или укажите chat_id в deliver_extra. |
mattermost |
Направляет ответ в Mattermost. Использует домашний канал или укажите chat_id в deliver_extra. |
homeassistant |
Направляет ответ в Home Assistant. Использует домашний канал или укажите chat_id в deliver_extra. |
email |
Направляет ответ на Email. Использует домашний канал или укажите chat_id в deliver_extra. |
dingtalk |
Направляет ответ в DingTalk. Использует домашний канал или укажите chat_id в deliver_extra. |
feishu |
Направляет ответ в Feishu/Lark. Использует домашний канал или укажите chat_id в deliver_extra. |
wecom |
Направляет ответ в WeCom. Использует домашний канал или укажите chat_id в deliver_extra. |
weixin |
Направляет ответ в Weixin (WeChat). Использует домашний канал или укажите chat_id в deliver_extra. |
bluebubbles |
Направляет ответ в BlueBubbles (iMessage). Использует домашний канал или укажите chat_id в deliver_extra. |
Для кроссплатформенной доставки целевая платформа также должна быть включена и подключена к шлюзу. Если chat_id не указан в deliver_extra, ответ отправляется в настроенный домашний канал этой платформы.
Режим прямой доставки {#direct-delivery-mode}
По умолчанию каждый POST-запрос вебхука запускает сессию агента — полезная нагрузка становится промптом, агент обрабатывает её, и ответ агента доставляется. Это расходует токены LLM на каждое событие.
Для случаев, когда нужно просто отправить обычное уведомление — без рассуждений, без цикла агента, просто доставить сообщение — установите deliver_only: true на маршруте. Отрендеренный шаблон prompt становится телом сообщения, и адаптер отправляет его напрямую в настроенный канал доставки.
Когда использовать прямую доставку
-
Внешний push-сервис — вебхук Supabase/Firebase срабатывает при изменении в БД → мгновенное уведомление пользователя в Telegram
-
Мониторинг оповещений — вебхук оповещений Datadog/Grafana → отправка в канал Discord
-
Межагентские уведомления — Агент A уведомляет пользователя Агента B о завершении долгой задачи
-
Завершение фоновых задач — Cron-задача завершена → публикация результата в Slack
Преимущества:
-
Нулевая стоимость LLM — агент никогда не вызывается
-
Доставка за доли секунды — один вызов адаптера, без цикла рассуждений
-
Та же безопасность, что и в режиме агента — HMAC-аутентификация, лимиты запросов, идемпотентность и ограничения размера тела всё ещё применяются
-
Синхронный ответ — POST возвращает
200 OKпосле успешной доставки или502, если целевая платформа отклоняет сообщение, так что ваш вышестоящий сервис может интеллектуально повторить попытку
Пример: Telegram push из Supabase
platforms:
webhook:
enabled: true
extra:
port: 8644
secret: "global-secret"
routes:
antenna-matches:
secret: "antenna-webhook-secret"
deliver: "telegram"
deliver_only: true
prompt: "🎉 New match: {match.user_name} matched with you!"
deliver_extra:
chat_id: "{match.telegram_chat_id}"
Ваша edge-функция Supabase подписывает полезную нагрузку HMAC-SHA256 и отправляет POST на https://your-server:8644/webhooks/antenna-matches. Адаптер вебхуков проверяет подпись, рендерит шаблон из полезной нагрузки, доставляет в Telegram и возвращает 200 OK.
Пример: динамическая подписка через CLI
hermes webhook subscribe antenna-matches \\
--deliver telegram \\
--deliver-chat-id "123456789" \\
--deliver-only \\
--prompt "🎉 New match: {match.user_name} matched with you!" \\
--description "Antenna match notifications"
Коды ответов
| Статус | Значение |
|---|---|
200 OK |
Успешно доставлено. Тело: {"status": "delivered", "route": "...", "target": "...", "delivery_id": "..."} |
200 OK (status=duplicate) |
Повторный X-GitHub-Delivery ID в пределах TTL идемпотентности (1 час). Не доставляется повторно. |
401 Unauthorized |
HMAC-подпись недействительна или отсутствует. |
400 Bad Request |
Некорректное JSON-тело запроса. |
404 Not Found |
Неизвестное имя маршрута. |
413 Payload Too Large |
Тело превысило max_body_bytes. |
429 Too Many Requests |
Превышен лимит запросов для маршрута. |
502 Bad Gateway |
Целевой адаптер отклонил сообщение или вернул ошибку. Ошибка логируется на сервере; тело ответа содержит общее сообщение Delivery failed, чтобы не раскрывать внутренности адаптера. |
Подводные камни конфигурации
-
deliver_only: trueтребует, чтобыdeliverбыл реальной целью.deliver: log(или отсутствиеdeliver) отклоняется при запуске — адаптер отказывается запускаться, если находит неправильно настроенный маршрут. -
Поле
skillsигнорируется в режиме прямой доставки (нет запусков агента, поэтому некуда внедрять навыки). -
Рендеринг шаблонов использует тот же синтаксис
{dot.notation}, что и в режиме агента, включая токен{__raw__}. -
Идемпотентность использует тот же заголовок
X-GitHub-Delivery/X-Request-ID— повторные попытки с тем же ID возвращаютstatus=duplicateи НЕ доставляются повторно.
Динамические подписки (CLI) {#dynamic-subscriptions}
В дополнение к статическим маршрутам в config.yaml, вы можете создавать подписки вебхуков динамически с помощью команды CLI hermes webhook. Это особенно полезно, когда сам агент должен настраивать управляемые событиями триггеры.
Создание подписки
hermes webhook subscribe github-issues \\
--events "issues" \\
--prompt "New issue #{issue.number}: {issue.title}\\nBy: {issue.user.login}\\n\\n{issue.body}" \\
--deliver telegram \\
--deliver-chat-id "-100123456789" \\
--description "Triage new GitHub issues"
Это возвращает URL вебхука и автоматически сгенерированный HMAC-секрет. Настройте ваш сервис на отправку POST на этот URL.
Список подписок
hermes webhook list
Удаление подписки
hermes webhook remove github-issues
Тестирование подписки
hermes webhook test github-issues
hermes webhook test github-issues --payload '{"issue": {"number": 42, "title": "Test"}}'
Как работают динамические подписки
-
Подписки хранятся в
~/.hermes/webhook_subscriptions.json -
Адаптер вебхуков перезагружает этот файл при каждом входящем запросе (по времени изменения файла, с незначительными накладными расходами)
-
Статические маршруты из
config.yamlвсегда имеют приоритет над динамическими с тем же именем -
Динамические подписки используют тот же формат маршрута и возможности, что и статические (события, шаблоны промптов, навыки, доставка)
-
Перезапуск шлюза не требуется — подпишитесь, и подписка сразу активна
Подписки, управляемые агентом
Агент может создавать подписки через инструмент терминала, если его направляет навык webhook-subscriptions. Попросите агента "настроить вебхук для GitHub issues", и он выполнит соответствующую команду hermes webhook subscribe.
Безопасность {#security}
Адаптер вебхуков включает несколько уровней безопасности:
Проверка HMAC-подписи
Адаптер проверяет входящие подписи вебхуков, используя соответствующий метод для каждого источника:
-
GitHub: заголовок
X-Hub-Signature-256— HMAC-SHA256 hex-дайджест с префиксомsha256= -
GitLab: заголовок
X-Gitlab-Token— точное совпадение строки секрета -
Общий: заголовок
X-Webhook-Signature— сырой HMAC-SHA256 hex-дайджест
Если секрет настроен, но ни один из распознаваемых заголовков подписи не присутствует, запрос отклоняется.
Секрет обязателен
Каждый маршрут должен иметь секрет — либо заданный непосредственно на маршруте, либо унаследованный от глобального secret. Маршруты без секрета вызывают ошибку при запуске адаптера. Только для разработки/тестирования вы можете установить секрет в "INSECURE_NO_AUTH", чтобы полностью пропустить проверку.
INSECURE_NO_AUTH принимается только когда шлюз привязан к loopback-хосту (127.0.0.1, localhost, ::1). Если он используется с привязкой не к loopback, например 0.0.0.0 или LAN IP, адаптер отказывается запускаться — это предотвращает случайное раскрытие неаутентифицированной конечной точки на публичном интерфейсе.
Ограничение частоты запросов
Каждый маршрут по умолчанию ограничен 30 запросами в минуту (фиксированное окно). Настройте это глобально:
platforms:
webhook:
extra:
rate_limit: 60 # requests per minute
Запросы, превышающие лимит, получают ответ 429 Too Many Requests.
Идемпотентность
Идентификаторы доставки (из X-GitHub-Delivery, X-Request-ID или запасной вариант с временной меткой) кэшируются на 1 час. Повторные доставки (например, повторные попытки вебхука) молча пропускаются с ответом 200, предотвращая дублирующиеся запуски агента.
Ограничения размера тела
Полезные нагрузки, превышающие 1 MB, отклоняются до чтения тела. Настройте это:
platforms:
webhook:
extra:
max_body_bytes: 2097152 # 2 MB
Риск инъекции в промпт
Устранение неполадок {#troubleshooting}
Вебхук не приходит
-
Проверьте, что порт открыт и доступен из источника вебхука
-
Проверьте правила брандмауэра — порт
8644(или ваш настроенный порт) должен быть открыт -
Проверьте, что путь URL совпадает:
http://your-server:8644/webhooks/<route-name> -
Используйте конечную точку
/health, чтобы убедиться, что сервер работает
Не проходит проверка подписи
-
Убедитесь, что секрет в конфигурации маршрута точно соответствует секрету, настроенному в источнике вебхука
-
Для GitHub секрет основан на HMAC — проверьте
X-Hub-Signature-256 -
Для GitLab секрет — это простое совпадение токена — проверьте
X-Gitlab-Token -
Проверьте логи шлюза на наличие предупреждений
Invalid signature
Событие игнорируется
-
Проверьте, что тип события находится в списке
eventsвашего маршрута -
События GitHub используют значения, такие как
pull_request,push,issues(значение заголовкаX-GitHub-Event) -
События GitLab используют значения, такие как
merge_request,push(значение заголовкаX-GitLab-Event) -
Если
eventsпусто или не задано, принимаются все события
Агент не отвечает
-
Запустите шлюз в интерактивном режиме, чтобы увидеть логи:
hermes gateway run -
Проверьте, что шаблон промпта рендерится корректно
-
Проверьте, что целевой канал доставки настроен и подключён
Дублирующиеся ответы
-
Кэш идемпотентности должен предотвращать это — проверьте, что источник вебхука отправляет заголовок ID доставки (
X-GitHub-DeliveryилиX-Request-ID) -
Идентификаторы доставки кэшируются на 1 час
Ошибки CLI gh (доставка комментариев GitHub)
-
Запустите
gh auth loginна хосте шлюза -
Убедитесь, что аутентифицированный пользователь GitHub имеет доступ на запись к репозиторию
-
Проверьте, что
ghустановлен и находится в PATH
Переменные окружения {#environment-variables}
| Переменная | Описание | По умолчанию |
|---|---|---|
WEBHOOK_ENABLED |
Включить адаптер платформы вебхуков | false |
WEBHOOK_PORT |
Порт HTTP-сервера для приёма вебхуков | 8644 |
WEBHOOK_SECRET |
Глобальный HMAC-секрет (используется как запасной, когда маршруты не указывают свой) | (нет) |