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:
- Habilita la plataforma de webhooks (
hermes gateway setupo env comoWEBHOOK_ENABLED=true). - 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.
- Crea una ruta con nombre en la config o vía
hermes webhook subscribe(comando según la documentación actual). - Comprobación de estado:
curl http://localhost:8644/health - Apunta el sistema externo a
https://your-host/webhooks/<name> - 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 subscribese 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_AUTHsolo 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, como0.0.0.0o 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 ruta | Fuente | Trabajo | Entrega |
|---|---|---|---|
gh-pr-opened | PR de GitHub abierto | Resumen de riesgo + tests que faltan | Tema de Telegram de ingeniería |
stripe-dispute | Disputa de Stripe creada | Borrador de lista de comprobación | Slack de finanzas + log |
support-form | n8n tras validación | Clasificar + borrador de respuesta | Canal privado de Slack configurado |
uptime-alert | Webhook de monitorización | Reunir contexto de despliegues recientes | Canal de guardia |
Cada ruta debería responder:
- ¿Qué eventos se aceptan?
- ¿Cuál es la única salida esperada?
- ¿Qué herramientas están permitidas para el perfil de agente de esta ruta?
- ¿Adónde va el resultado?
- ¿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_approvalhabilitado, y comprueba qué persiste. Si no hace falta razonamiento del agente, usa el modo documentadodeliver_onlyen 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.
- Crea la ruta
gh-pr-opened. Para una entrega directa desde GitHub, configura el secreto compartido con el que se verificaX-Hub-Signature-256; para un relay genérico de n8n, implementa en su lugar el contrato HMAC V2 con marca de tiempo. - Filtra a
pull_request/opened/ basemain. - 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.
- Herramientas: fetch de GitHub de solo lectura si está configurado; shell deshabilitado o con aprobación requerida.
- 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-Deliveryo unX-Request-IDestable. 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
- Adaptador de webhooks de Hermes
- Servidor API de Hermes
- Modelo de seguridad de Hermes
- Documentación de Hermes
- Interno: /articles/first-ai-agent-in-n8n, /articles/hermes-vs-n8n-choose-by-job
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.



