Multiplexación de perfiles: ya es lo predeterminado


Lunes por la mañana y el grupo está en silencio. En una sola máquina corres tres perfiles de Hermes —default, coder y research— y cada uno tiene su propia unidad de systemd, su propio puerto y su propio hermes -p coder gateway start que se supone que debes recordar. El reinicio de anoche levantó solo dos de los tres, y el token de Telegram de un perfil terminó siendo consultado por dos procesos a la vez, así que cada mensaje se respondía dos veces. No cambiaste nada: simplemente tienes una cosa más que cuidar por cada bot que agregas. Hermes responde a eso con un único proceso gateway que atiende todos los perfiles de la máquina, y desde el 2026-09-16 es el comportamiento predeterminado, con una guarda para que ninguna instalación existente cambie por sorpresa.

Qué es un gateway con multiplexación

Tres palabras primero. Un perfil (profile) es un agente Hermes autocontenido: su propia configuración, credenciales, memoria, skills y tareas cron, todo bajo ~/.hermes/profiles/<name>. El gateway es el proceso permanente que mantiene tus bots conectados: consulta tu bot de Telegram, responde a tu webhook, despierta las tareas cron y entrega cada mensaje entrante a un turn del agente. Multiplexación (multiplexing) significa dejar que un solo proceso gateway atienda más de un perfil.

Hasta ahora la forma estándar era un proceso por perfil (lo que la documentación llama standalone, y lo que restaura --standalone): cada perfil tiene su unidad, su puerto y su archivo PID. El multiplexor reemplaza esa flota por un único proceso: el gateway del perfil default pasa a ser el único proceso de entrada y atiende los mensajes de todos los perfiles de la máquina, enrutando cada mensaje al perfil al que pertenece. Los perfiles siguen siendo agentes separados; lo único que se comparte es el proceso.

La forma de un proceso por perfil sigue siendo totalmente compatible, y la guía de perfiles multi-instancia recorre cómo conviven varios perfiles en la misma máquina.

Qué cambió: activado por defecto, pero decidido en cada arranque

gateway.multiplex_profiles ahora viene activado. La parte que conviene leer dos veces es qué pasa si nunca lo configuraste: un flag sin definir se trata como una petición, no como un veredicto, y el gateway predeterminado resuelve esa petición una vez por arranque.

Al iniciarse ejecuta el mismo preflight que hermes gateway migrate --multiplex, y solo multiplexa cuando el traslado habría sido seguro:

  • el perfil default existe y hay dos o más perfiles en la máquina;
  • ningún perfil secundario sigue ejecutando su propio gateway, ni como proceso vivo ni como servicio instalado;
  • ningún par de perfiles comparte una credencial de bot;
  • ninguna plataforma que ocupa un puerto se queda sin su ingress /p/<profile>/;
  • la máquina es de un tipo que la migración entiende: no un contenedor s6, no Windows Scheduled Tasks.

Si algo de eso falla, el gateway arranca exactamente como siempre, sirviendo solo el perfil default, y deja un WARNING que nombra el bloqueo y te da la línea hermes gateway migrate --multiplex. Nada se modifica en disco.

Un valor explícito nunca se pone en duda:

  • gateway.multiplex_profiles: true (lo que escribe la migración) multiplexa sin importar el preflight: la decisión la tomaste tú o la migración;
  • gateway.multiplex_profiles: false (lo que restaura --standalone) mantiene un gateway por perfil de forma permanente; con el valor en false, todo lo que sigue queda inerte;
  • GATEWAY_MULTIPLEX_PROFILES en el entorno del proceso tiene prioridad sobre ambos.

El veredicto se registra, no solo se registra en el log: el gateway_state.json del home predeterminado guarda multiplex_standalone_reason, y hermes gateway status lo imprime como ⚠ Serving the default profile only (gateway.multiplex_profiles unset): <reason> junto con las dos soluciones posibles. Un arranque que sí multiplexa lo borra.

Los demás procesos tampoco adivinan el veredicto a partir del valor predeterminado fusionado: hermes -p coder setup, el dashboard, la resolución de puertos del disparo de cron y hermes gateway migrate leen primero el registro served_profiles del gateway en ejecución, y solo recurren al flag explícito cuando no hay ningún gateway corriendo.

Cómo saber en qué modo estás

Un comando lo responde:

hermes gateway status
  • Si aparece una línea que empieza con Serving the default profile only, estás en modo standalone, y el texto después de los dos puntos es la razón por la que la guarda de arranque se contuvo.
  • Si esa línea no aparece, el gateway corre como multiplexor. Su propio registro —served_profiles dentro del gateway_state.json del home predeterminado— enumera cada perfil que sirve, y ese registro es la fuente que consultan las demás herramientas.

En el dashboard la misma información está en la página System, y a nivel de API /api/status?profile=coder la entrega como gateway_shared_with (null cuando ese perfil tiene gateway propio).

También puedes comprobarlo de forma empírica: intenta arrancar el gateway de un perfil secundario. En modo multiplexor la CLI lo rechaza; en modo standalone arranca.

¿Un gateway o un proceso por perfil?

La multiplexación conviene cuando:

  • despliegas en un contenedor o un VPS donde N unidades de supervisor, N puertos y N archivos PID son una carga;
  • tienes muchos perfiles de poco tráfico que por sí solos no justifican un proceso completo;
  • quieres una sola cosa que arrancar, vigilar y reiniciar.

Agregar un perfil pasa entonces a costar un token de bot —el gateway en ejecución lo incorpora— en lugar de otro servicio que cuidar. Nuestra guía sobre servir perfiles en un gateway en marcha cubre esa vía en caliente.

Quédate con un proceso por perfil cuando necesites aislamiento duro a nivel de proceso entre perfiles: huellas de memoria separadas, dominios de fallo independientes y la posibilidad de reiniciar un perfil sin tocar los demás. Cuando una skill descontrolada en un perfil podría arrastrar todo el proceso, eso pesa más que ahorrarse unas cuantas unidades.

Plegar la flota de forma deliberada

En lugar de reiniciar el gateway predeterminado y esperar que la guarda de arranque esté de acuerdo, ejecuta la migración:

hermes gateway migrate --multiplex

En orden: escribe gateway.multiplex_profiles: true en el perfil default, detiene y desinstala todas las unidades instaladas de cada secundario, levanta el gateway predeterminado como multiplexor y confirma a partir del registro served_profiles en vivo de ese gateway que cada perfil migrado está siendo servido de verdad.

La migración es transaccional. El flag se escribe primero, así que una interrupción en cualquier punto posterior se puede reanudar: volver a ejecutar el comando lee el manifest que la migración escribió y continúa desde el estado en vivo —el served_profiles del gateway predeterminado en vivo debe cubrir cada perfil migrado en el manifest antes de darse por terminada—. Si falla un paso posterior (detener un secundario, un daemon-reload tras desenlazar una unidad, la escritura de la configuración), los secundarios ya retirados se reinstalan y el flag vuelve atrás. Un rollback que no logra completarse conserva el manifest y lo dice, para que puedas terminar a mano.

También se niega antes de tiempo, sin detener ningún gateway que esté funcionando, cuando el plan ya muestra que el traslado no puede funcionar: una flota de unidades de sistema de root sin un User= registrado que reproducir, un usuario registrado que el sistema no puede resolver, o un config.yaml que no se puede escribir. El User= de una unidad de sistema se conserva: la migración lo lee antes de retirar nada y lo lleva consigo al arranque del gateway predeterminado y al rollback.

Si empiezas de cero en lugar de migrar, es más simple: instala Hermes, deja que arranque el gateway predeterminado y el preflight de arranque hace el resto.

Qué se comporta distinto cuando está activado

El multiplexor es el único proceso de entrada, así que un segundo gateway para el mismo perfil duplicaría el binding de sus plataformas. Todo lo que sigue es automático y queda inerte en cuanto el flag está en false.

Un perfil secundario no puede arrancar su propio gateway. hermes -p coder gateway run, start, install y restart son un error duro con código de salida 78, que te devuelve al multiplexor: “The default gateway is running as a profile multiplexer and already serves profile ‘coder’.” El rechazo ocurre en la CLI antes de tocar cualquier gestor de servicios, así que un perfil servido nunca termina con una unidad systemd permanentemente fallida ni con un bucle de respawn de launchd. hermes -p coder gateway stop rechaza igual (salida 78) cuando coder no tiene gateway propio: no hay nada que detener salvo el multiplexor, y hermes gateway stop en el perfil default lo baja para todos los perfiles servidos.

El dashboard y el escritorio siguen a la CLI. Para un perfil servido, Start y Stop responden 409 con la misma explicación, mostrada como aviso en línea en la página System, y Restart reinicia el multiplexor —el proceso que en realidad sirve el perfil— en lugar de lanzar un -p coder restart que solo podría fallar. Como esa reconexión afecta a todos los bots del equipo, ambas apps preguntan antes: “Restart the shared gateway? All bots on this device reconnect: default, coder, research” —la lista es el served_profiles del gateway en ejecución— y luego informan “Shared gateway restarted (3 bots)”. Un perfil standalone conserva el reinicio normal. A nivel de API, /api/status?profile=coder devuelve esa misma lista como gateway_shared_with (null si el gateway es standalone).

