New historyButtonsOpacity setting (10-100 percent, default 50) in the
terminal menu (Display & Interaction) and the Web UI. Upgrade now also
copies the Web UI app to /opt/kiosk-webui and restarts it, so new Web UI
settings appear after an upgrade (it previously kept its old copy).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZQveYdjfcrEsyazq32
Alt+Left/Right now go back/forward within the page (the standard browser
keys) instead of switching sites; sites switch with Ctrl+Tab, Ctrl+[ ] and
the standard Ctrl+PageUp/PageDown. New enableHistoryButtons option (off by
default) shows half-transparent back/forward buttons at the top middle.
Settable from the terminal menu and the Web UI.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZQveYdjfcrEsyazq32
provision_configure_display always installed 10-serverflags.conf with
DontVTSwitch=true, so Ctrl+Alt+F1-F8 never worked on a fresh install
despite the installer saying consoles are enabled by default, and every
Upgrade overwrote what Advanced -> Virtual Consoles had set.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZQveYdjfcrEsyazq32
The kiosk app can end up with the no-new-privileges flag, after which sudo
refuses to run ("The no new privileges flag is set"). The helper is now
served over a root-owned, kiosk-group-only unix socket (systemd socket
activation); sudo stays as a fallback. With several WiFi adapters the screen
shows a dropdown and lists all of them; status/scan/connect take an iface.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZQveYdjfcrEsyazq32
A 'Scan with camera' button opens the phone's own camera app; the photo is shrunk
on the phone and read on the server with zbarimg, then goes through the normal add
flow (also available on the sticker step). Works over plain HTTP on iPhone and
Android - no HTTPS needed.
- /api/scan: JPEG/PNG only (checked by magic bytes before ImageMagick sees it),
12 MB cap, login required, 20 s timeout, temp files cleaned up
- UPC-A reads (zbar reports 13 digits with a leading zero) normalised to the 12
digits printed on the item so duplicate checks match
- tests/gen_photos.py + tests/check_scan.py: phone-like photos (tilt, blur, noise,
low light, upside down, EXIF rotation, small, UPC, Code 39 sticker); 10/10 read
at the size the page sends
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
A '|| true' at the end of the install chain swallowed any earlier failure, so a
transient/blocked apt step produced a successful build with no Koha in it (found
when an update rebuilt without the proxy CA). Steps are now separate, memcached and
cron are installed explicitly, and a final step refuses to produce an image that is
missing koha-create, memcached, cron, apache2ctl, zebrasrv, ... Quick Add's image
verifies yaz and python3 the same way.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
- OPAC_URL / STAFF_URL let Quick Add build correct links behind a reverse proxy
- restore the Catalogue/Staff header links (lost when the init block was
rewritten) and verify them in the browser test
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
Quick Add:
- barcode choices: automatic (zero-padded, >= 7 digits), scan a pre-printed
sticker per item, or none for now; bulk 'give barcodes to items without one';
reserve a block of numbers and print the sheet ahead of time (link opens
Koha's range printer prefilled)
- default shelf saved on the server (shared by every device)
- item types follow Koha; CSP allows cover redirects; aria attrs fixed
Koha bootstrap:
- default lending rule (3 weeks, 25 items, 3 renewals, no fines) - a fresh
Koha refuses every checkout without one
- automatic library-card numbers (start at 1000001), fines off
- Koha's own auto-barcode off (one numbering scheme)
- patron card template (Avery 5371) and layout with a scannable barcode
- LIBRARY_CODE, SHELVES and ITEM_TYPES honoured at first install
Why 7 digits: decoded printed sheets - 7-digit Code 39 read back 40/40, 6-digit
all-numeric symbols were reinterpreted as Code 32 for ~1 in 10 values.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
- Quick Add: choose the shelf/room (Koha LOC values) once; every scanned item
gets it and the choice is remembered; invalid values are ignored
- install.sh --status shows localhost in loopback mode; port probing works
without ss
- library/README.md; kiosk Readme.md add-on section and licenses
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
- BIND_ADDR (default 0.0.0.0) on every published port; 127.0.0.1 keeps the
stack reachable from the host only (Docker bypasses ufw, so this is the
actual control the kiosk addon's 'this kiosk only' choice uses)
- Koha serves its web-installer page with HTTP 200 before the database
exists, so 'port answers' was not 'ready': the entrypoint now writes
/run/koha-ready, the healthcheck requires it, and install.sh waits for
healthy + the quick-add health endpoint
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
- lib/docker.sh: shared Docker/Compose tooling (on-demand install via apt or
get.docker.com, compose wrapper, container state/health, HTTP wait)
- menus/addon_library.sh: install/update/uninstall the Koha + Quick Add stack
from library/ to /opt/kiosk-library, add the catalogue as a kiosk page
(duration 0), choose kiosk-only vs LAN access, show addresses and login
- wire into install.sh menu, complete_uninstall and clone_settings
- tests/addon_library.test.sh: 41 checks against a stub sudo/docker and a
fixture stack (no root, Docker or network needed)
Note: the 'network access' choice relies on a BIND_ADDR setting in the
library stack, added in a follow-up commit.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
A small standard-library Python service next to Koha:
- looks the code up via Z39.50 (yaz) then Open Library over HTTPS
- checks Koha for an existing copy (ISBN-10/13 and UPC/EAN aware)
- adds the record + an item with the next barcode via Koha's REST API
- scanner-friendly UI: auto-focus, audible feedback, duplicate card with
'add another copy', manual entry when nothing is found, undo, and the
barcode range to print labels for
Also: enable RESTBasicAuth, map 024$a to the ean column, wire the service,
port and credentials into compose/install.sh.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
- Seed Avery 5160/5167 and Demco spine templates plus Spine, Spine+barcode
and Barcode-only layouts (Dewey call number split across lines)
- Default 942$c (item type) to BOOK so a Z39.50 import saves without edits
- Enable Plack includes in addition to the plain ones (static files/rewrites)
- Swap Apache from mpm_itk to prefork after koha-create (itk can't switch
users in a container and returned HTTP 500 on every page)
- Drop unneeded SETUID/SETGID caps
Verified live: staff login, Z39.50 search/import against a local server,
save + auto barcode, label PDF with Dewey spine + barcode.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
- koha-shell -c wraps the command in single quotes, which broke the env
assignments; use runuser + env instead and verify tables were created
- koha-create requires mpm_itk, so keep it and grant the caps it needs
- stop any Apache started by koha-create before running it in foreground
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
- Run Apache as the instance user instead of mpm_itk AssignUserID
- Add Listen for the staff port; start zebra/indexer/worker/plack explicitly
- Tolerate koha-plack --enable returning non-zero under set -e
- install.sh: only fail on a crash loop, not a single restart
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
Compose stack (MariaDB + Koha built from the Koha project's Debian
packages), headless bootstrap replacing the web installer, seeded item
types and LoC Z39.50 server, and an install.sh that installs Docker,
generates passwords and waits for the site. End-to-end testing in progress.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wi3ZSot4XbUViwRFgLpRf
Unattended installs on an --offline ISO cap the online attempt (default 40 min)
and finish from the bundle on timeout or failure. Interactive retry menu gains
[O] to install from the bundle. NodeSource curl gets timeouts.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A97KUggSE3rBL8veDM5Mr4
kiosk-wifi-helper gains region-status/region-set. The country is persisted as
a cfg80211 regdom module option and in the WiFi netplan file; locale and
keyboard go through localectl, validated against what's installed.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A97KUggSE3rBL8veDM5Mr4
Without a country Linux uses the restrictive world domain: networks show up
in scans but the adapter can't join them. The menu asks (default from the
existing config or system locale); the web helper uses the same default.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A97KUggSE3rBL8veDM5Mr4
- Menu asks which adapter when several exist (shows USB/PCI); the web UI
helper prefers the adapter already configured.
- Stale-entry cleanup also marks remaining installer wifis optional so a
dead WiFi entry can't hold up boot.
- After a failed connect, read wpa_supplicant's log to say wrong password /
network not found / rejected.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A97KUggSE3rBL8veDM5Mr4
netplan apply waits ~90s for every wifis: interface in every file and then
fails if one doesn't exist (e.g. installer config naming wlan0 while the
adapter is wlp1s0). Remove such entries (backup kept as *.kiosk-bak) before
applying, in both the menu and kiosk-wifi-helper.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A97KUggSE3rBL8veDM5Mr4
The WiFi screen now opens with Ctrl+Alt+Super+W, which is easier to
press than the five-key chord. The Shift+W+S+F+H chord and
Ctrl+Alt+Shift+Super+W are removed. Fn isn't part of the combo: most
keyboards never send it to the computer.
With password protection on, the screen asks for the unlock code: the
PIN pad for a PIN, a password field for a password. It shares the
lock screen's wrong-code slowdown. With protection off, it falls back
to the hidden-sites PIN as before. The lock screen's check now goes
through the same checkCode() helper.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cjz8a58eB5P9sUweqd3NHm
.jitsi-pin now holds "sha256:<hex>" (or NOPIN) instead of the digits.
The menu writes the hashed form. A plain-digits file still works and
is converted by the kiosk app at startup.
The PIN is now checked in the main process instead of the PIN pad's
renderer, using the lock screen's slowdown, now factored out
(createCodeThrottle): 5 wrong in a row refuse entry for 30s, doubling
up to 5 minutes. The F10 PIN pad and the WiFi screen share one count.
The WiFi screen's close-after-5-tries is replaced by this. The PIN pad
no longer shows the default PIN on screen, and the menu no longer
echoes the PIN as it's typed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cjz8a58eB5P9sUweqd3NHm
The unlock code and the boot lock code can each be a password or a
4-8 digit PIN. A PIN gets an on-screen number pad, so the lock works on
a touch-only kiosk. The boot lock can use the unlock code or its own.
These are the kiosk's own codes, set in Core Settings -> Password
Protection & Lockout or on the Web UI's Lockout page, never the admin
(sudo) password.
The lock screen moves from an inline data: URL to lockscreen.html. The
code is now hashed and compared in the main process, which also slows
down guessing: after every 5 wrong codes in a row, input is refused for
30s, doubling up to 5 minutes. Existing configs behave as before:
missing fields mean a password, and the same code at boot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cjz8a58eB5P9sUweqd3NHm
With the virtual consoles turned off there was no way to join a WiFi
network at the kiosk itself. Holding Shift+W+S+F+H together (or
Ctrl+Alt+Shift+Super+W) now opens a PIN-protected WiFi screen: scan,
pick or type a network, enter the password, connect.
The root side is a small helper (kiosk-wifi-helper: status, scan,
connect only) that the kiosk user may run through a validated
sudoers.d rule. It writes the same netplan file as Core Settings ->
WiFi and puts the previous one back if the new network gives no
address. Installed by provisioning and Advanced -> Upgrade, removed by
Complete Uninstall.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cjz8a58eB5P9sUweqd3NHm