fix: stop browsers running a cached noVNC against the desktop's own

The desktop failed to start in a browser that had cached noVNC 1.6
from the Debian desktop image: its vnc.html, which has no clipboard
Clear button, ran the Ubuntu image's 1.3 ui.js, which wires one up
and threw "Cannot read properties of null". websockify sends only
Last-Modified, so browsers cache noVNC on a guess, and it answers a
revalidation with 304 when the file on disk is older than the copy.

Serve noVNC through websockify-no-store, which adds Cache-Control:
no-store to every response, and from /novnc/ rather than /, so no
browser holds a stale copy at the paths the desktop now uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
vumas3r 2026-10-26 16:45:03 -06:00
parent 86671da15b
commit cf4f5be7ae
5 changed files with 37 additions and 3 deletions

View File

@ -4,6 +4,16 @@ Notable changes to kubestronaut-sim, newest first. A version is the `VERSION` st
(year.month.day.n); `./ksim help` and the top bar of the web page show the one you are running. (year.month.day.n); `./ksim help` and the top bar of the web page show the one you are running.
Every entry names the commit it came in. Every entry names the commit it came in.
## Unreleased
### Fixed
- The desktop could fail as it started - "noVNC encountered an error: Cannot read properties of
null (reading 'addEventListener')" - in a browser that had cached the Debian desktop's noVNC
1.6: its page ran the Ubuntu image's 1.3 `ui.js`. websockify now sends `Cache-Control:
no-store`, and noVNC moved to `/novnc/`, past what browsers hold at the old paths. Rebuild the
desktop (`./ksim build`) and restart `./ksim web`.
## 2026.10.19.1 — 2026-10-21 ## 2026.10.19.1 — 2026-10-21
Merged into `main` from `feat/lessons`. Everything below came in `0e5f768`, except the CKS Q103 Merged into `main` from `feat/lessons`. Everything below came in `0e5f768`, except the CKS Q103

View File

@ -29,7 +29,8 @@ RUN useradd -m -s /bin/bash -u 1000 candidate \
&& install -d -o candidate -g candidate /home/candidate/.config/xfce4/xfconf/xfce-perchannel-xml \ && install -d -o candidate -g candidate /home/candidate/.config/xfce4/xfconf/xfce-perchannel-xml \
&& install -m644 -o candidate -g candidate /etc/xdg/xfce4/panel/default.xml \ && install -m644 -o candidate -g candidate /etc/xdg/xfce4/panel/default.xml \
/home/candidate/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml \ /home/candidate/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml \
&& chmod +x /usr/local/bin/start-desktop && ln -s /usr/share/novnc /srv/novnc \
&& chmod +x /usr/local/bin/start-desktop /usr/local/bin/websockify-no-store
USER candidate USER candidate
WORKDIR /home/candidate WORKDIR /home/candidate
EXPOSE 6080 EXPOSE 6080

View File

@ -9,4 +9,6 @@ Xvnc :1 -geometry "${GEOMETRY:-1600x900}" -depth 24 -rfbport 5901 -localhost \
for _ in $(seq 50); do [ -S /tmp/.X11-unix/X1 ] && break; sleep 0.1; done for _ in $(seq 50); do [ -S /tmp/.X11-unix/X1 ] && break; sleep 0.1; done
cd "$HOME" cd "$HOME"
dbus-launch --exit-with-session startxfce4 >/tmp/xfce.log 2>&1 & dbus-launch --exit-with-session startxfce4 >/tmp/xfce.log 2>&1 &
exec websockify --web /usr/share/novnc 6080 127.0.0.1:5901 # noVNC is at /novnc/ (/srv/novnc), not /: browsers may still hold the Debian image's noVNC 1.6 at
# the old paths, cached before websockify-no-store.
exec websockify-no-store --web /srv 6080 127.0.0.1:5901

View File

@ -0,0 +1,21 @@
#!/usr/bin/python3
# websockify, with Cache-Control: no-store on every file it serves. On its own it sends only
# Last-Modified, so a browser keeps noVNC's files for months on a guess, and answers a revalidation
# with 304 whenever the file on disk is older than the one cached. When the image's noVNC changes
# (Debian's 1.6 to Ubuntu's 1.3), a cached page then runs the other version's ui.js and fails as it
# starts. Over loopback, fetching every file each time costs nothing.
import sys
from websockify import websocketproxy
handler = websocketproxy.ProxyRequestHandler
end_headers = handler.end_headers
def no_store(self):
self.send_header("Cache-Control", "no-store")
end_headers(self)
handler.end_headers = no_store
sys.exit(websocketproxy.websockify_init())

View File

@ -28,7 +28,7 @@ DOCS = {
def url(): def url():
return f"http://127.0.0.1:{PORT}/vnc.html?autoconnect=1&resize=remote&reconnect=1&reconnect_delay=1500&show_dot=1" return f"http://127.0.0.1:{PORT}/novnc/vnc.html?autoconnect=1&resize=remote&reconnect=1&reconnect_delay=1500&show_dot=1"
def running(): def running():