Webhooks de Hermes: agentes impulsados por eventos sin un prompt gigante que lo abarque todo
Intermedio7 min de lecturaAutomatizaciones

Webhooks de Hermes: agentes impulsados por eventos sin un prompt gigante que lo abarque todo

Configura webhooks de Hermes Agent con la autenticación adecuada para cada proveedor, controles de estado en el puerto 8644 y rutas pequeñas con nombre, para que los eventos desencadenen ejecuciones de agente enfocadas y con un destino de entrega explícito.

Lo que deberías poder hacer

Los webhooks superan a cron cuando acaba de ocurrir algo. Protege cada ruta de Hermes con el método de autenticación que exija su origen, verifica /health en el puerto 8644 y asigna a cada tipo de evento un prompt limitado y un destino de entrega configurado.

Guardado solo en este navegador.
En este artículo

Cron pregunta «¿es la hora?». Los webhooks dicen «esto acaba de ocurrir, actúa». Para el trabajo con agentes, esa distinción importa. Un repaso programado de la bandeja es distinto de «se creó una disputa de Stripe» o «se abrió un pull request contra main».

Los webhooks de Hermes Agent convierten eventos HTTP POST autenticados en ejecuciones de agente cuyos resultados llegan a un destino de entrega configurado. Bien usados, son puertas pequeñas con cerraduras y tareas claras. Mal usados, son un puerto expuesto con un prompt gigante que intenta manejar cada fragmento de JSON que envía internet.

Este artículo cubre el diseño de rutas, la autenticación adecuada para cada proveedor, el control de estado documentado y una prueba básica práctica. Combínalo con el endurecimiento de la primera semana en /articles/hermes-first-week-memory-and-skills antes de exponer nada más allá de localhost.

Cuándo los webhooks son el desencadenante correcto

Prefiere webhooks cuando:

  • La latencia importa (revisar un PR mientras el autor aún cambia de contexto).
  • El sistema fuente ya emite eventos (GitHub, GitLab, Jira, Stripe, formularios internos).
  • Cada evento debería convertirse en una tarea enfocada, no en una sesión conversacional larga.

Prefiere cron cuando:

  • Sondeas el sistema en busca de deriva («¿algún certificado que caduque en 14 días?»).
  • La fuente no puede hacer push.
  • Quieres un brief periódico tranquilo en lugar de interrupciones por evento.

La guía oficial de Hermes coincide con esta división: cron para las comprobaciones programadas y los webhooks para las ejecuciones desencadenadas por eventos.

Arquitectura en una imagen

Sistema de origen (GitHub / GitLab / n8n / aplicación propia)
        |  HTTPS POST + autenticación adecuada al origen
        v
Adaptador de webhooks de Hermes  (puerto predeterminado 8644)
        |  ruta: /webhooks/<name>
        v
Ruta con nombre (filtros + prompt + entrega)
        v
Ejecución del agente (skills/herramientas según tu política de aprobación)
        v
Entrega configurada (canal de chat, comentario de GitHub o registro)

n8n puede situarse a la izquierda como capa de validación y concentración: valida los campos, descarta lo innecesario y luego envía una carga útil mínima a Hermes mediante POST. Es una arquitectura ilustrativa descrita en /articles/hermes-vs-n8n-choose-by-job, no una integración llave en mano documentada por el proveedor. Si n8n necesita recuperar el resultado del agente dentro de su flujo de trabajo, llama al servidor API de Hermes independiente, que usa por defecto el puerto 8642 y autenticación con token bearer, en lugar de tratar el adaptador de webhooks como un callback síncrono.

Proceso de configuración (verifícalo contra la documentación vigente)

La documentación de webhooks del proyecto original describe este proceso:

  1. Habilita la plataforma de webhooks (hermes gateway setup o env como WEBHOOK_ENABLED=true).
  2. Configura un secreto para cada ruta. Usa el encabezado HMAC de GitHub, el encabezado de token simple de GitLab o el HMAC V2 con marca de tiempo para emisores genéricos, según corresponda al origen.
  3. Crea una ruta con nombre en la config o vía hermes webhook subscribe (comando según la documentación actual).
  4. Comprobación de estado: curl http://localhost:8644/health
  5. Apunta el sistema externo a https://your-host/webhooks/<name>
  6. Envía una carga útil de prueba autenticada; confirma la ruta, el prompt, el alcance de las herramientas y el destino de entrega esperados.

