Skip to content

Repository files navigation

kiosk-engine

Build interactive event flows in a browser, run them fully offline on locked-down kiosk hardware, and sync captured data back when a connection returns.

Bun + Hono Electron Logto TypeScript strict 300+ tests passing

Flow builder: screen list, per-field editor with hardware actions, and a live themed kiosk preview

Build an interactive event flow in a browser, publish it, and it runs on a locked-down kiosk out on the floor. The kiosk works with no network at all: it captures every entry to a local database first, drives attached hardware like a relay or a receipt printer, and syncs the data back to the cloud when a connection returns. Nothing is lost if the wifi drops, the machine reboots, or the power cuts mid-entry.

It is three services. A cloud console where you build flows and manage the fleet, a sync server that is the source of truth, and the kiosk app that runs offline on the hardware.

How it works

  1. An operator builds a flow in the console: a sequence of screens (welcome, form, quiz, thank-you) with fields and optional hardware actions, then publishes a version.
  2. A kiosk is provisioned from the console. It gets its own credentials and pairs to an event.
  3. On the floor the kiosk pulls its published flow and runs it fullscreen, offline. A visitor taps through the screens on a touch display.
  4. Each submission is written to a local SQLite database inside one transaction, and only then does the screen advance. The write is flushed to disk before the visitor sees the next screen.
  5. Any hardware action on the screen fires after that durable write: pulse a relay, print a ticket, read a sensor. A hardware fault never loses the capture.
  6. A background sync engine drains the local outbox to the cloud whenever it can reach the server, retrying with backoff and deduplicating so a dropped connection never doubles a record or loses one.
  7. Back in the console, the fleet view shows each kiosk's status, backlog, and storage, and captured data can be reviewed or exported to CSV.

Features

Flow builder. Author a flow as a list of screens with typed fields (text, email, phone, number, select, multiselect, boolean, rating). Attach hardware actions to a screen, set a theme color, and watch a live kiosk preview render exactly what will run on the device. Flows are versioned and immutable once published.

Flow builder with three panes: reorderable screen list, field and hardware editor, and a live themed kiosk preview

Kiosk runtime. The published flow runs fullscreen with large touch targets, an on-screen keyboard for text fields, inline validation, and an idle reset that wipes entered data and returns to the start. The only ambient signal is a small sync badge; everything else is the visitor's flow.

Kiosk form screen with large touch fields, required markers, and a rating input

Kiosk quiz screen with selectable options

Offline-first capture. Every entry lands in a local SQLite database with write-ahead logging and full synchronous durability. Captures survive an unclean shutdown or power loss because the row is committed to disk before the flow advances. A transactional outbox tracks what still needs to sync.

Device fleet. Provision a kiosk from the console and it is issued its own machine credentials through Logto. The fleet view shows derived online/offline status, pending backlog, storage use, and power source per device, with health charts for backlog, storage, and heartbeat gaps over time.

Device fleet table with online, offline, and not-paired kiosks, plus health charts for backlog, storage, and heartbeat gaps

One-time pairing. Provisioning shows a client secret exactly once, with copy buttons and pairing steps. Enter it on the kiosk's admin screen and it pulls its flow and starts running.

Provision dialog showing a one-time client secret with copy buttons and kiosk pairing steps

Hardware over serial. A framed, byte-stuffed serial protocol drives relays, sensors, printers, and scanners. Replies are matched to commands, NACKs and timeouts retry with backoff, and a dropped port reconnects on its own. The driver is proven against a real virtual serial loopback, not a mock.

Locked-down operation. In the packaged build the kiosk is a frameless, non-closable fullscreen window with developer tools, refresh, navigation, and OS shortcuts blocked. A hidden corner gesture and a PIN open a technician panel for status, sync, hardware, config, and a controlled exit.

Admin PIN pad over a dimmed kiosk screen

Admin hardware panel with serial ports, manual fire, and a recent action log

Tech

Layer Choice
Server Bun + Hono, three-layer routes/controllers/services, Prisma over Postgres
Console React + Vite, TanStack Query, Tailwind, Recharts for health charts
Kiosk Electron + electron-vite, React renderer, a hardened contextBridge IPC boundary
Local store better-sqlite3 with WAL and full synchronous durability, a transactional outbox
Hardware serialport over a framed protocol, proven on a real serial loopback
Auth Self-hosted Logto: OIDC auth-code for admins, OAuth2 client-credentials for devices
Sync Hand-rolled outbox: UUIDv7 ids, idempotency keys, per-record acks, backoff with jitter
Packaging electron-builder (Windows NSIS, Linux AppImage and deb), electron-updater
Testing Bun test, Vitest, Testing Library, Playwright, real-hardware and real-Postgres integration

Requirements

  • Docker and Docker Compose
  • Bun 1.3+
  • Node 20+ for the Electron kiosk build

Cloud stack

Bring up Postgres, Logto, and the server with one command:

cp .env.example .env
docker compose up -d

This starts:

  • Postgres on 5432 with two databases, kiosk for the app and logto for auth
  • Logto on 3001 (OIDC) and 3002 (admin console)
  • The server on 8787

Check health once the containers report healthy:

curl http://localhost:8787/health

First-time Logto setup

Logto needs two applications configured once, in its admin console at http://localhost:3002:

  1. Create the admin account on first visit.
  2. Create an API resource with the indicator from LOGTO_API_RESOURCE (default https://api.kiosk-engine.local).
  3. Create a Single Page App for the console. Set the redirect URI to http://localhost:5173/callback and post-logout to http://localhost:5173. Copy its App ID into VITE_LOGTO_APP_ID in .env.
  4. Machine-to-machine apps for devices are created later, per device, from the console.

Console

cd console
bun install
bun run dev

Opens on http://localhost:5173 and redirects to Logto to sign in.

Kiosk

cd kiosk
bun install
bun run dev

The kiosk captures locally with no server. Pair it to an event from its admin screen using the credentials shown when you provision the device in the console, and it will pull its flow and start syncing.

Tests

Each service has its own suite. From a service directory:

bun run typecheck
bun run test
bun run test:e2e

The kiosk also has a real serial-hardware suite (bun run test:hw) that runs against a virtual serial loopback. See tools/README.md for the loopback setup on Windows (com0com) and Linux (socat).

About

Build interactive event flows in a browser, run them offline on locked-down kiosk hardware, and sync when a connection returns.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages