Update CLAUDE.md for ich777/novnc-baseimage switch

CLAUDE.md still described the old jayfield/novnc-baseimage layout
(/opt/TurboVNC, /tmp/websockify, /tmp/noVNC) and omitted the stale-lock
cleanup step added to server.sh.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZjg4yXCYacriPz3euenQF
This commit is contained in:
2026-07-27 23:14:41 +02:00
co-authored by Claude Sonnet 5
parent 84cf0e3fdc
commit 4a308711b4
+7 -6
View File
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## What this is
A Docker image definition for a headless X server exposed over noVNC (VNC-over-websockets in the browser) plus raw X11-over-TCP. It builds on the external base image `jayfield/novnc-baseimage`, which already provides TurboVNC, websockify, and noVNC — this repo only adds the window manager, a TLS cert for noVNC, and the entrypoint/lifecycle scripts.
A Docker image definition for a headless X server exposed over noVNC (VNC-over-websockets in the browser) plus raw X11-over-TCP. It builds on the public base image `ich777/novnc-baseimage`, which already provides TurboVNC, websockify, fluxbox, and noVNC — this repo only adds `net-tools`/`socat`, a TLS cert for noVNC, and the entrypoint/lifecycle scripts. Published as `jayfield/xserver-novnc` on Docker Hub.
## Commands
@@ -26,7 +26,7 @@ There's no automated way to exercise `scripts/server.sh` / `scripts/server_start
## Architecture
**Image layering** (`Dockerfile`):
- Base image supplies TurboVNC (`/opt/TurboVNC`), websockify + noVNC (`/tmp/websockify`, `/tmp/noVNC`).
- Base image supplies TurboVNC (`Xvnc` on `$PATH`), websockify (`websockify` on `$PATH`), and noVNC's web client at `/usr/share/novnc/` — all installed at standard FHS paths via Debian packages, not under `/opt` or `/tmp`.
- This layer adds `net-tools` and `socat`, generates a self-signed TLS cert/key at build time (`/tmp/novnc.pem`, `/tmp/novnc.key`) for noVNC's HTTPS/WSS listener, and copies `scripts/` to `/opt/scripts/`.
- `ENTRYPOINT` is `/opt/scripts/server_start.sh /opt/scripts/server.sh` — the signal-handling wrapper invoked with the real startup script as its argument.
@@ -40,9 +40,10 @@ There's no automated way to exercise `scripts/server.sh` / `scripts/server_start
`create.sh` and `run.sh` hardcode `DISPLAY_NUM=99` and map host ports accordingly (`5910:5999` for VNC, `8099:8099` for noVNC); update these together if `DISPLAY_NUM` ever changes.
**Startup sequence** (`scripts/server.sh`, run inside the container):
1. Start `Xvnc` (TurboVNC's X/VNC server) on `$DISPLAY` at `${SCREEN_W}x${SCREEN_H}x24` with security disabled (`-securitytypes none` — auth is expected to happen at the noVNC/TLS layer or via network isolation, not VNC auth).
2. Start `fluxbox` as the window manager against that display.
3. Start `websockify` in the foreground (`-D` daemonizes websockify itself, but the script's own process still ends up blocking via the wrapper below), serving noVNC's web client and proxying to the local VNC port, using the cert generated in the Dockerfile.
4. Start `socat` to bridge the X11 Unix socket to TCP.
1. Remove any stale `/tmp/.X${DISPLAY_NUM}-lock` and `/tmp/.X11-unix/X${DISPLAY_NUM}` before starting Xvnc. `/tmp` survives `docker stop`/`docker start` on the same container, and Xvnc doesn't get a chance to clean these up when killed, so without this a restart fails to rebind the display.
2. Start `Xvnc` (TurboVNC's X/VNC server) on `$DISPLAY` at `${SCREEN_W}x${SCREEN_H}x24` with security disabled (`-securitytypes none` — auth is expected to happen at the noVNC/TLS layer or via network isolation, not VNC auth).
3. Start `fluxbox` as the window manager against that display.
4. Start `websockify` in the foreground (`-D` daemonizes websockify itself, but the script's own process still ends up blocking via the wrapper below), serving noVNC's web client and proxying to the local VNC port, using the cert generated in the Dockerfile.
5. Start `socat` to bridge the X11 Unix socket to TCP.
**Signal handling** (`scripts/server_start.sh` + `scripts/signals.sh`): Docker sends `SIGTERM` to PID 1 on `docker stop`. `server_start.sh` traps `SIGTERM`, forwards it to the child process tree, waits for the child's descendants to exit, and re-raises the correct exit code (`128 + signal number`) so the container's exit status reflects how it was stopped. When touching this wrapper, keep in mind it's generic (`$APP` is passed in as an argument) — it isn't specific to `server.sh` and could wrap a different entrypoint script.