c8534c967c
Co-authored-by: Cursor <cursoragent@cursor.com>
131 lines
7.2 KiB
Markdown
131 lines
7.2 KiB
Markdown
# 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
|
|
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:
|
|
```bash
|
|
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 (see also `jgitta/homelab-configs`)
|
|
|
|
Source of truth for infra: **https://gitea.jgitta.com/jgitta/homelab-configs**
|
|
|
|
| Piece | Detail |
|
|
|--------|--------|
|
|
| Gitea app | Docker on **siklos** `192.168.88.27` — HTTP **3002**, SSH **2222** (not 22) |
|
|
| Public URL | `gitea.jgitta.com` → **Caddy** `192.168.88.110` → proxies to Gitea :3002 |
|
|
| DNS | MikroTik `192.168.88.1` / siklos; public DNS may resolve to WAN IP (still reaches Caddy :443) |
|
|
|
|
**This repo (senior-kiosk):**
|
|
|
|
- **HTTPS (usual — what worked before):** `https://gitea.jgitta.com/jgitta/senior-kiosk.git` — through Caddy. Auth via Gitea username + access token (`git config credential.helper store`), or token in URL (legacy on kiosk-dev).
|
|
- **SSH:** Gitea SSH is on port **2222**, not 22. Plain `git@gitea.jgitta.com:...` hits port **22 on the WAN** and will **not** accept your Gitea password. Use either:
|
|
- `ssh://git@192.168.88.27:2222/jgitta/senior-kiosk.git` (LAN), or
|
|
- `~/.ssh/config`: `Host gitea.jgitta.com` → `Port 2222` (requires WAN/LAN port-forward 2222→siklos:2222 if using the hostname)
|
|
- **Branch:** `master`
|
|
- **Deploy:** `bash deploy.sh "message"` — push, sync home screen, `kiosk-restart`
|
|
- **Secrets:** `credentials.md` gitignored
|
|
|
|
## 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 — terminates HTTPS for `*.jgitta.com` (including `gitea.jgitta.com` → Gitea :3002)
|
|
- **Gitea**: behind Caddy at `gitea.jgitta.com`; app listens on port 3002 on the Gitea host
|
|
- **Headscale**: self-hosted Tailscale on siklos
|