a fixed stage for the game, and close the VNC port nobody meant to open

── the stage ──

The framebuffer is now the picture. 800x600 whatever the difficulty, the game
centred in it by the sidecar, and the panel draws it entire — scaled to fit,
centred by its flex parent, never cropped. Changing difficulty moves the board
instead of resizing the panel.

What this replaces parked the window at the origin and had the client crop to
the window's size, polled every four seconds. Two things wrong with it. The
panel resized under the player mid-game; and wine puts the window at 3,29
rather than 0,0, so cropping from the origin showed a black strip on top and
cut a row of cells off the bottom — the "expert gets cut off" report.

Centring runs on a one-second timer because nothing announces a resize: not
RFB, which keeps the framebuffer, and not X without a window manager to listen
through. One chained `xdotool search … getwindowgeometry` per tick, one
process, and a `windowmove` only when the numbers actually changed.

The client loses the geometry poll, the offscreen canvas and the crop with it.

── the port ──

x11vnc was listening on 0.0.0.0:5900 and [::]:5900 with -nopw. Every account
on this machine, every device on the tailnet and everything on the LAN could
open it and take the display — which is precisely the hole the 5907 version
was replaced to close, reopened wider and documented as impossible.

`-unixsock` ADDS a unix socket. It does not remove the TCP one. Three flags do:
-rfbport 0 for IPv4, -rfbportv6 0 for IPv6, -noipv6 for outgoing. `-rfbport 0`
alone leaves [::]:5900 up, which is how this nearly shipped half-fixed.

Found with `ss -lntp` on the running process, and that is the only check that
was right about it at any point. Running the same argv by hand showed no IPv6
listener — most likely because the PM2 session still held [::]:5900 and the
hand-run copy failed to bind it silently. Comments in session.ts and index.ts
that asserted there was no port are corrected rather than deleted.

Verified on the live socket: the streamed framebuffer is 447k teal pixels
around a 152x214 window at 324,193 — dead centre — and window grey arrives as
c0 c0 c0, no TCP listener for the x11vnc pid.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: de3ed4e6-f88a-429c-94e6-a760336ba83b
This commit is contained in:
2026-08-23 21:17:12 +00:00
co-authored by Claude Opus 5
parent 76b6a9c567
commit e6cec953e4
5 changed files with 226 additions and 112 deletions
+25 -4
View File
@@ -44,10 +44,31 @@ The download needs a browser `User-Agent`; the site answers `403` to curl's defa
## Two things worth knowing before you change anything
**x11vnc has no password, and that is correct.** It binds a unix socket inside `run/`, not a port. The
kernel refuses every other account before x11vnc is asked anything, so a password would be a second lock
on a door only one person can reach. The version this replaced bound `127.0.0.1:5907` with `-nopw`, which
is a different claim entirely — every local account could open it and take the display.
**x11vnc has no password, and that is correct — but only because it also has no port.** It binds a unix
socket inside `run/`, mode 0700. The kernel refuses every other account before x11vnc is asked anything,
so a password would be a second lock on a door only one person can reach. The version this replaced bound
`127.0.0.1:5907` with `-nopw`, which is a different claim entirely — every local account could open it and
take the display.
**`-unixsock` does not remove the TCP listener, and the first alpha shipped believing it did.** It adds a
unix socket beside the port; x11vnc was on `0.0.0.0:5900` and `[::]:5900` with no password, reachable from
the whole tailnet and the whole LAN, under a comment saying there was no port at all. Three flags close it
and each one earns its place: `-rfbport 0` for IPv4, `-rfbportv6 0` for IPv6 — `-rfbport 0` alone leaves
`[::]:5900` up — and `-noipv6` for outgoing sockets.
The check that matters is `ss -lntp` against the **running** process. Reading the arguments cannot show a
listener that exists because a flag is missing, and running the same arguments by hand did not reproduce
it either.
**The game is centred on a fixed stage, and the whole stage is what you see.** The framebuffer is 800x600
whatever the difficulty, `sidecar/session.ts` keeps the window in the middle of it, and the panel draws the
framebuffer entire — scaled to fit, never cropped. So changing difficulty moves the board rather than
resizing the panel, and the surround is teal (`-solid`) rather than the root's black.
It used to park the window at the origin and have the client crop to the window's size, polled every four
seconds. That resized the panel under the player, and it was off by the 3x29 wine puts between the root
origin and the window — a black strip along the top and a row of cut-off cells along the bottom, worst on
expert. `GET /status` still reports the window geometry; nothing consumes it now, it is a diagnostic.
**The wine prefix is confined to `C:` on every start.** A fresh prefix maps the whole filesystem to `Z:`
and symlinks the profile into the real home, and Minesweeper X has Open and Save dialogs for its replays.