El puerto documentado por defecto es 8644. Los valores predeterminados documentados también limitan cada ruta a 30 solicitudes por minuto y rechazan cuerpos de más de 1 MB. Si has cambiado estos valores, prueba los límites configurados en lugar de confiar en los predeterminados.

Los cambios de configuración estática pueden requerir el ciclo de vida de la pasarela que documente la versión instalada. Las rutas dinámicas creadas con hermes webhook subscribe se recargan en caliente sin reiniciar y reciben un secreto generado automáticamente. En ambos casos, confirma que el proceso de la pasarela ve el perfil y el entorno previstos; que un comando funcione en un shell interactivo no demuestra que el daemon tenga la misma configuración.

La autenticación de rutas no es opcional

Cada ruta debe heredar o definir un secreto; de lo contrario, el adaptador falla al arrancar. La autenticación depende del proveedor: GitHub usa X-Hub-Signature-256, GitLab usa una coincidencia exacta de X-Gitlab-Token y los emisores personalizados genéricos deben usar HMAC V2 con marca de tiempo. La autenticación del emisor demuestra que la solicitud procede de alguien que posee el secreto, pero no convierte las instrucciones de la carga útil en contenido de confianza.

Reglas que aguantan en producción:

  • Genera un secreto aleatorio largo; guárdalo en un gestor de secretos o en un archivo de entorno con permisos restringidos, nunca en el Markdown de una skill que el agente pueda leer fácilmente.
  • Prefiere secretos por ruta cuando los sistemas tienen distintos niveles de confianza (app de GitHub vs formulario interno vs webhook de partner).
  • Rechaza en el borde las peticiones sin firma o con firma inválida; no «registres y continúes».
  • Usa INSECURE_NO_AUTH solo para pruebas temporales en la interfaz de bucle local. El adaptador se niega a arrancar si combinas ese valor con una dirección que no sea de bucle local, como 0.0.0.0 o una dirección LAN.

Para emisores propios, usa el esquema genérico V2 actual de Hermes: X-Webhook-Timestamp son segundos Unix; X-Webhook-Signature-V2 es el digest HMAC-SHA256 en hexadecimal en minúsculas de <timestamp>.<raw-body>. Hermes rechaza las marcas de tiempo fuera de una ventana de ±300 segundos. V1 solo firma el cuerpo y carece de protección contra repeticiones, así que no construyas emisores nuevos sobre él (contrato oficial de seguridad de webhooks).

Prueba básica firmada y reproducible

Tras crear una ruta llamada support-triage, pon una carga útil sin datos sensibles en payload.json. Haz que tu mecanismo aprobado de inyección de secretos defina WEBHOOK_SECRET antes de que arranque este shell; no escribas un secreto de producción en el historial de comandos. El comando Node de abajo lee la clave del entorno en lugar de expandirla en los argumentos del proceso y firma exactamente los bytes del archivo:

: "${WEBHOOK_SECRET:?inject a disposable route secret before running this test}"
timestamp="$(date +%s)"
signature="$(TIMESTAMP="$timestamp" node -e '
  const { createHmac } = require("node:crypto");
  const { readFileSync } = require("node:fs");
  const hmac = createHmac("sha256", process.env.WEBHOOK_SECRET);
  hmac.update(`${process.env.TIMESTAMP}.`, "utf8");
  hmac.update(readFileSync("payload.json"));
  process.stdout.write(hmac.digest("hex"));
')"

curl --fail-with-body \
  -H 'Content-Type: application/json' \
  -H "X-Webhook-Timestamp: $timestamp" \
  -H "X-Webhook-Signature-V2: $signature" \
  --data-binary @payload.json \
  http://127.0.0.1:8644/webhooks/support-triage

