17/Documentation
Variantes de despliegue B–E
Otras puertas de entrada además de la predeterminada: un proxy con reconocimiento de identidad, un proxy inverso sin Tailscale, un ingress externo al host, varios Collies en un mismo host (uno por usuario, o varias instancias para un usuario) y la puerta de reserva de un crew
El puente se vincula a 127.0.0.1. Los despliegues varían según el ingreso y la verificación de identidad. Variante A (tailscale serve sin cifrar) se encuentra en el README. Los requisitos de seguridad de docs/security.md se aplican a todas las configuraciones.
- Front doors, producto por producto: Tailscale, Cloudflare Tunnel, Nginx Proxy Manager, NetBird, Caddy
Los dispositivos individuales también se pueden autorizar sin proxy mediante emparejamiento.
---
Variante B: proxy consciente de la identidad + autorización por dispositivo
Collie lee un ID de dispositivo opaco desde COLLIE_DEVICE_HEADER y comprueba COLLIE_DEVICE_ALLOWLIST. Los ID en lista blanca reciben acceso de escritura; los ID ausentes o no listados reciben acceso de solo lectura (la entrada de terminal, las subidas y las acciones del panel se bloquean). El emparejamiento es un segundo control y siempre está activo: cada dispositivo también necesita un token de emparejamiento para cada llamada a /api, lecturas incluidas.
Configuración de Collie (.env):
COLLIE_HOST=127.0.0.1 # keep loopback (default)
COLLIE_DEVICE_HEADER=X-Device-Id # the header your proxy injects
# ids allowed to drive agents; others → read-only
COLLIE_DEVICE_ALLOWLIST=my-phone,my-laptop
# only if the proxy does NOT forward the public Host
# COLLIE_ALLOWED_ORIGINS=https://collie.example.com
# REQUIRED unless the proxy forwards a Host Collie already knows: loopback, a
# discovered Tailscale host, or an allowed origin's host
# COLLIE_PUBLIC_HOSTS=collie.example.com
# opt out of Host validation entirely (re-opens DNS rebinding)
# COLLIE_ALLOW_ANY_HOST=1
# COLLIE_TRUSTED_USER still composes on top if your ingress also injects
# Tailscale-User-Login
# accept a request carrying no Tailscale-User-Login at all
# COLLIE_TRUSTED_USER_OPTIONAL=1Requisitos del proxy:
- Autenticar el dispositivo (mTLS, SSO, forward-auth).
- Sobrescribir el encabezado del dispositivo en cada solicitud ascendente para evitar la suplantación de clientes.
- Hacer proxy a loopback (
127.0.0.1:$COLLIE_PORT). - Reenviar el
Hostpúblico sin modificaciones, o listar el origen público enCOLLIE_ALLOWED_ORIGINSpara superar la comprobación de mismo origen.
Ejemplo de configuración de Nginx:
location / {
proxy_set_header X-Device-Id $device_id;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8787;
}Verifique la inyección y sobrescritura de encabezados desde un dispositivo externo. Cada llamada a /api excepto /api/health y /api/pair necesita un token de emparejamiento, por lo que primero se debe emparejar un terminal. En el host, ejecute collie pair. Imprime un código de 8 caracteres. Solicite el código desde el dispositivo con el que realice la prueba. El encabezado Origin debe coincidir con la URL pública. La respuesta contiene el token. Manténgalo en una variable de shell y no lo pegue nunca en un archivo:
$ collie pair # on the host; note the code it prints
$ TOKEN=$(curl -s -X POST https://collie.example.com/api/pair \
-H 'Content-Type: application/json' -H 'Origin: https://collie.example.com' \
-d '{"code":"ABCD1234","label":"header-check"}' | jq -r .token)A continuación, ejecute la comprobación de encabezados con el token:
$ curl -s -H "Authorization: Bearer $TOKEN" https://collie.example.com/api/snapshot | jq -c .device
{"enforced":true,"device":"header-check","authorized":true}
$ curl -s -H "Authorization: Bearer $TOKEN" -H 'X-Device-Id: not-listed' https://collie.example.com/api/snapshot | jq -c .device
{"enforced":true,"device":"header-check","authorized":true}Con un token, device designa la etiqueta emparejada (header-check), no el valor del encabezado. Ningún campo devuelve ya el valor sin procesar del encabezado, por lo que debe leerse authorized: es true solo cuando el encabezado que vio el puente está en COLLIE_DEVICE_ALLOWLIST y el token es válido. Si la segunda comprobación devuelve "authorized":false, el proxy está añadiendo el encabezado del cliente en lugar de sobrescribirlo. Al terminar, elimine el dispositivo de prueba con collie devices revoke header-check.
Notas operativas:
- El acceso directo por loopback (
http://127.0.0.1:$COLLIE_PORT) no envía encabezados y es de solo lectura, y sigue requiriendo un token de emparejamiento. - Para ejecutar comandos localmente mediante curl, pase el token y el encabezado explícitamente a loopback:
curl -H "Authorization: Bearer $TOKEN" -H 'X-Device-Id: my-laptop' http://127.0.0.1:$COLLIE_PORT/api/... - Revoque el acceso eliminando el ID de
COLLIE_DEVICE_ALLOWLISTy reiniciando (Standalone:bin/collie restart; Herdr:herdr plugin action invoke restart --plugin herdr.collie). - No active
COLLIE_DEVICE_HEADERentailscale servebásico; no sobrescribe encabezados.
---
Variante C: proxy inverso como única puerta de entrada (sin Tailscale)
Utilice esta opción al ejecutar fuera de Tailscale o detrás de un proxy TLS/SSO dedicado. COLLIE_SKIP_SERVE=1 evita que Collie administre tailscale serve.
Se aplican los cuatro requisitos de proxy de Variante B.
Configuración de ejemplo de Caddy:
collie.example.com {
reverse_proxy 127.0.0.1:8787 {
header_up X-Device-Id {your_device_id}
header_up Host {host}
}
}Configuración de Collie (.env):
# proxy is ingress; never run tailscale serve
COLLIE_SKIP_SERVE=1
# REQUIRED — Host validation fails closed, and `collie start` discovers no
# tailnet name here
COLLIE_PUBLIC_HOSTS=collie.example.com
# opt out of Host validation (re-opens DNS rebinding)
# COLLIE_ALLOW_ANY_HOST=1
# exact public origin for the same-origin gate
COLLIE_ALLOWED_ORIGINS=https://collie.example.com
COLLIE_DEVICE_HEADER=X-Device-Id # the header your proxy injects…
# …and the ids allowed to drive; others → read-only
COLLIE_DEVICE_ALLOWLIST=my-phone,my-laptop
# optional — status banner, `collie qr`, and the address a lead hands
# joining machines (crew)
# COLLIE_PUBLIC_URL=https://collie.example.comCOLLIE_TRUSTED_USER no tiene efecto sin Tailscale. Los encabezados por dispositivo o la propia autenticación del proxy actúan como control de acceso.
Enrutamiento de /auth/ y almacenamiento en caché
- Pase
/sw.jsyindex.htmlcon el origenCache-Control(no-cache). No bloquee los recursos estáticos para clientes no autenticados, o los service workers no podrán actualizarse. - Enrute los flujos de autenticación bajo
/auth/*(o/cdn-cgi/access/para Cloudflare Access). Los service workers omiten el almacenamiento en caché para/auth/*. - Las redirecciones de forward-auth en solicitudes a la API se tratan como 401 para mostrar la interfaz de inicio de sesión. Las configuraciones con Authentik deben enrutar
/auth/a/outpost.goauthentik.io/start. - Sin un proxy en
/auth/, Collie responde a404allí. La página ahora le indica a un dispositivo no emparejado que ejecutecollie paire introduzca el código en Configuración. Nunca redirige, ya que la ruta pertenece a su proxy.
collie.example.com {
handle /auth/* {
reverse_proxy 127.0.0.1:9091
}
handle {
forward_auth 127.0.0.1:9091 { ... }
reverse_proxy 127.0.0.1:8787 { ... }
}
}Verifique el endpoint y las reglas de caché. $TOKEN es un token de emparejamiento, solicitado como en Variante B; authorized es true cuando el encabezado está en la lista blanca:
$ curl -s -H "Authorization: Bearer $TOKEN" https://collie.example.com/api/snapshot | jq -c .device
{"enforced":true,"device":"header-check","authorized":true}
$ curl -sI https://collie.example.com/sw.js | grep -i '^cache-control'
cache-control: no-cache---
Variante D: proxy de identidad fuera del host a través de la tailnet
Utilice esta opción al enrutar tráfico a través de un nodo de entrada centralizado de Tailscale.
Se aplican los requisitos de Variante B, excepto que el proxy apunta al endpoint HTTP de Tailscale del host en lugar de loopback.
ACL de Tailscale (obligatorio)
Dado que tailscale serve reenvía los encabezados del cliente sin modificaciones, las ACL de Tailscale deben restringir el acceso directo al puerto únicamente al nodo de entrada.
Tailscale / Headscale ≥ 0.29:
"grants": [
{ "src": ["tag:ingress"], "dst": ["tag:agent-host"], "ip": ["tcp:8787"] },
]Headscale ≤ 0.28:
acls:
- action: accept
src: ["ingress-node"]
dst: ["agent-host:8787"]
- action: accept
src: ["my-phone", "my-laptop"]
dst: ["agent-host:1-8786", "agent-host:8788-65535"]Configuración de Collie (.env):
# proxy terminates TLS; this hop is tailnet-internal
COLLIE_SERVE_MODE=http
COLLIE_HOST=127.0.0.1 # keep loopback (default)
# header your forward-auth injects — REQUIRED here
COLLIE_DEVICE_HEADER=X-Tailnet-Device
# ids allowed to drive; others + header-less → read-only
COLLIE_DEVICE_ALLOWLIST=my-phone,my-laptop
# REQUIRED — the Host the proxy forwards. COLLIE_TAILSCALE_HOSTS carries the bare
# tailnet name `collie start` found; a rewritten Host is yours to list.
# COLLIE_ALLOW_ANY_HOST=1 opts out.
COLLIE_PUBLIC_HOSTS=host:8787,host.your-tailnet.ts.net:8787
# the public origin the browser actually uses
COLLIE_ALLOWED_ORIGINS=https://collie.example.comCOLLIE_TRUSTED_USER coincide con la identidad del nodo de entrada, no con la del usuario final detrás de él.
Verificación:
Desde un par de tailnet sin ingress. $TOKEN es un token de emparejamiento, solicitado como en Variante B:
$ curl -s -H "Authorization: Bearer $TOKEN" https://collie.example.com/api/snapshot | jq -c .device
{"enforced":true,"device":"header-check","authorized":true}
$ curl -s --max-time 10 -H 'X-Tailnet-Device: my-phone' http://host.your-tailnet.ts.net:8787/api/snapshot
curl: (28) Connection timed outEn el host del agente, una petición no envía ningún encabezado, por lo que authorized es false incluso con un token válido:
$ curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8787/api/snapshot | jq -c .device
{"enforced":true,"device":"header-check","authorized":false}---
Variante E: cualquier otra red en malla o túnel (NetBird, ZeroTier, Cloudflare Tunnel)
Apunte el túnel/proxy a 127.0.0.1:$COLLIE_PORT.
Configuración de Collie (.env):
COLLIE_SKIP_SERVE=1 # never run tailscale serve
# REQUIRED — exact public host; Host validation fails closed and finds no
# tailnet name here
COLLIE_PUBLIC_HOSTS=collie.example.com
# exact public origin for the same-origin gate
COLLIE_ALLOWED_ORIGINS=https://collie.example.comReglas:
COLLIE_TRUSTED_USERestá inactivo sin Tailscale. UtiliceCOLLIE_DEVICE_HEADERo la autenticación del túnel.- Utilice un nombre de host estático para que el almacenamiento en caché de la PWA y
COLLIE_PUBLIC_HOSTSsigan siendo válidos.
---
Front doors, producto por producto
Configuraciones básicas para los front doors más habituales. Cada una concluye en el mismo punto: HTTPS en un nombre de host fijo, reenviado a 127.0.0.1:$COLLIE_PORT en este host.
Nota. Web Push, el micrófono y la instalación en la pantalla de inicio requieren HTTPS con un certificado en el que el teléfono confíe. Con HTTP simple, Collie sigue leyendo y respondiendo en una pestaña del navegador, pero estas tres funciones permanecen desactivadas (Entrada de voz y Web Push).
Salvo Tailscale, cada front door utiliza las mismas tres líneas en el .env de Collie y requiere reiniciar después:
COLLIE_SKIP_SERVE=1 # you own the door, not Collie
COLLIE_PUBLIC_HOSTS=collie.example.com # the hostname the phone opens
COLLIE_ALLOWED_ORIGINS=https://collie.example.com # the same, as an originEjecute el proxy o túnel en el mismo host que Collie. Collie rechaza las conexiones que no provengan de loopback, por lo que un proxy en otra máquina requiere Variante D en su lugar.
Luego empareje el teléfono con collie pair. Ninguno de estos accesos determina qué dispositivo puede escribir; el emparejamiento sí lo hace (Emparejar un dispositivo).
| Front door | Privado de forma predeterminada | Certificado HTTPS | Paso adicional |
|---|---|---|---|
| Tailscale | sí, solo tailnet | Tailscale lo emite | habilitar certificados HTTPS una vez |
| Cloudflare Tunnel | no, público | Cloudflare lo emite | inicio de sesión de Cloudflare Access al frente |
| Nginx Proxy Manager | depende del host | Let's Encrypt | red en modo host para el contenedor |
| NetBird | sí, solo mesh | ninguno propio | un proxy en el host, desafío DNS |
| Caddy | depende del host | Let's Encrypt, automático | un complemento de DNS para un nombre privado |
Tailscale
La opción predeterminada, y la única puerta que Collie administra por sí mismo: collie start ejecuta tailscale serve en su nombre de MagicDNS.
- En la consola de administración de Tailscale, abra DNS, active MagicDNS y luego Enable HTTPS.
- Inicie Collie, que se publica a sí mismo en la tailnet.
collie start
- Establezca
COLLIE_TRUSTED_USERcon su usuario de tailnet en.env, y luego reinicie.collie restart
Variante A contiene el resto. No configure COLLIE_SKIP_SERVE para esta puerta.
Nunca use tailscale funnel. Funnel expone el puerto a la internet pública.Cloudflare Tunnel
cloudflared se conecta hacia Cloudflare, por lo que no se abre ningún puerto en el router. El nombre de host es público, de modo que debe haber un inicio de sesión delante.
- Inicie sesión, cree un túnel y apunte un nombre de host hacia él.
cloudflared tunnel login cloudflared tunnel create collie cloudflared tunnel route dns collie collie.example.com
- Escriba
~/.cloudflared/config.ymlcon el identificador de túnel quecreateimprimió.tunnel: <tunnel-id> credentials-file: /home/you/.cloudflared/<tunnel-id>.json ingress: - hostname: collie.example.com service: http://127.0.0.1:8787 - service: http_status:404
- Instálelo como servicio con esa configuración para que persista tras reiniciar.
sudo cloudflared --config ~/.cloudflared/config.yml service install
- En Cloudflare Zero Trust, agregue una self-hosted application para
collie.example.com, con una política que permita el acceso únicamente a su correo electrónico.
Un túnel creado en el panel de Zero Trust también funciona: configure el servicio de su nombre de host público como http://127.0.0.1:8787. cloudflared reenvía el nombre de host público como Host, que es lo que COLLIE_PUBLIC_HOSTS espera.
Advertencia. Sin el paso 4, cualquiera que encuentre el nombre de host obtiene una shell en su máquina. Cloudflare Access es la cerradura de la puerta; la vinculación decide luego qué dispositivo puede escribir.
- Haga que Collie compruebe el bloqueo. En la aplicación de Access, copie la etiqueta Application Audience (AUD). Agréguela junto con el nombre de su equipo a
.env, luego reinicie.COLLIE_ACCESS_TEAM=myteam # from myteam.cloudflareaccess.com COLLIE_ACCESS_AUD=<the AUD tag>Cada solicitud a través del túnel debe llevar ahora un token firmado por Cloudflare para esta aplicación. Si la aplicación de Access se elimina, recibe una regla de omisión o aún no se propaga a todos los servidores perimetrales, Collie rechaza la solicitud en vez de mostrar los paneles. La comprobación de audiencia es importante: cada aplicación de Access en un equipo firma con la misma clave, por lo que un token de otra aplicación también se rechaza.
Collie obtiene las claves públicas del equipo al iniciar y cada hora. Hasta que la primera obtención funcione, cada solicitud remota recibe
503. Si una obtención posterior falla, Collie conserva las claves que ya tiene. Defina ambas líneas o ninguna: una sola rechaza cada solicitud remota. En cualquier caso, el registro del bridge nombra el problema en una línea; revise el registro tras reiniciar.Solo un proceso en esta máquina omite el token: una solicitud a
127.0.0.1olocalhostque no lleveCf-*ni encabezado de reenvío (X-Forwarded-For,X-Forwarded-Host,X-Forwarded-Proto,Forwarded,X-Real-Ip). Todavía requiere emparejamiento para escribir./api/healthy los enlaces de crew tampoco necesitan token.collie doctores uno de esos emisores locales, por lo que un doctor en verde no indica que las claves se hayan cargado o que un token se verifique. Abra el hostname desde el teléfono para comprobarlo. ADR 0081 detalla los motivos.Mantenga estas reglas con la compuerta activada:
- Una sola puerta de entrada a la vez. Con Access activado, una solicitud a través de
tailscale serve, Caddy o Traefik también necesita un token, ya que añaden un encabezado de reenvío de forma predeterminada. Una máquina que sirve tanto un túnel como un nombre de tailnet (tailscale serve) pierde el acceso de tailnet para navegadores: esas solicitudes no llevan token de Access y reciben401. - Sin un segundo proxy entrecloudflaredy Collie que haga que las solicitudes parezcan locales. Uno que reescribeHosta127.0.0.1olocalhosty eliminaCf-*y los encabezados de reenvío lo hace a propósito. Un nginxproxy_pass http://127.0.0.1:8787básico lo hace de forma predeterminada: envíaHost: 127.0.0.1:8787y no añade ningún encabezado de reenvío a menos que se configure uno. Esas solicitudes omiten el token. Apuntecloudflareda Collie directamente. - No añada reglas de omisión de Access para rutas de Collie, no para el manifiesto, el service worker o los iconos. Access añade el token a cada solicitud que deja pasar. Una ruta omitida no lleva token, por lo que Collie responde401.
Access redirige a /cdn-cgi/access/ para iniciar sesión. Mantenga sw.js y index.html sin almacenar en caché, tal como indica Enrutamiento de /auth/ y almacenamiento en caché.
Nginx Proxy Manager
Un proxy inverso con interfaz web y Let's Encrypt integrado. Se ejecuta en Docker, y dentro de un contenedor 127.0.0.1 es el contenedor, no su host.
- Asigne al contenedor la red del host para que pueda comunicarse con Collie en loopback.
services: npm: image: jc21/nginx-proxy-manager:latest network_mode: host volumes: - ./data:/data - ./letsencrypt:/etc/letsencrypt
- Abra la interfaz en el puerto 81 y agregue un Proxy Host: dominio
collie.example.com, esquemahttp, reenviar a127.0.0.1, puerto8787. - En la pestaña SSL, solicite un certificado de Let's Encrypt y active Force SSL.
Deje Almacenar activos en caché desactivado para que el service worker pueda actualizarse. Collie sondea mediante HTTP simple, por lo que el interruptor de Compatibilidad con Websockets no tiene efecto.
Para un nombre que solo se resuelve dentro de su red, elija Usar un desafío DNS en la pestaña SSL. Let's Encrypt no puede acceder a un host privado a través de HTTP.
Advertencia.host.docker.internalconhost-gatewayno funciona aquí. Se conecta a la dirección del puente de Docker, y Collie solo escucha en127.0.0.1.
Si el proxy está expuesto a internet, agregue una Lista de acceso al host del proxy, o mantenga los puertos 80 y 443 cerrados en el router y acceda al host a través de una VPN.
NetBird
NetBird es una red en malla WireGuard, como una tailnet. Transporta el tráfico y un proxy inverso en el host añade HTTPS.
- Conecte el host y el teléfono a la misma red NetBird.
netbird up
- Apunte un registro DNS para
collie.example.coma la dirección NetBird del host (100.x.y.z). - Ejecute Caddy en el host, vinculado a esa dirección, con un certificado mediante desafío DNS.
collie.example.com { bind 100.x.y.z tls { dns cloudflare {env.CF_API_TOKEN} } reverse_proxy 127.0.0.1:8787 }
El desafío DNS demuestra que usted posee el dominio sin que Let's Encrypt deba comunicarse con el host, permitiendo que el nombre apunte a una dirección privada. Caddy requiere el plugin de DNS correspondiente a su proveedor, por ejemplo xcaddy build --with github.com/caddy-dns/cloudflare.
Un nombre de par de NetBird como host.netbird.cloud no dispone de un certificado con confianza pública; utilice por tanto un dominio propio. La misma configuración sirve para ZeroTier o cualquier otra red en malla.
Caddy
Caddy obtiene y renueva su propio certificado. En un host accesible a través de los puertos 80 y 443, esta es la configuración completa:
collie.example.com {
reverse_proxy 127.0.0.1:8787
}Para un nombre privado, agregue el bloque tls { dns … } de NetBird arriba. Un host público también necesita un inicio de sesión delante, como en Variante C.
---
Servir Collie bajo una ruta
Una única compilación de release sirve cualquier punto de montaje. Establezca la ruta en la instancia .env (o [serve] base_path en config.toml), reinicie, y el bridge servirá la aplicación, sus activos y /api/* bajo esa ruta:
# https://<your-node>/collie/ — leading and trailing slash both optional; the bridge adds them
COLLIE_BASE_PATH=/collieAceptado: /collie, collie/, /apps/collie/. Rechazado, registrando una línea y usando la raíz en su lugar: un segmento . o .., espacios en blanco, ?, #. collie doctor muestra el montaje en su línea front-door.
Con la puerta de entrada predeterminada, collie serve (y cada collie start) publica tailscale serve --set-path=/collie/ en lugar de la raíz, lo que permite que otra aplicación mantenga / en el mismo nodo. Tailscale retira el montaje antes de actuar como proxy, y el bridge también acepta solicitudes que aún lo incluyan, de modo que un proxy inverso funciona en ambos casos. Para verificar el suyo:
curl -s https://<your-node>/collie/api/health # JSON, not HTML, means the mount is rightDetrás de su propio proxy (Variante C), monte el bridge en la misma ruta. Caddy: handle_path /collie/* { reverse_proxy 127.0.0.1:8787 }. Nginx: location /collie/ { proxy_pass http://127.0.0.1:8787/; }. Si configura COLLIE_PUBLIC_URL, incluya la ruta: https://collie.example.com/collie/.
Un cambio de montaje equivale a una nueva aplicación en el teléfono. El alcance y la identidad de la PWA instalada corresponden a la ruta desde la que se instaló. Tras modificar COLLIE_BASE_PATH o volver a la raíz, elimine el icono de la pantalla de inicio y agréguelo de nuevo desde la nueva dirección; el anterior seguirá apuntando a una ruta que el bridge ya no sirve. El bridge en sí solo requiere un reinicio, y collie serve traslada la puerta automáticamente: elimina el mapeo que designa su registro antes de publicar el nuevo.
---
Varios Collie en un mismo host
Para alojar instancias independientes por usuario en un sistema compartido (ADR 0001):
# ~/.config/herdr/plugins/config/herdr.collie/.env — one per Unix user
# this user's loopback bridge port — unique per user
COLLIE_PORT=8801
# this user's tailnet https listener — unique per user
COLLIE_SERVE_PORT=8443
COLLIE_TRUSTED_USER=dev-a@example.com # only this tailnet login may drive these agentsVerificación de URL de binario independiente: bin/collie url. Verificación de URL de plugin de Herdr: herdr plugin action invoke url --plugin herdr.collie.
Requisitos:
- Ejecutar usuarios de Unix distintos e instancias separadas de
herdr/Collie. - Definir siempre
COLLIE_TRUSTED_USERpara restringir el acceso por puerto. - Las vinculaciones iniciales de Tailscale requieren configuración de operador: ejecute
bin/collie serve(o el equivalente de Herdr) con privilegios de operador.
COLLIE_SERVE_PORT define el puerto de entrada para una instancia. COLLIE_INSTANCE crea una instancia aislada con su propia unidad de servicio y configuración en el mismo host (Múltiples instancias de Collie en un mismo host).
---
Múltiples instancias de Collie en un mismo host
Use esta configuración para ejecutar un segundo Collie independiente en la misma máquina. Los ejemplos incluyen ejecutar una versión estable junto a una copia de trabajo, o ejecutar un segundo multiplexor (tmux/zellij) junto al valor predeterminado administrado por Herdr. Cada instancia obtiene un puerto dedicado, configuración, directorio de estado y unidad de servicio. Esto difiere de Varios Collie en un mismo host, que asigna una instancia por usuario de Unix. En este caso, un solo usuario ejecuta múltiples instancias.
Crear la segunda instancia
Defina COLLIE_INSTANCE=<name> para nombrar la instancia. El nombre debe coincidir con [a-z0-9-]{1,16}. Una instancia con nombre también requiere un COLLIE_PORT explícito. La CLI finaliza con un error si falta este puerto; no infiere ningún valor predeterminado.
Crear ~/.config/herdr/plugins/config/herdr.collie-<name>/.env:
COLLIE_INSTANCE=<name> # required — [a-z0-9-], max 16 chars
COLLIE_PORT=8788 # required for a named instance — no default is inferred
# Unset, this defaults to ~/.local/state/collie, with no instance suffix
COLLIE_STATE_DIR=/home/you/.local/state/collie-<name>COLLIE_INSTANCE, COLLIE_PORT y COLLIE_STATE_DIR pueden residir en ese archivo .env. El entorno fusionado resuelve la instancia. HERDR_PLUGIN_CONFIG_DIR anula el directorio donde la CLI busca la configuración.
Ejecutar una unidad de servicio por instancia
collie start escribe la unidad cuando el entorno define la instancia. El operador no la escribe manualmente. En macOS escribe un plist de launchd en ~/Library/LaunchAgents/ en su lugar. La unidad define COLLIE_PORT, COLLIE_INSTANCE, COLLIE_PLUGIN_ROOT, HERDR_PLUGIN_CONFIG_DIR y EnvironmentFile=-<config dir>/.env. --instance <name> en ExecStart existe únicamente para que dos instancias que comparten un binario puedan distinguir sus bridges (cli/unit.ts systemdUnit()):
ExecStart=<root>/bin/collie _exec-bridge --instance <name>
Environment=COLLIE_PORT=8788
Environment=COLLIE_INSTANCE=<name>
Environment=COLLIE_PLUGIN_ROOT=<root>
Environment=HERDR_PLUGIN_CONFIG_DIR=<config dir>
EnvironmentFile=-<config dir>/.envLa CLI mantiene un archivo de controlador tailscale serve por instancia, por lo que ejecutar unserve en una instancia no elimina la asignación de otra.
Apuntar a una instancia con nombre desde la CLI
Cada verbo de la CLI (pair, devices, url, qr, crew …, logs, push-test, …) resuelve su instancia de destino a partir del entorno del proceso. Defina COLLIE_INSTANCE antes de invocar el verbo:
COLLIE_INSTANCE=next bin/collie pair
COLLIE_INSTANCE=next bin/collie devices listSin COLLIE_INSTANCE, la CLI consulta a Herdr. Herdr solo rastrea el plugin sin sufijo, por lo que el comando se ejecuta en la primera instancia. Defina siempre COLLIE_INSTANCE explícitamente al ejecutar múltiples instancias en un host para evitar modificar la instancia incorrecta.
La regla de rechazo
Si COLLIE_INSTANCE está definido pero falta herdr.collie-<name>/.env, la CLI finaliza con un error. No recurre a la configuración de otra instancia.
El emparejamiento es por instancia
collie pair escribe un código de vinculación en el directorio de estado de esa instancia. El código QR que imprime codifica la URL propia de esa instancia, ya que cada instancia tiene su propio directorio de estado y, a través de COLLIE_PUBLIC_URL o de su propio puerto, su propio punto de entrada. Abra la URL específica de esa instancia en el teléfono e introduzca el código en Configuración → Sistema → Dispositivos vinculados. Un código generado para una instancia no permite vincular un dispositivo a ninguna otra instancia (Emparejar un dispositivo).
---
La puerta de reserva: la ruta de conmutación por error de un crew
Para despliegues de crew. Configura un deputy preautorizado para asumir el control si el líder queda inaccesible (ADR 0027, ADR 0028, CREW_PROTOCOL.md §18).
Configuración de deputy y líder:
| Clave | Predeterminado | Qué hace |
|---|---|---|
COLLIE_STANDBY_PORT | (no definido) | Puerto para el listener en standby. Si no se define, se desactiva la puerta de standby. Debe coincidir en el lead y en el deputy. |
COLLIE_STANDBY_HOST | 127.0.0.1 | Dirección de enlace (127.0.0.1 para proxys locales, IP de overlay para remotos). |
COLLIE_STANDBY_ARM_MS | max(30000, 2.5 × COLLIE_POLL_IDLE_MS) | Duración del silencio del lead requerida antes del armado. |
Defina COLLIE_STANDBY_PORT con un puerto idéntico y sin usar tanto en el lead como en el deputy.
Requisito previo: un hostname, dos backends
El lead y el deputy deben servirse desde el mismo origen para compartir el registro de la PWA y las credenciales del dispositivo. Un crew independiente sin ingress unificado se recupera mediante bin/collie promote (o Herdr: herdr plugin action invoke promote --plugin herdr.collie; CREW_PROTOCOL §14.4).
Configuración de ejemplo para Traefik:
http:
routers:
collie:
rule: "Host(`collie.example.com`)"
service: collie-crew
tls: {}
services:
collie-crew:
failover:
service: collie-lead
fallback: collie-deputy
collie-lead:
loadBalancer:
servers:
- url: "http://lead.internal:8787"
healthCheck:
path: /standby/health
interval: 5s
timeout: 2s
collie-deputy:
loadBalancer:
servers:
- url: "http://deputy.internal:8788" # COLLIE_STANDBY_PORT
healthCheck:
path: /standby/health
interval: 5s
timeout: 2sRespuestas del health check: el lead devuelve 200 (distinto de 200 cuando está depuesto); el deputy devuelve 503 hasta que se arma, y luego 200.
Configúrelo una vez, mientras todo funcione correctamente
Configuración independiente en el lead:
bin/collie pair
bin/collie crew deputy nas
bin/collie crew statusConfiguración con Herdr en el lead:
herdr plugin action invoke pair --plugin herdr.collie
herdr plugin action invoke crew --plugin herdr.collie deputy nas
herdr plugin action invoke crew --plugin herdr.collie statusEn el deputy, configure COLLIE_STANDBY_PORT=8788 y reinicie. Asegúrese de reiniciar todos los peers para cargar el deputy warrant. Verifique en https://collie.example.com/standby.
⚠️ El deputy debe estar supervisado
Durante la toma de control, el deputy guarda el estado y finaliza con el código de salida 75 (EX_TEMPFAIL) para activar el reinicio del proceso (bridge/index.ts).
Asegúrese de que el supervisor se reinicie con códigos de salida distintos de cero (systemd: Restart=always o Restart=on-failure).
En caso de fallo: el runbook
- Abra
https://collie.example.com. - Cuando el lead no responde correctamente, el proxy enruta hacia
/standby. - Revise el estado de standby del deputy.
- Seleccione Tomar el control.
- El deputy verifica el consenso de los peers, aplica el warrant y finaliza con
75. - El supervisor reinicia el proceso; la PWA se recarga como el nuevo lead.
Tras la recuperación:
- Actualice la dirección del peer del nodo depuesto: independiente
bin/collie crew set-address <member> <host:port>o Herdrherdr plugin action invoke crew --plugin herdr.collie set-address <member> <host:port>. - Asigne un nuevo deputy: independiente
bin/collie crew deputy <member>o Herdrherdr plugin action invoke crew --plugin herdr.collie deputy <member>. - No ejecute
crew rotatehasta que todos los miembros se hayan reconectado.