What it is
Locked-down public-library kiosk sim — ticket/HKID auth against an in-page database, nine working apps, guest-mode browsers that block chrome:// and settings, a floating control bar (pause/zoom/logout), and a hard wipe on logout. One self-contained 72 KB HTML file, the web fallback for a 'modified Windows 11 ISO' request.
Version history
Straight from the project’s own card data — what each version added.
1236 lines — lock screen, 8 apps, kiosk toolbar, hidden admin panel
996 lines — session timer, on-screen keyboard, print dialog, catalog search, high-contrast
~1115 lines — right-click context menu; fixed rendering and browser-start bugs
671-line clean rewrite — Windows 11 start menu, window snap, all handlers inline, node --check clean
Files on the site
What broke, and what I’d change
Not written yet. This is the part only I can write — what actually went wrong, and what I’d do differently. It lives in builds/reflections/library-kiosk.md (see _TEMPLATE.md); as soon as that file has words in it, this section fills in on the next build.
Everything else on this page — the spec, the version history, the file counts — is generated from the project’s own build notes and files, so nothing here is guesswork.
Build spec
The project’s own build notes, rendered. Open the raw .md ↗
Show the spec
Build spec — Library Kiosk (locked-down public-library terminal)
This is the web fallback for a "modified Windows 11 ISO" request. A real Windows image was not built;
this single-file HTML kiosk delivers the requested behaviour — ticket/HKID unlock, guest browsers, floating
control bar, wipe-on-logout — in something a library can deploy in seconds.
Goal. One self-contained HTML file that behaves like a Hong Kong public-library kiosk terminal: a lock screen that accepts a one-time ticket number or an HKID, a Windows-11-style desktop with working apps, guest-mode browsers that refuse settings/chrome internals, a floating control bar (pause / zoom / logout), and a hard wipe of every patron's data on logout.
Hard requirements
- Single file, no build step, no external dependencies. Must open from
file://and a USB stick. - Two equal auth paths: ticket (matched + consumed, so a photocopied ticket dies) and HKID
- Guest-mode browsers block
chrome://,edge://,about:config,about:supportbefore navigation and - Logout wipes everything: DOM nodes removed (not hidden), iframes/editors/canvas destroyed, zoom and timer
- Floating control bar: Pause Screen, Zoom 100/150/200%, Zoom Out, on-screen keyboard, high-contrast, help,
- Hidden admin panel (⚙️ Admin link + 5-click corner trigger, password-gated): Tickets, HKID, Catalog,
- Every event handler inline (
onclick/onkeydown), noaddEventListeneron elements, nopostMessage.
(case/whitespace-insensitive, greets the patron by name).
re-check the iframe's location.href after load so an in-page link can't sneak a restricted URL past the bar.
reset, back to the lock screen. Nothing survives a session.
red Log Out.
Announcements, Usage Logs, Settings — persisted to localStorage.
These three choices were forced by real breakage in earlier passes.
State shape
D = { tickets:[], hkids:[], catalog:[], announcements:[], logs:[] } // data, persisted
S = { user, zoom, paused, remaining, printUsed, … } // session, wiped on logout
W = { z: 40, open:{} } // window manager
K = { shiftOn, capsOn } // on-screen keyboard state
A = { … } // admin panel
One object per concern; logout() tears down W.open, resets S and K, clears the DOM, returns to the lock screen. Counts are rendered from their arrays, not hard-coded.
Verification
node --check /Users/isaac/Desktop/library-kiosk.html # 0 syntax errors
shasum -a 256 ~/Desktop/library-kiosk.html demos/library-kiosk-mirror/index.html # must match
Then exercised in-browser: ticket unlock (and that the ticket is consumed on retry), HKID unlock by name, blocked-URL refusal, DuckDuckGo search, print dialog, catalog search, zoom reflow, on-screen keyboard, admin login + all six tabs, and logout leaving no window/iframe/editor content behind.
Bugs this build actually hit (and fixed at the cause)
let oskCaps+function oskCaps()in the same scope →SyntaxErrorkilled every handler. Renamed toaddEventListeneron elements → moved to inlineonclick/onkeydown.srcdocHTML-escaped (</>) → browser start page rendered as literal text. Only escape", leave tags raw.postMessagebridge never fired → start page navigates the iframe directly.- Live function reference inserted into
innerHTML→ call the body builder at render time.
K.capsOn / K.caps() (and the same for oskShift).
Honest limits (stated in the artifact and on the analysis page)
- Browsers cannot load Google/Wikipedia (they send
X-Frame-Options: DENY); only DuckDuckGo/Bing render in-frame. - "Data wipe" is in-memory/
localStorage, not forensic — a devtools user can read the database. - A web page cannot lock the OS; for real enforcement pair this UI with Assigned Access / kiosk mode.
File map
demos/library-kiosk.html the kiosk (single file) demos/library-kiosk-mirror/index.html byte-identical mirror demos/library-kiosk-analysis.html long-form analysis