Despliegue de red
Desplegar ZeroClaw para que pueda recibir tráfico entrante: exposición del gateway, canales de webhook, túneles y configuraciones solo para LAN frente a configuraciones públicas. Las Raspberry Pi y otros hosts de la red doméstica son objetivos principales aquí.
Cuando importan los puertos de entrada
| Modo | ¿Puerto de entrada? | Notas |
|---|---|---|
| Telegram (polling largo) | No | ZeroClaw consulta api.telegram.org, funciona detrás de NAT |
| Matrix / Mattermost / Nextcloud Talk | No | Sync/WebSocket, solo saliente |
| Discord / Slack (Modo de socket) | No | WebSocket de salida |
Signal (signal-cli-rest-api) | No | Contenedor de localhost |
| Nostr / IMAP / MQTT | No | Todos los salientes |
| Webhooks (GitHub, Slack Events API, WhatsApp, bot de Nextcloud Talk, personalizado) | Sí | Se requiere un punto de conexión POST público |
| Emparejamiento de puerta de enlace desde LAN | Sí (alcance de LAN) | Vincular a 0.0.0.0 o usar un túnel |
| Discord / Slack (Eventos HTTP) | Sí | Si no usas el Modo de Sockets |
Conclusión: un bot exclusivo de Telegram se ejecuta en una Pi detrás de un router doméstico sin ningún reenvío de puertos. Cualquier cosa basada en webhooks necesita una URL accesible, que es donde entran en juego los túneles.
Vinculando la puerta de enlace
De forma predeterminada, el gateway se vincula a 127.0.0.1, inaccesible desde otros dispositivos. Tres opciones para exponerlo:
Opción 1: Vinculación pública (LAN)
Entonces cualquier dispositivo en la LAN puede acceder a http://<pi-ip>:42617. No sirve para webhooks accesibles desde internet, la IP pública de tu router no está redirigida a la Pi.
Seguridad: Es necesario establecer allow_public_bind = true porque vincularse a 0.0.0.0 representa un cambio significativo en la configuración de seguridad. Sin esta opción, el demonio se niega a ejecutarse. Esto es intencional.
Opción 2: Túnel (accesible desde internet)
Luego reinicia el daemon; el túnel se gestiona de forma declarativa desde la configuración, iniciándose junto con el gateway.
El túnel reenvía desde una URL pública hacia el gateway en 127.0.0.1. Sin configuración del router, sin puertos abiertos. Establece tunnel.tunnel_provider en uno de los valores compatibles; cada uno funciona de manera similar:
| Proveedor | Configurar fricción | Costo | Bueno para |
|---|---|---|---|
tailscale | Cuenta + cliente | Capa gratuita | URLs estables a largo plazo |
cloudflare | Cuenta + cloudflared + token | Gratis | Dominios personalizados |
ngrok | Cuenta + agente + token | Gratis con límites | Pruebas, de corta duración |
pinggy | SSH, sin cuenta | Capa gratuita | URLs rápidas de un solo uso |
openvpn | Tu propio egress OpenVPN | Autoalojado | Infraestructura VPN existente |
custom | Un comando bajo [tunnel.custom] | Depende de | Algo más |
tunnel_provider = "none" (el valor predeterminado) mantiene el gateway local sin túnel. Consulta la Referencia de configuración para los campos [tunnel.<provider>] de cada proveedor.
Tailscale: WSS y inscripción
Con tunnel_provider = "tailscale", la puerta de enlace se publica con tailscale serve (o tailscale funnel cuando [tunnel.tailscale].funnel = true). Cuando [wss] está habilitado, el daemon también publica el listener RPC de WSS en la tailnet, junto con el endpoint de enrolamiento cuando [enroll] está habilitado. Cada uno conserva su propio puerto:
tailscale serve --tcp <wss.port> tcp://127.0.0.1:<wss.port>
tailscale serve --tcp <enroll.port> tcp://127.0.0.1:<enroll.port>
Estos son reenvíos de paso directo de TCP en bruto. TLS sigue terminando dentro del daemon, por lo que el plano WSS permanece mutuamente autenticado, y la cadena corta de autenticación de la inscripción sigue vinculada a la propia CA del daemon. El proxy HTTPS de Tailscale no se usa para ellos, porque eliminaría el certificado de cliente. Una dirección de enlace comodín (0.0.0.0 / ::) se reenvía a loopback; una dirección de enlace específica se reenvía a esa misma dirección.
Cosas que debes saber:
- Solo en la tailnet, incluso con funnel. Funnel solo escucha en los puertos 443, 8443 y 10000, y exponer el plano mTLS a internet público debe ser una elección explícita tuya. No se activa como efecto secundario de exponer la puerta de enlace con funnel. Para acceder a WSS desde fuera de la tailnet, usa
[relay]. - Los nombres del certificado son automáticos. Cuando se accede a los listeners de WSS y de inscripción a través de Tailscale, el daemon añade los nombres de la tailnet del nodo al certificado de servidor autogenerado al iniciarse. Esto incluye el nombre MagicDNS (
<node>.<tailnet>.ts.net), su nombre corto (<node>), las IP de la tailnet del nodo y[tunnel.tailscale].hostnamesi está definido. «Acceder a través de Tailscale» significa usar este túnel, o bien tener[wss].bind/[enroll].bindconfigurado directamente con una dirección de tailnet (100.64.0.0/10ofd7a:115c:a1e0::/48), con o sin túnel. Los nombres se consultan una vez al iniciar el daemon (y en cada recarga) y se comparten entre ambos listeners, de modo que WSS y la inscripción siempre presentan el mismo certificado. Solo se regenera el certificado de servidor cuando cambian los nombres. La CA no cambia, por lo que los clientes inscritos siguen funcionando. Si no se puede contactar con tailscaled al iniciar, o si responde antes de haber recibido su mapa de red (algo habitual justo después del arranque), el daemon registra una advertencia y conserva todos los nombres que ya incluye el certificado existente. Las nuevas entradas de[wss].sanssiguen aplicándose, pero las eliminaciones esperan a un inicio en el que tailscaled responda. En una instalación nueva con tailscaled detenido, el certificado solo incluye los nombres conocidos sin él (localhost,127.0.0.1,[wss].sansy[tunnel.tailscale].hostname); reinicia una vez que tailscaled esté activo para añadir el resto. Un certificado propio indicado encert_pathnunca se modifica. - El bloqueo de inscripción es compartido. Las conexiones reenviadas llegan al endpoint de inscripción desde loopback. Esto significa que todos los dispositivos inscritos en la tailnet comparten un mismo bloqueo de intentos de emparejamiento, y los códigos incorrectos repetidos de un mismo par de la tailnet bloquean temporalmente la inscripción para los demás. El bloqueo falla en modo seguro (fail closed). Nunca debilita la puerta de código de emparejamiento.
- Los reenvíos son sesiones en primer plano de
tailscale servepropiedad de la puerta de enlace, por lo que se retiran cuando la puerta de enlace se detiene. Si alguno termina por su cuenta más adelante, el demonio registra una advertenciatailscale serve --tcp exited after publication; reinicia la puerta de enlace para volver a publicarlo. - La inscripción sigue al modo de CA. Con una CA de cliente propia (
[wss.client_auth]habilitado conca_cert_path), el daemon no tiene clave de firma y no ejecuta el endpoint de inscripción, por lo que tampoco se publica.
Opción 3: Proxy inverso
Ejecuta nginx / Caddy / Traefik delante de la puerta de enlace. Termina TLS allí y reenvía la conexión a localhost:42617. Adecuado para:
- Servidores con una IP pública real
- Configuraciones de proxy inverso existentes con Let’s Encrypt
- Servir múltiples servicios en el mismo host
Una configuración mínima de Caddy:
agent.example.com {
reverse_proxy localhost:42617
}
El gateway permanece vinculado a 127.0.0.1, el proxy es el que escucha.
Autenticación de webhook de puerta de enlace genérica
Las rutas POST /webhook y POST /sop/* exclusivas de SOP de la puerta de enlace pueden requerir, independientemente del emparejamiento, un encabezado de secreto compartido que coincida exactamente:
[gateway]
webhook_secret = "replace-with-a-random-secret"
Envía el valor como X-Webhook-Secret. Cuando require_pairing = true y webhook_secret está configurado, los clientes deben enviar tanto el token bearer emparejado como el secreto del webhook. El secreto de la puerta de enlace genérica está deliberadamente separado de [channels.webhook.<alias>].secret; los alias de canal utilizan sus propios listeners y, en su lugar, la verificación HMAC del cuerpo.
Las escrituras de configuración de Gateway surten efecto mediante la vista de configuración activa de Gateway. Las ediciones directas de archivos requieren la recarga normal del demonio (o un reinicio independiente de Gateway).
Recarga remota del daemon
POST /admin/reload vuelve a leer config.toml y reconstruye cada subsistema en el mismo proceso (mismo PID, tiempo de inactividad inferior a un segundo). Es la forma admitida de aplicar cambios de configuración sin un reinicio completo. De forma predeterminada solo acepta llamadas de loopback, por lo que un panel remoto o un curl desde otra máquina recibe 403 Forbidden.
Para permitir recargas remotas autenticadas:
[gateway]
allow_remote_admin = true # off by default
require_pairing = true # required for remote reload (also the default)
Con esto habilitado, un llamador que no sea de loopback puede acceder a /admin/reload solo si también pasa la autenticación de emparejamiento (Authorization: Bearer <token>). Los llamadores de loopback (la CLI local) siempre están permitidos y no necesitan token. /admin/shutdown y los endpoints de código de emparejamiento permanecen restringidos a localhost independientemente de este flag.
Debido a que el acceso remoto se aplica mediante emparejamiento, allow_remote_admin no tiene efecto a menos que require_pairing también esté activado: si el emparejamiento está deshabilitado, un llamador remoto no puede ser autenticado, por lo que la solicitud se rechaza con 403 Forbidden en lugar de permitirse de forma anónima. Esto hace imposible exponer una recarga remota sin autenticación cambiando un solo indicador.
Seguridad: deja allow_remote_admin desactivado a menos que necesites específicamente recargar desde otro host. Mantén require_pairing = true (el valor predeterminado) para que las recargas no puedan activarse de forma anónima.
Despliegue en Raspberry Pi
Requisitos previos
- Raspberry Pi 3/4/5 (u otra SBC similar) con Raspberry Pi OS o Alpine
- Conectividad de red (WiFi o Ethernet)
- Opcional: periféricos USB para la integración de hardware
Instalar
Clona y ejecuta el instalador. Sin flags, entra en un selector interactivo donde eliges el tipo de build y qué características compilar, incluyendo las características de hardware para GPIO/I2C/SPI. En la Pi también usa los perfiles de cargo ajustados para la Pi; consulta Configuración de Raspberry Pi para la configuración de swap y la matriz de builds por modelo.
Raspberry Pi OS
git clone https://github.com/zeroclaw-labs/zeroclaw.git
cd zeroclaw
./install.sh
Alpine
apk add curl rust cargo openssl-dev pkgconf git
git clone https://github.com/zeroclaw-labs/zeroclaw.git
cd zeroclaw
./install.sh
Otorga acceso a GPIO, I2C, SPI a través de rppal cuando seleccionas las características de hardware. La unidad de servicio predeterminada ya agrega al usuario a los grupos gpio, spi, i2c.
Lista de verificación
- Instala el binario (
./install.sh, elige tus características en el selector) - Ejecuta
zeroclaw quickstart - Configura tus canales. Telegram no necesita puerto; los webhooks necesitan un túnel
- Instala el servicio:
zeroclaw service install && zeroclaw service start - Para el acceso LAN: establece
[gateway] host = "0.0.0.0"+allow_public_bind = true - Para webhooks: configura
[tunnel]con un proveedor
Alpine Linux (OpenRC)
Los servicios de OpenRC se ejecutan a nivel del sistema. Instálelos como root:
sh
sudo zeroclaw service install
Crea:
/etc/init.d/zeroclaw: script de inicio/etc/zeroclaw/: directorio de configuración/var/log/zeroclaw/: archivos de registro
Habilitar y comenzar:
sh
sudo rc-update add zeroclaw default
sudo rc-service zeroclaw start
sudo rc-service zeroclaw status
Registros:
sh
sudo tail -f /var/log/zeroclaw/error.log
Notas de OpenRC
- El servicio se ejecuta como
zeroclaw:zeroclaw(menor privilegio) - Solo a nivel de sistema: no hay servicios OpenRC a nivel de usuario
- Todas las operaciones de servicio requieren
sudo.
Advertencia sobre el polling de Telegram
El método getUpdates de la Telegram Bot API es de un solo poller por token de bot. No puedes ejecutar dos instancias con el mismo token; la segunda recibe Conflict: terminated by other getUpdates request.
Si ves esto:
-
ps aux | grep zeroclawy confirma que solo se está ejecutando un daemon. -
Verifica que no tengas
cargo run --bin zeroclaw -- channel start telegramde una sesión de desarrollo pendiente. -
Si está desactualizado, restablece la sesión de la encuesta de Telegram:
sh
curl -X POST "https://api.telegram.org/bot$TOKEN/close"
Exponer webhooks de forma segura
Una URL de webhook accesible públicamente es una superficie de ataque. Como mínimo:
- Verificación de firma HMAC:
secretconfigurado en cada canal de webhook - Lista de IPs permitidos de origen donde el servicio tiene IPs de salida fijas (GitHub, AWS SNS)
- Limitación de tasa:
rate_limit_per_secen la configuración del canal de webhook
Consulta Canales → Webhooks para ver el conjunto completo de opciones.
Ver también
- Configuración → Contenedor: configuración de red específica de Docker
- Configuración → Gestión de servicios: integración del servicio de la plataforma
- Operaciones → Resumen
- Seguridad → Resumen