Files
senior-kiosk/CLAUDE.md
T
jgitta 8553705e47 Fix bookmarks menu dismiss, home Email URL, and deploy hygiene.
Close the hamburger panel on outside click/focus via X11 (xinput/xdotool)
without a fullscreen overlay; point home Email to Roundcube; sync
home-screen-index.html in deploy/install; remove credentials.md from git
and document private LAN-only Gitea usage.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 21:39:07 +00:00

6.7 KiB
Raw Blame History

SeniorNet Kiosk — Project Context for AI Assistants

What this project is

A locked-down, zero-maintenance browser workstation for elderly users. The senior never sees a desktop — only a full-screen Chromium window with a slim toolbar on top. No configuration, no maintenance required from the user.

Architecture

┌─────────────────────────────────────────────────────┐
│  PyQt5 toolbar (72px, always-on-top, FramelessHint) │
│  [SeniorNet]  [◀] [▶] [⌂] [☰ bookmarks]            │
├─────────────────────────────────────────────────────┤
│                                                     │
│           Chromium --app mode                       │
│       (CDP port 9222 for navigation)                │
│                                                     │
└─────────────────────────────────────────────────────┘
  • browser.py — main app. Launches both the PyQt5 toolbar and Chromium as a subprocess.
  • Toolbar talks to Chromium via Chrome DevTools Protocol (CDP) over WebSocket on port 9222.
  • Bookmarks stored in bookmarks.json (nested JSON, supports folders).
  • Home screen: /home/jgitta/kiosk-home/index.html

Machines

kiosk-dev — 192.168.88.48 (SSH alias: kiosk-dev) ← ACTIVE DEV MACHINE

  • Debian 13 Trixie, Proxmox VM 130 on jg-hud
  • System Chromium: /usr/lib/chromium/chromium — NOT snap
  • Repo at: /home/jgitta/kiosk-browser/
  • This is the machine being actively developed and tested on

Production kiosk — 192.168.88.44 (SSH alias: kiosk)

  • Ubuntu 24.04 + snap Chromium — needs to be migrated to Debian to match kiosk-dev
  • Currently offline / pending migration

Deploy workflow

bash /home/jgitta/kiosk-browser/deploy.sh "describe your change"

This: commits + pushes to Gitea, restarts kiosk-dev immediately, and deploys to production if reachable.

CRITICAL: kiosk-restart must kill Chromium (pkill -f "remote-debugging-port=9222") BEFORE killing browser.py. If only Python is killed, Chromium holds the SingletonLock and the new instance silently exits — deploys appear to work but nothing changes on screen.

/usr/local/bin/kiosk-restart on kiosk-dev:

pkill -f "remote-debugging-port=9222" || true
pkill -f "python3.*browser" || true
sleep 1

Key files

File Purpose
browser.py Main application — toolbar + CDP + Chromium launcher
bookmarks.json Nested bookmark structure
home-screen-index.html Home screen source (deploy copies to /home/jgitta/kiosk-home/index.html)
deploy.sh One-command deploy
/home/jgitta/kiosk-home/index.html Senior home screen (live copy, outside repo)
~/.config/openbox/autostart Auto-restart loop on crash
/usr/local/bin/kiosk-restart Safe restart (kills Chromium + Python)
/etc/X11/xorg.conf.d/50-kiosk-hardening.conf Blocks TTY switching

Gitea repo (private LAN only)

  • URL: git@gitea.jgitta.com:jgitta/senior-kiosk.git (SSH) or https://gitea.jgitta.com/jgitta/senior-kiosk.git (HTTPS)
  • Branch: master
  • Exposure: Internal only — gitea.jgitta.com resolves on LAN/VPN (Caddy at 192.168.88.110). Not public internet.
  • Auth: SSH key preferred (git@gitea.jgitta.com:jgitta/senior-kiosk.git) once the machines public key is in Gitea → Settings → SSH Keys. Until then use HTTPS without a token in the URL and git config credential.helper store. Rotate any token that was ever in a remote URL.
  • Deploy: bash deploy.sh "message" — push to Gitea, sync home screen, kiosk-restart; production via SSH on 192.168.88.x / Tailscale.
  • Secrets: credentials.md is gitignored; Roundcube passwords live on the mail server, not in this repo.

Roundcube webmail

  • URL: https://mail.jgitta.com/roundcube/ (proxied via Caddy at 192.168.88.110)
  • LXC container 203 on jg-hud, IP: 192.168.88.45
  • Ubuntu 24.04, Apache2, PHP, MariaDB, Roundcube 1.6.6
  • Gmail IMAP: ssl://imap.gmail.com:993
  • Autologin plugin: /var/lib/roundcube/plugins/autologin/autologin.php
  • Logs: /var/lib/roundcube/logs/autologin.log and errors.log
  • Run commands on container: ssh jg-hud "pct exec 203 -- bash -c '...'" (jg-hud is the Proxmox host)
  • browser.py clears the Roundcube session cookie on every startup (forces fresh autologin)

Known hard-won lessons

Chromium

  • --no-sandbox causes "unsupported flag" infobar — never use it on Debian system Chromium
  • --test-type triggers Google CAPTCHA bot detection — never use it
  • --disable-blink-features=AutomationControlled causes "unsupported flag" infobar — removed
  • --disable-infobars, --disable-session-crashed-bubble, --disable-save-password-bubble, --suppress-message-center-popups, --disable-translate are all deprecated/removed in current Chromium — do not use
  • Profile dir: /home/jgitta/chromium-kiosk (not hidden — snap couldn't write hidden dirs; kept this convention)
  • Chromium adds its own flags via the Debian wrapper (--show-component-extension-options etc.) — ignore these, they're harmless

PyQt5 / Openbox

  • QMenu does NOT work under Openbox/X11 with Qt.Tool | WindowStaysOnTopHint parent — use a custom QWidget with the same flags instead (BookmarkPanel)
  • Qt event filters only see clicks on Qt widgets — clicks that land on Chromium (separate X11 process) are invisible. Use a QTimer polling QApplication.mouseButtons() + QCursor.pos() to detect outside clicks.
  • To prevent toolbar minimize: override changeEvent, detect Qt.WindowMinimized, call QTimer.singleShot(50, self._self_restore)
  • To restore minimized Chromium: periodic QTimer + xprop -id <wid> _NET_WM_STATE to check _NET_WM_STATE_HIDDEN, then xdotool windowmap <wid>

Roundcube autologin

  • Plugin MUST call both session->regenerate_id(false) AND session->set_auth_cookie() after login
  • Logs are in /var/lib/roundcube/logs/ NOT /var/log/roundcube/

Deployment

  • pkill -f browser.py in SSH kills its own session (shell cmdline contains "browser.py") — use kiosk-restart
  • Chromium must be killed before Python or SingletonLock prevents new instance from starting

Infrastructure overview

  • Proxmox host: jg-hud (main), siklos (192.168.88.27)
  • TrueNAS: 192.168.88.24
  • Thinkstation (dev desktop): 192.168.88.41
  • Caddy reverse proxy: 192.168.88.110 — handles *.jgitta.com
  • Gitea: gitea.jgitta.com (port 3002 internal)
  • Headscale: self-hosted Tailscale on siklos