El setup se salta el servicio en segundo plano. hermes -p coder setup gateway, hermes -p coder setup, hermes -p coder gateway setup y hermes -p coder import siguen configurando los bots de ese perfil, pero se saltan el paso “instalar el servicio en segundo plano del gateway” y en su lugar imprimen “Profile ‘coder’ is already served by the default multiplexer”, en vez de registrar una unidad o un plist que solo quedaría muerto. Agrega el token del bot y el multiplexor en ejecución lo incorpora.

Las plataformas que ocupan un puerto pasan a un prefijo de URL. Una plataforma que escucha en un puerto se sirve en el listener del gateway predeterminado bajo /p/<profile>/, así que un webhook que antes apuntaba al puerto propio de ese perfil ahora apunta al listener compartido más ese prefijo, con una forma como /p/<profile>/webhooks/.... El preflight de arranque comprueba exactamente eso: que ninguna plataforma que ocupa un puerto se quede sin su ingress /p/<profile>/.

Los tokens siguen siendo por perfil y las credenciales nunca se comparten. Cada perfil servido levanta sus plataformas con sus propias credenciales de bot, y cada turn resuelve la configuración, las skills, la memoria, el SOUL y las claves de provider del perfil enrutado. Un perfil servido tampoco hereda las credenciales del perfil de lanzamiento, con el flag activado o no: a un proceso hijo enrutado a otro home se le limpian las credenciales Tier-1/Tier-2 del perfil de lanzamiento antes de superponer su propio ámbito, y una lectura de credenciales sin ámbito para un secundario lanza un error en lugar de devolver en silencio el valor del perfil predeterminado. Dale a cada perfil su propio token de bot: dos perfiles compartiendo uno es justo lo que el preflight rechaza.

--force sigue existiendo. run, start, install y restart aceptan --force si deliberadamente quieres un proceso aparte para un perfil, algo que no se recomienda mientras el multiplexor está en marcha. Y el script envoltorio de ciclo de vida entre perfiles de las instalaciones standalone no se usa en modo multiplexor: solo administras el gateway predeterminado.

Desactivarlo de forma duradera o forzarlo

Configura el flag en el perfil default —es quien posee el multiplexor— y reinicia su gateway:

hermes config set gateway.multiplex_profiles false
hermes gateway restart

De forma equivalente, en el ~/.hermes/config.yaml del perfil default:

gateway:
  multiplex_profiles: false

(El flag también se acepta como multiplex_profiles: true en el nivel superior, por comodidad.)

false mantiene un gateway por perfil de forma permanente: un false en disco se respeta como un rollback deliberado y todos los comportamientos descritos arriba quedan inertes. true fuerza la multiplexación incluso donde el preflight de arranque se habría contenido, porque la decisión es tuya. GATEWAY_MULTIPLEX_PROFILES en el entorno del gateway predeterminado tiene prioridad sobre ambos, que es la palanca cómoda en un contenedor donde controlas el entorno y no el archivo.

Hay una palanca más en la vía desatendida: hermes update puede plegar la flota al gateway predeterminado por su cuenta, y la clave que lo gobierna es gateway.auto_multiplex_migration; hermes config set gateway.auto_multiplex_migration false deja explícita esa renuncia. Un false del ámbito gestionado gana a un true del usuario, y el update se niega a plegar una flota hacia una unidad de sistema predeterminada cuyo User= no puede resolver.

Diagnóstico y resumen

  • hermes gateway status dice que un secundario aún ejecuta su propio gateway. Deténlo y desinstálalo —un PID vivo o una unidad instalada es lo que la guarda espera— y vuelve a ejecutar hermes gateway migrate --multiplex.
  • Dos perfiles comparten una credencial de bot. Dale a uno su propio token; el preflight no va a arrebatar tokens que otro proceso todavía retiene.
  • Una plataforma que ocupa un puerto no tiene ingress /p/<profile>/. Apunta su webhook al listener predeterminado con ese prefijo, o quédate en standalone.
  • En un contenedor s6 o en Windows Scheduled Tasks no se pliega nada. Esa máquina no es de un tipo que la migración entienda, así que la guarda de arranque no puede plegarla por ti; mantén un gateway por perfil allí.
  • Obtienes el código de salida 78 al arrancar un secundario. Eso es el contrato, no un bug: administra el gateway predeterminado con hermes gateway restart y recurre a --force solo si de verdad quieres un proceso aparte.
  • Una migración murió a medias. Vuelve a ejecutar hermes gateway migrate --multiplex; se reanuda desde el manifest que escribió, y un rollback incompleto conserva el manifest y te dice qué falta.

La versión corta: la multiplexación está activada por defecto, pero en una máquina que no está lista no cambia nada —un flag sin definir se decide en cada arranque, el veredicto queda registrado y se muestra en hermes gateway status, y un true o false explícito siempre gana—. Si corres varios bots en una misma máquina, hermes gateway status es ahora el único comando que te dice en qué mundo estás. La lista completa de flags está en la referencia del comando hermes gateway.