Después repite la prueba sin ninguna de las dos cabeceras de firma y con una marca de tiempo de más de 300 segundos de antigüedad. Ambas deben rechazarse. No pegues un secreto real en capturas de pantalla, tickets o el historial del shell; usa un secreto de ruta desechable para las pruebas de documentación y rótalo después.

Un webhook expuesto que puede colocar texto controlado por un atacante frente a un agente capaz de usar el terminal crea un riesgo de ejecución remota de herramientas. La autenticación limita quién puede enviar eventos, pero el texto de una carga útil autenticada también puede ser adversarial. Usa TLS y controles de red, minimiza las cargas útiles, limita o desactiva las herramientas de terminal, archivos y acciones salientes, y aísla la ejecución del host. Las solicitudes de aprobación son un control de la intención del operador, no un sandbox contra entradas hostiles.

Qué poner en la carga útil

Envía al agente un contrato, no un chorro de datos sin filtrar:

{
  "event_type": "github.pull_request.opened",
  "repo": "acme/api",
  "pr_number": 1842,
  "title": "Add billing retry worker",
  "author": "ada",
  "base_ref": "main",
  "html_url": "https://github.example.invalid/acme/agent-service/pull/1842",
  "task": "Summarize risk for main. List missing tests. Do not approve or merge."
}

Elimina los campos que no se utilicen. Los volcados enormes de workflows desperdician contexto e invitan a un uso confuso de herramientas. «Cargas útiles pequeñas y explícitas con una tarea clara» es la recomendación de diseño de este artículo, no una afirmación sobre una integración oficial con n8n.

Diseño de rutas: muchas puertas pequeñas

No construyas /webhooks/everything. Construye rutas con nombre, filtros y prompts:

Nombre de rutaFuenteTrabajoEntrega
gh-pr-openedPR de GitHub abiertoResumen de riesgo + tests que faltanTema de Telegram de ingeniería
stripe-disputeDisputa de Stripe creadaBorrador de lista de comprobaciónSlack de finanzas + log
support-formn8n tras validaciónClasificar + borrador de respuestaCanal privado de Slack configurado
uptime-alertWebhook de monitorizaciónReunir contexto de despliegues recientesCanal de guardia

Cada ruta debería responder:

  1. ¿Qué eventos se aceptan?
  2. ¿Cuál es la única salida esperada?
  3. ¿Qué herramientas están permitidas para el perfil de agente de esta ruta?
  4. ¿Adónde va el resultado?
  5. ¿Qué ocurre ante un fallo (¿reintento? ¿dead-letter? ¿avisar a un humano?)?

Las cargas útiles de los webhooks suelen contener correos electrónicos, identificadores de cuenta o cuerpos de mensajes. Minimiza los campos antes de que lleguen a Hermes. El esquema de las rutas no documenta un conmutador de escritura de memoria por ruta. Usa un perfil dedicado con la memoria desactivada o con memory.write_approval habilitado, y comprueba qué persiste. Si no hace falta razonamiento del agente, usa el modo documentado deliver_only en lugar de ejecutar un agente.

Controles de estado y operabilidad

Endpoint de estado documentado: http://localhost:8644/health (o tu host/puerto). Úsalo para:

  • Pruebas básicas locales tras la habilitación
  • Sondas de disponibilidad (readiness) de Docker/Kubernetes
  • Comprobaciones externas de disponibilidad contra una URL de estado privada, no contra una ruta de webhook sin autenticación

También registra:

  • Fallos de firma (posible ataque o secreto mal configurado)
  • Fallos de validación de la carga útil
  • Duración de la ejecución del agente y denegaciones de aprobación de herramientas
  • Fallos de entrega posteriores (API de chat caída, etc.)

Sin esas señales, «el agente parecía poco fiable» será tu único informe de incidente. Estos registros mejoran la observabilidad, pero no constituyen automáticamente un rastro de auditoría completo y resistente a manipulaciones.

Ejemplo: PR de GitHub abierto → ejecución enfocada

