Claude Desktop headless por VNC (dispatch, vm-playground)
Claude Desktop corre en una sesión gráfica virtual persistente en vm-playground — no en la sesión física de ningún monitor. El objetivo no es que Esteban lo use como app de escritorio normal: es que la función de dispatch de Claude Desktop tenga una ventana de navegador real donde clickear y leer pantalla para operar web apps (Odoo en particular), disponible siempre, independiente de si alguien está sentado frente al equipo.
2026-09-01: el setup gemelo en pop-os (t640-pop) se desmontó por completo — se pararon, deshabilitaron y borraron los 5 servicios systemd (xvfb-claude, openbox-claude, tint2-claude, x11vnc-claude, claude-desktop-vnc) y sus configs (~/.config/openbox, ~/.config/tint2, ~/.vnc). Motivo: pop-os pasó a usarse solo con su sesión física real (COSMIC), y mantener dos sesiones X11 virtuales — una de ellas sin respaldo real de uso — no se justificaba. El paquete apt claude-desktop y sus datos de sesión (~/.config/Claude, login incluido) se dejaron intactos en pop-os por si se necesita usar la app ahí en algún momento, fuera de este esquema headless. vm-playground queda como el único host con este patrón.
Por qué una sesión virtual y no la del monitor físico
Compartir la sesión real (COSMIC, Wayland) habría atado dispatch a que el monitor físico esté encendido y sin bloquear, y expuesto la actividad del agente en la pantalla que Esteban usa normalmente. En vez de eso, el host corre una sesión X11 aparte que arranca con el sistema (systemd, WantedBy=multi-user.target) y sigue viva sin importar quién se conecte o se desconecte:
Xvfb :1— framebuffer virtual puro por software, sin GPU. Se eligió por encima de un enfoque Wayland/wayvncporque una sesión headless persistente sobre COSMIC (compositor propio, joven) no está bien soportada ni documentada; Xvfb + X11 es el patrón estándar y maduro para esto.- Openbox — gestor de ventanas mínimo, con
~/.config/openbox/rc.xmlforzandomaximized=truey posición(0,0)en toda ventana nueva, para que Claude Desktop siempre ocupe el espacio completo disponible (descontando el panel). - lxpanel — ver sección aparte.
claude-desktop— el binario del paquete apt oficial, lanzado conDISPLAY=:1.x11vnc -display :1 -forever -shared -rfbauth ...— sirve esa sesión por VNC en el puerto5901, sin cerrarse nunca al desconectarse un cliente (a diferencia del modelo por defecto de xrdp, que crea y destruye una sesión por cada login).
Las cinco (con el panel) son unidades systemd independientes con dependencias explícitas (After=/Requires=) para que el orden de arranque sea determinista tras un reinicio del host.
Por qué VNC y no RDP
La ventaja habitual de RDP sobre VNC — negociar la resolución con el cliente al conectar — no aplica aquí: la resolución de Xvfb queda fija desde que se crea la pantalla virtual, antes de que exista ninguna conexión. Con la resolución ya fija de antemano, x11vnc es más simple y maduro que dejar xrdp reconectando a una sesión existente (su modelo por defecto es crear sesión nueva por login, no persistir una).
Resolución: 1920×1080, no la del teléfono
La sesión se pensó primero para verse bien en la pantalla plegable abierta de un Honor Magic V2 (2156×2344), pero se cambió a 1920×1080 porque quien realmente “mira” esta pantalla la mayor parte del tiempo es dispatch, no un ojo humano — y Odoo, como casi cualquier web app, cambia a layout tablet/mobile (menús colapsados, columnas ocultas) por debajo de ciertos anchos. Un aspect ratio atípico como el del teléfono arriesgaba disparar esos breakpoints y que el agente viera una versión reducida de la interfaz. 1920×1080 es la resolución de escritorio estándar sobre la que Odoo está pensado y probado, y de paso reduce el tamaño de cada captura de pantalla que dispatch procesa. Cuando Esteban se conecta manualmente por VNC desde el cel, el cliente escala la imagen igual — se ve más chica pero completa, no recortada.
Panel de barra (touch, sin teclado)
El host se opera por VNC desde el cel (touch, sin atajos de teclado cómodos), así que no depende de keybindings de Openbox para lanzar apps. Usa lxpanel (reemplazó a tint2, que se probó primero en un setup gemelo que ya no existe — ver nota de 2026-09-01 arriba). Trae un botón de menú “Inicio” con categorías desplegables de todas las apps instaladas — 100% clic/touch, sin necesidad de escribir nada en un buscador tipo rofi. Se prefirió sobre tint2 + rofi porque en este host además hay que poder abrir Chromium para automatización de navegador, y el menú clásico de lxpanel cubre ese caso sin agregar una segunda pieza (rofi).
El panel se declaró como servicio systemd propio (lxpanel-claude.service) con Environment=XDG_DATA_DIRS= incluyendo /var/lib/snapd/desktop — necesario porque Chromium se instaló vía snap y su .desktop vive fuera de las rutas XDG por defecto, así que ni el launcher ni el menú lo encontraban sin esto.
vm-playground: el botón de menú estuvo muerto desde que se creó (2026-08-25 a 2026-09-05)
El Plugin { type=menu } de ~/.config/lxpanel/default/panels/panel traía un bloque extra system-menu-item-toggle-visible { name=Terminal Emulator cmd=xterm }, con la intención de agregar un lanzador directo de terminal dentro del menú. Ese nombre de bloque no existe en el parser de configuración de lxpanel 0.10.1 — el log de lxpanel-claude.service marcaba menu: unknown block system-menu-item-toggle-visible, menu: plugin init failed, lxpanel: can't load menu plugin en cada arranque, y cuando el plugin de menú falla al inicializar, lxpanel igual dibuja el botón con el ícono configurado (image=start-here) pero sin ningún handler de clic detrás — visualmente parece un launcher normal, no hay ningún error visible en la barra, y solo se nota que está roto al hacer clic y ver que no pasa nada. Confirmado con capturas de pantalla (import -window root sobre DISPLAY=:1) y clics simulados con xdotool: el ícono de menú no abría nada, mientras que el botón de Chromium al lado sí respondía. Además xterm nunca estuvo instalado en este host, así que ese bloque tampoco habría funcionado aunque el nombre fuera válido.
Fix: se quitó el bloque inválido, dejando el plugin de menú en su forma mínima soportada (image=start-here + system {}), y el lanzador directo de terminal se resolvió agregando un segundo Button al plugin launchbar ya existente, apuntando a zutty.desktop (el emulador de terminal real instalado en este host, no xterm/lxterminal). Tras el fix, el botón de menú abre el árbol de categorías (Accessories, Graphics, Internet, Programming, Sound & Video, System Tools, Preferences) con clic normal, verificado con captura de pantalla.
Lección para configs de plugins de lxpanel: un bloque de nombre desconocido dentro de Config { } no se ignora ni genera un error visible en la UI — tumba la inicialización de todo el plugin en silencio. Cualquier bloque custom que no sea system, separator o item (sintaxis estándar del plugin menu de lxpanel) debe probarse contra el log de journalctl del servicio antes de darlo por bueno.
Navegador para automatización real (Claude in Chrome)
Dispatch necesita un navegador Chromium-based con la extensión Claude in Chrome para poder automatizar clicks/lectura de pantalla — la extensión no existe para Firefox.
vm-playground no tenía ningún navegador. Se instaló Chromium vía snap (el paquete .deb clásico está descontinuado en Ubuntu 24.04 ARM64; chromium-browser en apt es solo un stub que reinstala el snap) y se fijó como default en ~/.config/mimeapps.list.
Pendiente (requiere login manual de Esteban, no automatizable): instalar la extensión Claude in Chrome desde la Chrome Web Store dentro de esa sesión Chromium, e iniciar sesión en Claude Desktop con la cuenta de Anthropic.
Acceso remoto
El host expone su x11vnc en el puerto 5901, alcanzable por la IP de NetBird del host (ver Hosts) sin necesidad de túnel adicional — evita exponer el puerto a internet público. La contraseña de x11vnc vive únicamente en ~/.vnc/passwd de vm-playground (usuario ubuntu), fuera de esta wiki.
Repo de referencia para replicar en un host nuevo
Los cinco archivos de unidad systemd (xvfb-claude, openbox-claude, lxpanel-claude, claude-desktop-vnc, x11vnc-claude) de vm-playground sirven de plantilla para un host nuevo — cambiaría el usuario del sistema, la arquitectura del paquete apt (arm64/amd64) y, si el navegador no viene de snap, el XDG_DATA_DIRS extra.
Véase también
- NetBird — malla que expone el puerto 5901 sin túnel público
- Hosts — specs y rol de vm-playground
- Claude Code remoto — visualizar outputs (Rich) desde el cel — mismo problema de fondo (acceso móvil a algo que corre en un servidor), solución distinta porque ahí no hace falta una sesión gráfica completa