Volver a la lista

Blog

Por qué construimos Tactic Remote sobre tmux: persistencia de sesiones y arquitectura de terminal

Tactic Remote utiliza tmux para gestionar las sesiones de Claude Code, proporcionando persistencia ante desconexiones de red, ciclos de suspensión del Mac y reinicios de la app, mientras transmite la salida del terminal a tu iPhone en tiempo real.

Publicado Etiquetas: architecture / tmux / reliability / engineering
Blog

Sigue las novedades de producto e ingeniería de esta categoría.

Ver categoría

Cuando empezamos a construir Tactic Remote, la primera decisión arquitectónica fue fundamental: ¿cómo mantener una sesión de Claude Code viva y observable cuando el dispositivo de control (tu iPhone) tiene una conexión poco fiable? La respuesta fue tmux, y esa decisión moldeó casi todo lo demás del producto.

El problema

Claude Code se ejecuta como un proceso interactivo de larga duración. Una sesión típica dura entre 10 y 60 minutos. Durante ese tiempo, Claude lee archivos, propone cambios, ejecuta comandos, espera aprobaciones e itera sobre retroalimentación. Si el proceso muere a mitad de sesión, se pierde todo el contexto. Claude debe empezar de nuevo, releer archivos y reconstruir su comprensión de la tarea.

En un terminal local, esto no es un problema — tu Mac permanece encendido y el proceso se ejecuta hasta completarse. Pero Tactic Remote introduce un cliente móvil. Los iPhones cambian entre Wi-Fi y datos móviles. Bloquean la pantalla y suspenden apps en segundo plano. Los usuarios caminan por zonas sin señal. Cualquier arquitectura que vincule el ciclo de vida del proceso de Claude Code a la conexión de red del iPhone está fundamentalmente rota.

Necesitábamos que Claude Code se ejecutara independientemente de cualquier conexión de cliente y que los clientes pudieran conectarse y desconectarse sin afectar la sesión en ejecución.

Por qué tmux

tmux es un multiplexor de terminal en uso en producción desde 2007. Separa el concepto de sesión de terminal del emulador de terminal que la muestra. Una sesión tmux se ejecuta como un proceso servidor en la máquina host. Los clientes se conectan para ver e interactuar con la sesión, y se desconectan sin afectar los procesos en ejecución.

Evaluamos varias alternativas antes de decidirnos por tmux:

screen — El multiplexor de terminal original. Funcional pero con mantenimiento menos activo y una API de scripting más limitada. La interfaz de comandos estructurada de tmux facilitó significativamente el control programático.

Gestión de PTY personalizada — Prototipamos una solución usando pseudo-terminales con node-pty. Funcionaba para casos básicos pero nos obligaba a reimplementar persistencia de sesión, gestión de ventanas y buffering de salida. Estábamos construyendo un tmux inferior desde cero.

Aislamiento basado en contenedores — Ejecutar Claude Code en un contenedor Docker con un PTY conectado. Esto añade sobrecarga, complica el acceso al sistema de archivos (Claude Code necesita leer y escribir los archivos reales de tu proyecto) e introduce una capa de abstracción que crea más problemas de los que resuelve.

nohup / disown — Gestión simple de procesos en segundo plano. Sin captura de salida, sin reconexión, sin control programático. Inadecuado para sesiones interactivas.

tmux nos proporcionó todo lo necesario de serie: persistencia de sesión, control programático de paneles, captura de salida y una API estable y bien documentada. La contrapartida es que tmux debe estar instalado en el Mac del desarrollador, pero brew install tmux es un coste de configuración único que consideramos aceptable.

Arquitectura

La integración de tmux en Tactic Remote funciona en tres capas:

Capa 1: Gestión de sesiones

Cuando inicias una sesión de Claude Code a través de Tactic Remote, el compañero Mac crea una nueva sesión tmux con un identificador único. Claude Code se lanza dentro del panel principal de esta sesión.

tmux new-session -d -s claude-remote-{session-id} "claude-code --project /path/to/project"

El flag -d es fundamental — crea la sesión en modo desconectado. No se necesita un emulador de terminal conectado para que la sesión exista y se ejecute. Claude Code arranca inmediatamente y el compañero Mac lo monitoriza en segundo plano.

Cada sesión obtiene una sesión tmux dedicada en lugar de un panel dentro de una sesión compartida. Esto proporciona un aislamiento limpio: variables de entorno específicas de sesión, buffers de desplazamiento independientes y sin riesgo de interferencia entre sesiones.

Capa 2: Captura y streaming de salida

El compañero Mac captura continuamente la salida del terminal del panel tmux y la transmite a los clientes iPhone conectados vía WebSocket. Este es el mecanismo que permite ver la salida de Claude Code en tiempo real en tu teléfono.

La captura de salida usa tmux capture-pane con coordenadas que cubren el contenido visible del panel más el buffer de desplazamiento. El compañero ejecuta un bucle de captura que:

  1. Captura el contenido actual del panel a un intervalo configurable (por defecto: 200ms).
  2. Compara el contenido capturado con el último estado conocido.
  3. Envía solo la diferencia a los clientes conectados.
  4. Mantiene un buffer circular de salida reciente para clientes que se reconectan y necesitan ponerse al día.

El intervalo de captura de 200ms es un equilibrio entre capacidad de respuesta y uso de CPU. A esta tasa, las actualizaciones del terminal se sienten casi en tiempo real en el iPhone mientras consumen menos del 2% de CPU en un Mac moderno. Los usuarios que prefieran actualizaciones más rápidas pueden reducir el intervalo a 100ms; los que usan batería pueden aumentarlo a 500ms.

Capa 3: Enrutamiento de entrada

