Files
diskless-workstation/serving/kiosk-app/CLAUDE.md
T

3.8 KiB

CLAUDE.md — Fleet Kiosk App

The kiosk SPA for the diskless fleet: a cyber sci-fi spaceship monitoring/control terminal (Svelte 5 + Tailwind v4 + Vite) shown fullscreen by the diskless workstation's kiosk session (weston + Chromium). Built to a static bundle, served by a tiny nginx, and reverse-proxied at /kiosk/ by the boot serving stack.

This is an independent project, consumed by the fleet boot repo (diskless-workstation) as a git submodule at serving/kiosk-app/. It builds and publishes its own container image; the serving stack pulls it. Keep this repo focused on the app — the OS-side kiosk runtime (weston, Chromium lockdown policy, the kiosk-session script, the systemd unit) lives in the parent repo under files/kiosk/, not here.

Build & run

npm install
npm run dev        # http://localhost:5173/kiosk/   (Vite dev server)
npm run build      # -> dist/  (static bundle, base path /kiosk/)
npm run preview    # serve the built bundle locally

# container (same as CI does):
docker build -t fleet-kiosk:dev .
docker run --rm -p 8088:80 fleet-kiosk:dev   # http://localhost:8088/

CI (.gitea/workflows/ci.yaml) builds the Dockerfile (which runs npm ci + npm run build) and pushes git.bitasylum.org/audiobuzz/fleet-kiosk — branch-aware tags, :latest only on main. Needs repo secrets PACKAGE_USER / PACKAGE_TOKEN.

Hard contracts with the consuming fleet (do not break these)

These are the integration invariants the parent serving stack + the kiosk display depend on. Changing one means a coordinated change in the parent repo.

  1. Base path is /kiosk/. vite.config.js sets base: '/kiosk/' because the main serving nginx reverse-proxies /kiosk/ to this container and strips the prefix. Emitted asset URLs must be absolute under /kiosk/. If this ever changes, the parent's http/default.conf proxy + the kiosk_url Packer default must change with it.
  2. Hash-based routing only (src/lib/router.svelte.js, #/power, #/nav…). Deep links stay client-side so the proxy needs no SPA-fallback rewrite. Don't switch to history/path routing without adding that rewrite upstream.
  3. Fully self-contained — no CDNs, no web fonts, no external calls. The boot LAN is air-gapped and the stack's dnsmasq sinkholes call-home (and the in-image Chromium policy blocks it). Bundle every asset; inline fonts. A request that escapes to the internet will silently fail on a real host.
  4. Static / read-only by nature. The display is a fixed kiosk (no keyboard nav, locked-down Chromium). Telemetry is dummy/simulated (src/lib/data.js, live.svelte.js, clock.svelte.js) — there is no backend. If a real data feed is ever added it must be reachable on the boot LAN and fail gracefully offline.
  5. Reproducible bundle. Keep package-lock.json committed; the Dockerfile uses npm ci. Don't introduce build-time network fetches beyond npm deps.

Layout

Dockerfile            2-stage: Vite build (node:alpine) -> nginx:alpine static serve
nginx.conf            serves the built assets at the container root (proxied as /kiosk/)
vite.config.js        base '/kiosk/', self-contained bundle settings
src/App.svelte        shell: navbar + routed page
src/lib/              Navbar, Panel, Radar, Stat, Bar, Tag, StatusBar + router/clock/data helpers
src/pages/            Dashboard, Power, Navigation, LifeSupport, Comms
.gitea/workflows/     CI: build + push the fleet-kiosk image

Conventions

  • Svelte 5 runes; Tailwind v4 via @tailwindcss/vite.
  • Keep components small and self-contained; shared sim helpers in src/lib/.
  • Target es2022 (modern Chromium kiosk — no legacy transpile needed).
  • Versioning: bump package.json version per release; the image tag tracks it on main (plus branch-name+sha tags for dev branches).