Objetivo: Cuando se abre un PR contra main, Hermes redacta una nota de riesgo para humanos. No fusiona, aprueba ni comenta salvo que más adelante añadas una vía de entrega revisada.

  1. Crea la ruta gh-pr-opened. Para una entrega directa desde GitHub, configura el secreto compartido con el que se verifica X-Hub-Signature-256; para un relay genérico de n8n, implementa en su lugar el contrato HMAC V2 con marca de tiempo.
  2. Filtra a pull_request / opened / base main.
  3. Contrato de prompt: resume el propósito, el radio de impacto, los tests que faltan, el riesgo del despliegue; marca lo desconocido; sin instrucciones de merge.
  4. Herramientas: fetch de GitHub de solo lectura si está configurado; shell deshabilitado o con aprobación requerida.
  5. Entrega: publica markdown en un canal interno; el humano decide los siguientes pasos.

Ejemplo ilustrativo de la estructura de una buena salida del agente; la redacción de tu modelo variará:

PR #1842: Añadir proceso de reintentos de facturación (ada a main)

Hechos
- Afecta al proceso de facturación y a la configuración de la cola (según el título y la lista de archivos proporcionados).
- URL vinculada: `https://github.example.invalid/acme/agent-service/pull/1842` (ejemplo)

Riesgos
- Tormentas de reintentos si falta la espera progresiva [inference; verify in diff]
- El título no menciona claves de idempotencia [unclear]

Pruebas pendientes de confirmar
- Comportamiento ante entregas duplicadas o mensajes defectuosos
- Alerta cuando se agota el presupuesto de reintentos

No fusiones cambios basándote en esta nota. Se requiere revisión humana.

Esa es una ejecución de agente impulsada por eventos con una regla de parada. No es un code owner autónomo.

Ejercicio: diseña tres rutas antes de habilitar una

En papel (o en tu runbook), escribe tres rutas de webhook para tu stack. Por cada una, rellena:

  • Nombre
  • Fuente + filtro de evento
  • Método de autenticación y responsable del secreto
  • Campos de la carga útil: empieza con 10 como máximo, como presupuesto deliberadamente pequeño del ejercicio
  • Prompt: empieza con 8 líneas como máximo y luego añade solo lo que exijan las evaluaciones de la ruta
  • Herramientas permitidas
  • Destino de entrega
  • Comportamiento ante fallo

Implementa primero solo la ruta de menor riesgo, normalmente una alerta interna o un resumen de PR solo en borrador. Ejecuta curl contra /health, después una solicitud POST de prueba autenticada y otra con autenticación no válida, y por último un evento real en un repositorio que no sea de producción o en un proyecto de staging.

Modos de fallo a esperar

  • Desajuste del secreto después de rotarlo: la autenticación falla; corrige el entorno que utiliza el proceso de la pasarela, no solo el shell del portátil.
  • Prompt demasiado amplio: el agente improvisa herramientas; divide la ruta.
  • Tormentas de reintentos: el origen repite las solicitudes POST. Hermes guarda los identificadores de entrega en caché durante una hora, pero la deduplicación efectiva exige un X-GitHub-Delivery o un X-Request-ID estable. Las acciones visibles para el cliente siguen necesitando una idempotencia de negocio duradera cuya retención se ajuste a la ventana de repetición.
  • Contaminación de la memoria: las alertas de gran volumen llegan a la memoria duradera; usa un perfil dedicado y ajustes de memoria explícitos.
  • Puerto expuesto: se puede acceder al control de estado y a los webhooks sin los controles previstos de TLS y red; corrige la red antes de añadir herramientas.

Referencias que conviene tener abiertas

Los agentes impulsados por eventos resultan útiles cuando cada ruta es limitada, está autenticada y es observable, con un destino de entrega configurado. El adaptador de webhooks es una puerta de entrada de eventos, no la API de solicitud y respuesta autenticada mediante bearer. Diséñalo y pruébalo de acuerdo con esa diferencia.

Leer a continuación

Continúa por el mismo itinerario de aprendizaje con los siguientes artículos prácticos.

Profundiza

Cursos externos seleccionados para profundizar en este tema.

Ver todos los cursos para Automatizaciones