Cuando envías entrada desde el iPhone — ya sea una respuesta de aprobación, un mensaje escrito o un comando de control de sesión — el compañero Mac lo enruta al panel tmux apropiado usando tmux send-keys. Esto mantiene la ilusión de una conexión directa de terminal mientras la entrada real viaja por WebSocket, a través de la red y hacia la sesión tmux.

Las respuestas de aprobación son un caso especial. En lugar de enviarse como pulsaciones de teclas, son procesadas por el sistema de hooks e inyectadas como respuestas estructuradas que el sistema de permisos de Claude Code espera. Esto asegura que las aprobaciones sean atómicas y no puedan corromperse por entrega parcial de red.

Persistencia en la práctica

La arquitectura tmux proporciona persistencia a varios niveles:

La app del iPhone se cierra. La sesión tmux sigue ejecutándose. Claude Code continúa trabajando y las aprobaciones se encolan en el compañero Mac. Cuando reabres la app, se reconecta al WebSocket, recibe la salida almacenada y vuelves a donde estabas.

El iPhone pierde red. Idéntico al cierre de la app desde la perspectiva de la sesión. El WebSocket cae, la salida se almacena en el Mac, y la reconexión restaura el estado.

El Mac entra en suspensión. Las sesiones tmux sobreviven a ciclos de suspensión y activación. Cuando el Mac despierta, el proceso servidor tmux se reanuda y cualquier proceso de Claude Code que estuviera a mitad de ejecución continúa. El compañero Mac restablece el listener WebSocket y se reconecta al iPhone.

El compañero Mac se bloquea. La sesión tmux sobrevive independientemente. Reiniciar el compañero Mac detecta las sesiones tmux existentes con el prefijo claude-remote-, se reconecta a ellas y reanuda la transmisión. No se pierde contexto de Claude Code.

El Mac se reinicia. Este es el único escenario donde las sesiones tmux no sobreviven, porque existen solo en memoria. Claude Code debe reiniciarse. Lo documentamos claramente y estamos explorando la integración de tmux-resurrect para futuras versiones, aunque la complejidad de restaurar sesiones IA interactivas (no solo el estado del shell) lo hace no trivial.

Ciclo de vida de la sesión

Una sesión de Tactic Remote pasa por estos estados:

EstadoEstado tmuxEstado iPhone
IniciandoSesión creada, Claude Code arrancandoIndicador de carga
ActivaProceso en ejecución, salida fluyendoVista de terminal en vivo
Esperando aprobaciónProceso bloqueado en hookNotificación de aprobación
Pausada (cliente desconectado)Ejecutándose, salida en bufferNo conectado
CompletadaProceso terminado, sesión retenidaResumen de sesión
ExpiradaSesión destruida tras periodo de retenciónArchivada

Las sesiones completadas se retienen en tmux durante un periodo configurable (por defecto: 30 minutos) para que puedas revisar la salida final incluso después de que Claude Code termine. Tras el periodo de retención, la sesión tmux se destruye y el registro de salida se archiva.

Observabilidad

tmux nos da capacidades de introspección difíciles de lograr con una solución PTY personalizada:

  • Listado de sesiones — tmux list-sessions proporciona un inventario instantáneo de todas las sesiones activas de Tactic Remote.
  • Dimensiones del panel — Podemos consultar y establecer las dimensiones del panel para ajustarlas a las características de pantalla del iPhone, asegurando un ajuste de línea correcto.
  • Monitorización de procesos — El comando tmux list-panes con flags de formato permite monitorizar el proceso de Claude Code sin mantener un sistema separado de seguimiento de PIDs.
  • Acceso al buffer de desplazamiento — El buffer completo está disponible para búsqueda y recuperación, lo que alimenta la vista de historial de sesión en el iPhone.

Características de rendimiento

Ejecutar a través de tmux añade una sobrecarga mínima comparado con un terminal directo:

MétricaTerminal directoSesión tmux
Inicio del proceso~800ms~850ms
Latencia de pulsación<1ms~2ms
Sobrecarga de memoria por sesión—~3MB
Sobrecarga de CPU (sesión inactiva)—<0.1%

La sobrecarga es inmedible en la práctica. tmux ha sido optimizado durante casi dos décadas de uso en producción, y la indirección adicional que introduce es invisible para los usuarios.

Lecciones aprendidas

Construir sobre tmux fue la decisión correcta, pero no estuvo exenta de desafíos:

Casos extremos de codificación de terminal. La salida de Claude Code incluye ocasionalmente secuencias de escape ANSI que interactúan inesperadamente con la emulación de terminal de tmux. Mantenemos una capa de normalización que limpia la salida antes de transmitirla al iPhone.

Instalación de tmux en macOS. tmux no viene preinstalado en macOS. Consideramos incluirlo en el paquete pero decidimos no hacerlo — los desarrolladores generalmente prefieren gestionar su propia instalación de tmux, y el empaquetado crearía conflictos de versión para quienes ya lo tienen instalado.

Rendimiento de capture-pane a escala. Con muchas sesiones concurrentes, el uso de CPU del bucle de captura escala linealmente. Implementamos tasas de captura adaptativas que reducen la frecuencia para sesiones sin cambios recientes en la salida.

tmux es infraestructura que desaparece cuando funciona bien. La mayoría de los usuarios de Tactic Remote no saben ni les importa que tmux esté involucrado — lo que ven es una sesión fiable que sobrevive a lo que sea que hagan su teléfono o su red. Ese era el resultado que buscábamos.

Prueba Tactic Remote

Controla tus agentes de programación desde el móvil

Conéctate a Claude Code, Codex y otros agentes en tu Mac, Windows o Linux. Consulta el progreso y envía la siguiente instrucción desde el iPhone o iPad.