DisPatch: Una aplicación de chat autohospedada para agentes de IA locales
- Categoría
- IA y LLM Local
- Publicado
- 11 julio 2026
- Actualizado
- 9 octubre 2026
- Por
- Jacob Lloyd — escrito con ayuda de IA, después del proyecto
- Tiempo de lectura
- 18 min de lectura
En palabras simples: DisPatch es una aplicación de chat que se ejecuta en su propio ordenador y funciona desde cualquier teléfono o portátil de la casa. Puede utilizarla para hablar con asistentes de IA que también se ejecutan en ese ordenador, así como para transferir archivos a él para que los asistentes puedan trabajar con ellos. Un PIN mantiene bloqueados a los potentes asistentes; por eso, en un dispositivo compartido por toda la familia solo se accede a un conjunto seguro de funciones.
DisPatch es la aplicación que creé para dejar de tener que ejecutar una instalación de 30 contenedores de Rocket.Chat con el único fin de enviar mensajes a mis propios agentes de IA. Se trata de un único proceso en Python y un archivo SQLite. Cualquier dispositivo en la casa puede acceder a él; los agentes responden en hilos de conversación, tal como lo harían las personas, y cualquier archivo que envíe desde mi teléfono llega al equipo donde residen dichos agentes. La versión 2.0.0 se lanzó el 09-10-2026: el archivo de descarga es un zip de 3 MB cuya propia suite de pruebas ejecuta 1,915 pruebas, todas exitosas. Esta página explica qué hace la aplicación, cómo las respuestas llegan a su teléfono exactamente una vez, y cómo el bloqueo mediante PIN impide que un tablet familiar acceda a los agentes capaces de realizar cambios reales.
Resumen rápido
- ¿Qué es?: un chat web autoalojado para agentes de IA locales (un pasarela OpenClaw, o una API de modelo a la que se apunte), además de una función de arrastrar y soltar archivos; puede usarse desde cualquier dispositivo que tenga acceso al host.
- ¿Cuánto cuesta?: $0. No hay costos por licencias SaaS ni se requiere cuenta alguna.
- ¿Qué se necesita?: Linux con Python 3.12+ y
uv, o Docker. OpenClaw solo si desea agentes que utilicen herramientas externas. - ¿Qué incluye?: hilos de conversación por cada bot, respuestas en tiempo real con botón para detenerlas, opciones para regenerar y editar mensajes, soporte para arrastrar archivos, herramientas y aplicaciones integrables, diez temas visuales, un Modo Seguro impuesto por el servidor y protegido mediante PIN, y una API de notificaciones para tareas programadas.
- Descarga: la versión 2.0.0, idéntica al código marcado como
v2.0.0en el repositorio público. Licencia MIT.
¿Qué hace?
Las capturas de pantalla provienen de una copia en entorno aislado con datos ficticios. Las cuatro primeras son de la versión 2.0; las imágenes del Modo Seguro y del Servidor de Archivos corresponden a la compilación de julio, y funcionan igual en la versión 2.0.





