diff --git a/workloads/docs-portal/docs/arquitectura/sesion-tmux-persistente.md b/workloads/docs-portal/docs/arquitectura/sesion-tmux-persistente.md index dc06db2..b533bcc 100644 --- a/workloads/docs-portal/docs/arquitectura/sesion-tmux-persistente.md +++ b/workloads/docs-portal/docs/arquitectura/sesion-tmux-persistente.md @@ -37,6 +37,9 @@ flowchart LR SSH["Cliente SSH
(laptop / tablet / lo que sea)"] -->|docker exec| C["Contenedor tmux-host
--network host, --restart unless-stopped"] C --> T["tmux server
sesión 'lab'"] T --> CC["proceso claude
(Claude Code)"] + C --> HC["healthcheck.sh (background)
cada 30 min"] + HC -.->|curl| NET["gitea.cruzcloud.net"] + HC -.->|recrea si falta| T C -.->|bind mount| DATA["/DATA (host)
repos y configs del lab"] C -.->|bind mount| SOCK["/var/run/docker.sock (host)
control de Docker/k3d"] C -.->|bind mount| HOME["/DATA/AppData/k3d-lab-v3/claude-code-home (host)
-> /root/.claude"] @@ -49,7 +52,17 @@ reinicia. ## Contenedor: `tmux-host` +La imagen se construye desde `scripts/tmux-host/Dockerfile` (antes era +un `docker run ... alpine sh -c "apk add ..."` inline — mismo contenido, +pero ahora como imagen versionada en vez de reinstalar todo en cada +arranque). El Dockerfile fija `WORKDIR /DATA/AppData/k3d-lab-v3/lab-cruzcloud` +(la raíz del lab, con `CLAUDE.md`, `apps-registry/`, `platform-infra/` y +`scripts/`) y agrega `healthcheck.sh` (ver más abajo) como proceso en +background del contenedor. + ```bash +docker build -t tmux-host:local scripts/tmux-host + docker run -d \ --name tmux-host \ --restart unless-stopped \ @@ -59,9 +72,15 @@ docker run -d \ -v /DATA/AppData/k3d-lab-v3/claude-code-home:/root/.claude \ -v /DATA/AppData/k3d-lab-v3/claude-code-home.json:/root/.claude.json \ --network host \ - alpine sh -c "apk add --no-cache tmux docker-cli bash curl git kubectl nodejs npm && npm install -g @anthropic-ai/claude-code && git config --global --add safe.directory '*' && tail -f /dev/null" + tmux-host:local ``` +`WORKDIR` hace que `docker exec` (y por lo tanto `lab-session.sh`) +arranquen ya posicionados en la raíz del lab sin tener que navegar a +mano — `lab-session.sh` además pasa la ruta explícita vía `tmux new -c` +por las dudas, para no depender de que `docker exec` herede el `WORKDIR` +de la imagen. + | Volumen | Destino | Por qué | |---|---|---| | `/DATA` | `/DATA` | Acceso a los repos (`apps-registry`, `platform-infra`, `scripts`) y configs del lab, mismos paths que en el host | @@ -195,13 +214,47 @@ Si la sesión `lab` existe, se adjunta a ella (mismo shell, mismo ejemplo justo después de que el contenedor se recreó desde cero— la crea. No hay que recordar cuál de los dos casos aplica. +## Chequeo de salud automático (cada 30 min) + +`scripts/tmux-host/healthcheck.sh` corre en background dentro del +propio contenedor (lanzado por `entrypoint.sh`, el `ENTRYPOINT` de la +imagen) y cada `HEALTHCHECK_INTERVAL_SEC` (1800s por defecto) hace dos +chequeos independientes, con una respuesta distinta según el tipo de +falla: + +- **Conectividad de red real** (`curl` a `https://gitea.cruzcloud.net/`): + si falla, **solo se registra** en el log y se notifica por Telegram — + el contenedor nunca intenta "arreglar" la red del host. Un problema de + red puede ser de hardware/BIOS (deuda técnica ya conocida, ver + `incidente-medusa-crashloop-2026-08.md`), fuera del alcance real de un + contenedor. +- **Existencia de la sesión tmux `lab`** (`tmux has-session`): si no + existe, **sí se recrea automáticamente** (`tmux new-session -d -s lab + -c `) — es solo recuperar el propio entorno de trabajo del + contenedor, no tocar infraestructura externa, así que es seguro + hacerlo sin intervención humana. + +Todo evento (chequeo ok, falla de red, sesión recreada) queda en +`/DATA/AppData/k3d-lab-v3/tmux-host/logs/health.log` — persistente en +`/DATA`, no solo en el stdout del contenedor (que se pierde con +`docker logs` al recrearlo). Las notificaciones de Telegram reusan el +mismo bot/`chat_id` ya configurado para `scripts/lab-agent/` +(`lab-agent.env`), en vez de duplicar credenciales entre los dos +agentes del lab. + +Para probar el intervalo sin esperar 30 minutos, se puede levantar el +contenedor con `-e HEALTHCHECK_INTERVAL_SEC=60` (solo para pruebas en un +contenedor descartable — nunca cambiar esto en el `tmux-host` real sin +volver a 1800 después). + ## Qué sobrevive a qué | Evento | ¿Sobrevive la sesión tmux (`lab`)? | ¿Sobrevive el login de Claude Code? | |---|---|---| | Corte de SSH / cerrar la terminal / cambiar de dispositivo | Sí — es exactamente el caso que este setup resuelve | Sí | -| `docker restart tmux-host` (reinicio del NAS) | **No** — el proceso `tmux server` muere con el contenedor; `lab-session.sh` crea una sesión nueva automáticamente | Sí, vía el volumen montado en `/root/.claude` | -| `docker rm` + recrear el contenedor desde cero | No | Sí, vía el mismo volumen — no hace falta volver a autenticar ni reconfirmar el diálogo de confianza del folder (ver el mount de `claude-code-home.json`) | +| `docker restart tmux-host` (reinicio del NAS) | **No al toque, pero se auto-recupera solo** — el proceso `tmux server` muere con el contenedor; `healthcheck.sh` la recrea en el próximo ciclo (máx. 30 min) sin intervención, o `lab-session.sh` la crea al instante si alguien se reconecta antes | Sí, vía el volumen montado en `/root/.claude` | +| `docker rm` + recrear el contenedor desde cero | No, pero igual se auto-recupera sola (mismo mecanismo de arriba) | Sí, vía el mismo volumen — no hace falta volver a autenticar ni reconfirmar el diálogo de confianza del folder (ver el mount de `claude-code-home.json`) | +| Falla de red real del host/NAS | N/A | N/A — `healthcheck.sh` la detecta y notifica, pero no la corrige (fuera de su alcance) | ## Validación en vivo