- Hilos por bot en todos los dispositivos. Abra la conversación en su teléfono y verá el mismo hilo con todo el historial. Los hilos se pueden fijar, archivar, renombrar y buscar.
- Respuestas en markdown en tiempo real, con resaltado de sintaxis para código, botones de copiado, vistas previas de imágenes y videos mediante lightbox, además de una PWA instalable.
- Panel de ejecución en vivo y botón Detener. Una línea de estado indica qué está haciendo el agente (cargar el modelo, escribir, usar alguna herramienta). Haga clic en los puntos de escritura para ver su proceso de razonamiento y llamadas a herramientas; también puede pulsar Detener para interrumpir la ejecución.
- Controles habituales de aplicaciones de chat. La opción Regenerar conserva las respuestas anteriores; puede editar un mensaje y volver a ejecutarlo, citar una respuesta, votar positiva o negativamente, o eliminar un mensaje del contexto. Cada hilo permite modificar el modelo y el nivel de razonamiento; además, hay un medidor que indica cuánto espacio queda en el contexto.
- Borradores y bandeja de salida sin conexión. Los borradores no enviados permanecen vinculados a su hilo; los mensajes enviados sin conexión se guardan y se transmiten cuando vuelva la conexión.
- Herramientas y aplicaciones. Un botón Herramientas abre sus propios proyectos dentro de la app: una carpeta estática, una página web o un paquete de aplicación de confianza con su propio backend y bot. El Tablero de Trabajos es la primera aplicación disponible.
- Dos formas de conectar la IA. Puede asignar directamente un bot a una API de modelo (LM Studio, Ollama, OpenAI, Anthropic o cualquier servicio compatible con OpenAI) desde el panel de configuración; también puede conectar un gateway OpenClaw para agentes que usan herramientas y actúan sobre el equipo.
- API de inserción de mensajes.
POST /api/injectpermite que un trabajo programado u otro agente introduzca un mensaje en un hilo. Así recibo informes matutinos en mi teléfono sin tener que solicitarlos. - Bloqueo de dos niveles. Un PIN separa a los agentes con plenos poderes del subconjunto “seguro”. Más detalles a continuación.
- Panel de control del host. Muestra el estado de salud, almacenamiento, integridad de la base de datos y copias de seguridad; también incluye una lista de posibles configuraciones erróneas.
- Ocho idiomas de interfaz, incluido el árabe de derecha a izquierda, y diez temas.
- Visor local de archivos. Al tocar una ruta de archivo en un mensaje, este se abre dentro de la app, incluso desde el teléfono. Solo sirve archivos si se indica una carpeta específica; nunca expone datos confidenciales ni rutas del sistema.
Los agentes también pueden publicar imágenes en un hilo. Un bot escribe un marcador [[pic:prompt|caption]] en su respuesta; de inmediato aparece un espacio reservado que luego se transforma en la imagen una vez generada. Mis imágenes provienen de una máquina con GPU ubicada en otra parte de la red. Si esa máquina no está disponible, el espacio reservado lo indica en lugar de fallar silenciosamente.
El Servidor de Archivos
Este es el componente que más uso. Abro DisPatch en cualquier dispositivo, arrastro un archivo hacia él y el archivo queda en el servidor, agrupado por día; además, hay miniaturas para imágenes y videos.

Lo valioso aquí es el destino final de los archivos: la misma máquina donde se ejecutan los agentes. Una foto subida desde mi teléfono puede ser leída por un agente segundos después; así puedo describirla, archivarla o publicarla en un sitio web, sin necesidad de un servicio en la nube ni cables.
El código previene los modos de fallo más comunes:
- El tamaño máximo permitido para la carga se verifica antes de que FastAPI almacene el archivo, evitando así que un teléfono agote la memoria del servidor.
- El espacio de almacenamiento total está limitado a 20 GB por defecto (
DISPATCH_FILES_TOTAL_MAX; si su valor es 0, esta restricción se desactiva). - Cada archivo se escribe primero en un archivo temporal, luego se sincroniza y finalmente se renombra para quedar en su ubicación definitiva; de este modo, una conexión interrumpida nunca deja un archivo incompleto.
- Un dispositivo bloqueado solo permite enviar archivos. No puede navegar ni descargar nada que se encuentre en el Servidor de Archivos.
¿Por qué lo creé en lugar de usar una interfaz de usuario existente?
Rocket.Chat funcionaba, pero una base de datos y un montón de contenedores representan mucha infraestructura para algo tan simple como “enviar texto, recibir texto de vuelta y sincronizarlo en todos lados”.
Open WebUI y LibreChat son las opciones habituales para implementar un chat de IA autoalojado; además, son buenos para interactuar con modelos. Mis necesidades, sin embargo, eran distintas. Quería que los agentes participaran en los hilos de conversación compartidos por toda la casa, que el servidor impusiera restricciones de acceso para los dispositivos que usan los niños, que hubiera un endpoint HTTP al cual los scripts pudieran enviar datos, y que los archivos quedaran en lugares donde los agentes pudieran leerlos. Esa combinación es lo que ofrece DisPatch. Si solo busca una interfaz agradable para interactuar con un modelo, le recomiendo empezar con alguno de esos otros proyectos.
Además, no requiere ningún paso de compilación. El frontend está compuesto por módulos JS puros y CSS/HTML sencillo; no se necesita ningún empaquetador ni ejecutar npm run build. Mi requisito inicial era que “fuera fácil de modificar posteriormente por un agente de IA”: editar un archivo, actualizar la página y listo. Volvería a elegir este enfoque sin dudarlo.
¿Qué contiene la descarga?
El archivo zip es dispatch-2026-10.zip, la versión DisPatch 2.0.0; bit a bit coincide con la etiqueta v2.0.0 del repositorio público. Lo medí para esta revisión:
| Elemento | En el zip |
|---|---|
| Tamaño de la descarga | 2,992,647 bytes (2.9 MB); 319 archivos; 7.9 MB al descomprimirse |
| SHA-256 | e9824d3c13bb3acd2d2eebcf2b162b14ae26eb86f56ca0e4ac8aa20f686a2398 |
| Backend | Python 3.12+, FastAPI + uvicorn; un archivo SQLite, gestionado mediante uv. Hay 36,501 líneas en backend/app/ (main.py contiene 12,331 líneas); además, 5,497 líneas corresponden a la aplicación Job Board incluida. |
| Pruebas | 30,726 líneas de pruebas en Python; pytest informa de 1,915 pruebas superadas; también hay 473 pruebas del frontend ejecutadas mediante node --test. |
| Frontend | JavaScript puro sin necesidad de compilación: unas 21,000 líneas fuera de las bibliotecas y pruebas incluidas; además, aproximadamente 9,300 líneas de CSS y HTML. |
| Bibliotecas incluidas | marked, highlight.js, DOMPurify. No se realizan llamadas a CDN. |
| Datos | Por defecto, fuera del código, en ~/.local/share/local-chat/ (el nombre antiguo del proyecto): la base de datos, config.yaml, security.yaml, archivos multimedia y copias de seguridad rotativas de la base de datos. |
| Licencia | MIT |
¿Qué ha cambiado desde el zip 1.0.0 de agosto?
- Las respuestas siguen un camino más rápido y no se pierden. Ahora los mensajes se envían al gateway del agente a través del WebSocket ya abierto, en lugar de iniciar un nuevo proceso
openclawpor cada mensaje. Eso redujo aproximadamente un segundo el tiempo de respuesta (de 2.26 s a 1.12 s de sobrecarga fija, según mediciones). El texto llega a medida que el modelo lo genera. Cada solicitud aceptada por el gateway se registra hasta que se resuelve; por eso una respuesta llega incluso tras una conexión interrumpida o un reinicio del servidor. - Licencia. La versión 1.0.0 usaba AGPL-3.0. El proyecto fue relicenciado bajo MIT el 30 de agosto de 2026; por eso la versión 2.0.0 está bajo esa licencia. Los archivos zip anteriores, de julio y agosto, siguen disponibles en sus direcciones originales, cada uno con su propio archivo
LICENSE. - El terminal de programación ha desaparecido. La versión 1.0.0 incluía un terminal opcional dentro del navegador (un PTY del servidor transmitido a xterm.js). Lo utilizaba para un agente de programación que ya he retirado; por eso en la versión 2.0 se eliminó junto con su biblioteca asociada. El panel DeepSeek Harness, activo cuando se instala
dsh, cumple esa función y ahora permite iniciar, supervisar y detener sesiones en tiempo real. - Novedades: controles de conversación, borradores y bandeja de envío, herramientas y aplicaciones, tablas de checklist, visor local, marcadores de imágenes, diez temas en lugar del simple cambio entre modo claro/oscuro, y filas de mensajes más discretas. La lista completa se encuentra en el archivo
CHANGELOG.mddel repositorio.
Configuración
Esto es un resumen extraído del README.md y de la carpeta docs/ dentro del archivo zip. Se trata de un proyecto normal de uv:
unzip dispatch-2026-10.zip
cd DisPatch-2.0.0/backend
uv sync --frozen
uv run pytest -q # 1,915 passed on my machine
uv run uvicorn app.main:app --host 0.0.0.0 --port 8765
O bien, puede omitirse Python: ejecute cp .env.example .env && docker compose up -d desde la raíz del archivo descomprimido. En la carpeta deploy/systemd/ encontrará los archivos de unidad para la versión que se ejecuta continuamente; además, en docs/deploy-bare-metal.md se explica el paso que todos suelen olvidar: loginctl enable-linger. De lo contrario, el servicio de usuario dejará de funcionar al cerrar la sesión SSH.
- Primero, establezca un PIN (icono de engranaje, luego “Seguridad”). Ambos comandos mencionados escuchan en
0.0.0.0; por lo tanto, hasta que se defina un PIN, cualquier persona en su red tendrá acceso total. Utilice--host 127.0.0.1(oBIND_ADDR=127.0.0.1en Docker) mientras explora el sistema, y no reenvíe el puerto a Internet. - Edite el archivo
config.yamlen el directorio de datos para que losidde los bots coincidan con los identificadores de sus agentes de OpenClaw. El archivo se vuelve a leer automáticamente cuando se modifica. - La configuración proviene de variables de entorno cuyo prefijo es
DISPATCH_; todas ellas se detallan en.env.example:HOST,PORT,DATA_DIR,AGENT_TIMEOUT(900 s),MAX_CONCURRENCY(3) yGATEWAY_WS. DISPATCH_MAX_CONCURRENCYlimita cuántas ejecuciones de agentes pueden ocurrir simultáneamente. El valor predeterminado, 3, protege una máquina que ejecute modelos locales. Un cuarto mensaje simplemente esperará su turno; no se descarta.
DisPatch no incluye OpenClaw por defecto. Para los agentes, espera que haya un pasarela de OpenClaw en el mismo host. Aun así, la aplicación se inicia y registra una advertencia si no existe dicha pasarela. Los hilos, la API de inyección, el servidor de archivos, el bloqueo mediante PIN, las herramientas y las APIs directas de modelos siguen funcionando sin problemas. Así es como realizo las pruebas preliminares al descomprimir el paquete: sin pasarela, en un puerto libre y tras ejecutar uv sync.
Cómo llega la respuesta hasta usted
La mayor parte del trabajo de ingeniería se centró en los casos de fallo: que la conexión se interrumpa a mitad de la respuesta, que el servidor se reinicie con una ejecución en curso, o que un subagente responda diez minutos después. El método de transporte se elige mediante DISPATCH_GATEWAY_WS. Si esta variable no está definida, cada mensaje inicia un proceso de openclaw agent, el cual devuelve la respuesta al finalizar. shadow se conecta y registra lo que entregaría sin realmente hacerlo. 1 indica que los mensajes se envían por el socket de la puerta de enlace y las respuestas regresan por él; yo utilizo 1. El archivo zip incluye esta opción desactivada, por lo que nada cambia en la forma en que llegan las respuestas hasta que se active.
Con el socket activado, toda ejecución aceptada por la puerta de enlace se registra en una tabla de procesos en curso y se finaliza mediante la llamada “esperar a que termine esta ejecución” propia de la puerta de enlace. Si la transmisión en tiempo real se interrumpe, la respuesta llega igualmente cuando la ejecución concluye; además, las ejecuciones que estaban en curso cuando el servidor se apagó se recuperan al reiniciarse. Solo se vuelve a intentar una ejecución que la puerta de enlace nunca llegó a aceptar, por lo que no se envía nada dos veces.
Una advertencia respecto al respaldo: el barrido de transcripciones lee los archivos de sesión de OpenClaw; sin embargo, las versiones más recientes de OpenClaw guardan las sesiones en una base de datos. En esos casos, DisPatch registra al iniciarse que los mecanismos de respaldo basados en archivos no pueden ejecutarse, por lo que el socket se convierte en la única vía de entrega. Ese es otro motivo para activar el socket.
Sea cual sea el método que lleve primero la respuesta, todos los posibles mensajes pasan por un mismo canal que verifica si ya han sido entregados; de este modo, una respuesta detectada dos veces aparece solo una vez. Incluso ante un error en la CLI, el barrido se ejecuta antes de darse por vencido, ya que con frecuencia el modelo ya ha escrito su respuesta en la transcripción antes de que el proceso se detenga.
El bloqueo de dos niveles: modo normal vs. Modo Seguro
El lado desbloqueado no es ningún juguete: los agentes que operan allí pueden editar código, enviar cambios a mi sitio web en producción, ejecutar herramientas y gestionar la máquina en la que se encuentran. El lado bloqueado, en cambio, sirve únicamente para chat general; sigue siendo útil para hacer preguntas o pedir ayuda, pero no permite acceso a nada más. Como el teléfono familiar no siempre está en manos de adultos, el servidor impone este bloqueo. A continuación se explica qué ocurre con una solicitud:
La opción de recordar dispositivos permanece desactivada hasta que se habilita en la configuración de Seguridad; al hacerlo, el período de confianza se establece en 30 días. Al pulsar “Bloquear” en un dispositivo recordado, este queda anulado como de confianza; asimismo, cambiar el PIN anula todos los dispositivos registrados. El servidor guarda como máximo 10 dispositivos recordados. Si el archivo de dispositivos de confianza se daña, ningún dispositivo será considerado seguro, aunque el PIN seguirá funcionando.
Si por error queda bloqueado fuera del sistema, existen dos formas de volver a acceder: mediante un archivo de códigos de recuperación o editando directamente el archivo security.yaml. Si security.yaml está dañado, la aplicación permanece bloqueada en lugar de permitir el acceso a cualquiera en la red local. Esa fue una decisión deliberada.
Posibles problemas
- El campo de la API de inyección es
content.POST /api/injectespera{"bot_id": "...", "content": "..."}. Enviartextfue el error de integración más común, incluido el mío. - No pases mensajes largos como argumentos de la CLI (esto sigue siendo importante incluso en el modo de respaldo). Linux limita el tamaño de un único argumento a 128 KB (
MAX_ARG_STRLEN); un mensaje muy largo provocará un error E2BIG al ejecutar el comando. El código escribe el mensaje en un archivo temporal y luego pasa--message-file. No “simplifiques” esto convirtiéndolo nuevamente en un argumento. - Las claves de sesión deben estar en minúsculas; los IDs de bots no. OpenClaw convierte las claves de sesión a minúsculas, pero tus IDs pueden tener mayúsculas y minúsculas (
Alpha,My_Bot). Si la búsqueda es sensible a mayúsculas/minúsculas, el panel de control y la entrega de respuestas se detendrán silenciosamente, aunque todo lo demás funcione. La solución es realizar una coincidencia exacta primero, y si no hay resultados, buscar en minúsculas. - La CLI solo devuelve el último bloque de texto. Sin conexión por socket, todo lo que diga el agente entre distintas llamadas a herramientas solo queda registrado en su transcripción. Por eso existe el proceso de limpieza de transcripciones; este sigue ejecutándose incluso cuando la conexión por socket está activa.
- Verifica el campo Origin en WebSockets. El middleware HTTP no se ejecuta para rutas WebSocket. DisPatch realiza la autenticación directamente y rechaza cualquier intento de conexión cuyo host en
Originno coincida conHost; esto evita ataques de secuestro de WebSocket entre sitios. Los clientes que no sean navegadores no envían el campo Origin y, por tanto, pasan la validación. - Nunca sirvas archivos SVG subidos como imágenes. Un archivo SVG abierto directamente puede ejecutar scripts en tu origen, lo cual constituye una vulnerabilidad de tipo XSS almacenado. DisPatch rechaza los archivos SVG como contenido multimedia y envía dichos archivos con políticas CSP que incluyen restricciones de ejecución y el atributo
nosniff. - Las actualizaciones del service worker afectan a las pestañas antiguas que usan una versión anterior de JavaScript. El comportamiento parece un regreso en la funcionalidad. La solución es realizar una recarga automática al detectarse el evento
controllerchange.
Replicando la idea
Incluso si nunca ejecutas mi archivo zip, la arquitectura consta de cuatro componentes intercambiables:
- Un backend de chat que gestiona el historial: un pequeño servidor web (aquí FastAPI y un archivo SQLite en modo WAL), un WebSocket para actualizaciones en tiempo real, y un endpoint HTTP sencillo para enviar mensajes. Esto reemplaza a la base de datos y a todos los contenedores necesarios.
- Una puerta de enlace de agentes con la que se comunica el backend: aquí se usa OpenClaw, pero cualquier sistema que acepte un ID de sesión y un mensaje, y devuelva texto, sirve. Al ser un proceso independiente, puede fallar, actualizarse o ser reemplazado sin que la aplicación de chat se detenga.
- Servidores de modelos detrás de la puerta de enlace (LM Studio, vLLM, Ollama). Son responsabilidad de la puerta de enlace, no de la aplicación de chat.
- Servicios multimedia opcionales, como un generador de imágenes en otra máquina, que los agentes pueden invocar como herramientas. El backend simplemente almacena lo que recibe.
Estas lecciones son aplicables independientemente de cuál sea tu stack tecnológico:
- Activa el modo bloqueado en el servidor, nunca mediante CSS.
- Considera que “el mensaje llega al usuario exactamente una vez” como un problema de entrega que debes resolver mediante deduplicación.
- Mantén el frontend sin necesidad de compilación si agentes de IA lo van a mantener.
Relacionado: mi stack de agentes de IA local con OpenClaw · Primer vistazo al DeepSeek Harness · gestionando mis sitios web con una caja de agentes de IA local · configurando tu asistente LLM
Descargas
Gratis para uso personal. Si te ahorra una tarde, el botón del café está aquí cerca.