Version 0.3.0 · Pi 3B, 4 and 5

A kiosk OS that does one thing on the screen.

pikiosk boots a Raspberry Pi straight into one fullscreen native app. No login prompt, no desktop, a read-only root, and a touchscreen UI in Rust that draws liquid glass straight to the display.

kiosk-ui home screen on a Raspberry Pi 3B with a Waveshare 5 inch touchscreen: clock, network, uptime, CPU temperature and memory tiles
The home page on a Pi 3B with a Waveshare 5″ 800×480 panel.
Why pikiosk

Built to be switched on and left alone

◧

Read-only by design

The OS partitions are never written. Pulling the power is safe. Logs live in RAM, and everything generated at boot goes to /run.

◐

Liquid-glass UI

kiosk-ui is Rust + egui. Glass panels refract, frost and light the wallpaper with a custom GLES 2 shader, through DRM/KMS with no X11 or Wayland.

⌁

One image per board

Buildroot 2026.08 images for the Raspberry Pi 3B, 4 and 5 (64-bit), flashed with Raspberry Pi Imager.

▦

Signed app store

Install and update apps from a TUF-signed store behind an admin PIN. A bad update rolls itself back. Off until you turn it on.

↻

Self-healing

Hardware watchdog, app auto-restart after 2 s, reboot on a crash loop, kernel panic auto-reboot.

✎

Configure on the card

Edit one file, kiosk.conf, on the FAT partition: hostname, WiFi, static IP, timezone, NTP, which app runs. No rebuild.

PartWhat it is
KernelRaspberry Pi kernel 6.12 (arm64), KMS graphics
Initsystemd, read-only root filesystem
GraphicsDRM/KMS + Mesa (VC4 on the Pi 3B; VC4 and V3D on the Pi 4 and 5): EGL, GLES2, GBM
NetworkEthernet and onboard WiFi (DHCP or static), DNS, NTP, mDNS (kiosk.local)
MaintenanceSSH with public keys only, and off unless you provide a key
DisplayWaveshare 5inch HDMI LCD (B): 800×480, USB touch, calibrated on the device
The UI

Three pages, one swipe apart

Switch with the floating tab bar, a horizontal swipe or the arrow keys. A compact layout is drawn for 800×480, a roomier one from 1280×720.

Touch calibration that cannot lock you out

Settings → Calibrate shows five crosses. kiosk-ui fits a calibration matrix to your taps, then shows a check screen. Keep saves it; without Keep, the old calibration returns after 15 s. It is stored in /data/app and survives re-flashing the root filesystem.

Tuned for a resistive panel

A fingertip wobbles up to 13 px on this panel, so a tap may move 20 px and still count. The animated wallpaper is capped at 30 fps on the Pi 3B; with animation off the screen redraws once a second.

Download

Pick your board

Each release has one compressed image per board and the SDK for building apps. Raspberry Pi Imager flashes the image directly (“Use custom”).

Raspberry Pi 3B

The tested board.

rpi3 image

Raspberry Pi 4

Built by CI, not yet run on hardware

rpi4 image

Raspberry Pi 5

Built by CI, not yet run on hardware

rpi5 image

All files, checksums and the app SDK are on the latest release. The Pi 4 and 5 need a micro-HDMI adapter cable for the Waveshare panel.

Get started

From download to kiosk in five minutes

Flash the image

Open Raspberry Pi Imager, choose “Use custom”, pick the .img.xz, write it to an SD card. The data partition grows to fill the card on first boot.

Edit kiosk.conf

The card’s FAT partition KIOSKBOOT shows up on your computer. Set hostname, timezone and WiFi, or leave WiFi empty and add networks on the touchscreen in Settings › Network. Changes apply on the next boot.

# KIOSKBOOT/kiosk.conf
HOSTNAME=kiosk
TIMEZONE=Europe/Amsterdam
WIFI_COUNTRY=NL
WIFI_SSID=MyNetwork
WIFI_PSK=correct-horse-battery

Boot it

Put the card in the Pi and power it on. kiosk-ui appears on the display. Calibrate the touch panel under Settings if the corners are hard to hit.

Optional: SSH

Put your public key in a file named authorized_keys on the same partition. Without it, SSH does not run.

ssh root@kiosk.local
journalctl -u kiosk -f
Apps

A signed app store for a kiosk

Apps are .kpkg packages built on kiosk-kit, the crate kiosk-ui itself uses for display, touch and glass. One app owns the screen at a time. Hold the top-right corner for 3 seconds to leave an app, even a hung one.

The Store catalog with category tabs and app cards
The catalog, with categories and a Check now button.
An app's detail page with install progress
An app’s detail page, with install progress.

Admin PIN

Installing, updating and removing need the PIN from STORE_PIN (4 to 8 digits), entered on a glass keypad. kioskd checks it; the UI only holds a token that ends 60 s after the last touch. Three wrong PINs lock it, and pulling the power does not reset that.

Atomic updates

A new version replaces the old in one step. Pull the power mid-install and the previous version still runs. App data in /data/store/data/<id> survives updates and re-flashing.

Offline once installed

Installed apps never need the network. The store is checked for updates only after NTP has set the clock, because signatures expire.

Health gate and rollback

1. InstallSignature, size and hash are checked first.
→
2. SwitchThe new version becomes current, the old one previous.
→
3. 30 s gateThe first run must stay up for 30 seconds.
→
4. Roll backOn a crash the old version returns, the bad one is deleted and blocked, and a notice is shown.
The rollback notice dialog
The notice after an automatic rollback.

Turn the store on

Both ways are off by default: with an empty STORE_URL and no STORE_PIN, the Store tile is hidden and nobody can install anything.

# KIOSKBOOT/kiosk.conf
STORE_URL=https://pierrejochem.github.io/pikiosk-apps
STORE_CHANNEL=stable
STORE_PIN=482913
KeyDefaultMeaning
STORE_URLempty (off)Base URL of the store. The board reads <url>/<channel>/metadata and /targets.
STORE_CHANNELstablestable or beta.
STORE_PINempty4 to 8 digits. Without it nobody is admin.
STORE_PIN_LOCKOUT300Seconds every PIN is refused after 3 wrong ones.
STORE_ALLOW_UNSIGNEDnoInstall unsigned .kpkg files from /boot/apps. For development only.
KIOSK_HOME_GESTUREyesThe 3 s top-right hold that leaves an app.
The admin PIN keypad
The admin PIN keypad.

Publish your own app

An app is a Rust crate that implements kiosk_kit::App and calls kiosk_kit::run, plus a manifest.toml and a 128×128 PNG icon. Start from apps/hello in the pikiosk-apps repository, which holds the publish workflow and the key ceremony. Each release attaches an SDK, so one binary runs on all three boards.

# manifest.toml
id = "de.example.hello"
version = "1.0.0"
name = "Hello"
entry = "bin/hello"
icon = "assets/icon.png"
category = "Demo"
boards = ["rpi3", "rpi4", "rpi5"]

# build and check the package
cargo run --release -p kpkg -- build my-app/ -o my-app.kpkg
cargo run --release -p kpkg -- inspect my-app.kpkg

# publish: tag the app, the workflow signs it into the beta channel
git tag hello-v1.0.0 && git push origin hello-v1.0.0
Security

What protects the device, and where it stops

Signed with TUF

Every package is checked for signature, expiry, rollback and hash. The trust anchor root.json is baked into the image, so changing it means a new image. A copy of an old store, or a clock turned back, is refused.

No logins

No login prompt on the screen, the serial port or anywhere else. SSH takes public keys only and is off without an authorized_keys file.

Apps run unprivileged

Apps run as the kiosk account in a systemd sandbox with a read-only /data view, no access to /boot or the kioskd socket, and task, CPU and OOM limits. Package environment variables that could load code into root commands are refused.

Known limits. An app can block the Home gesture by grabbing the touch device; recovery is a power cycle or SSH. There is no memory or disk cap for apps. Apps share a user with kiosk-ui. Anyone who can read the SD card can read the PIN in kiosk.conf. The design and the 0.2.0 security review are written up in docs/app-store.md.
Build it yourself

Docker on macOS or Linux

The first build takes 1 to 1.5 hours. After that a rebuild with unchanged system code takes about a minute, because the toolchain, kernel and compiler output are cached.

git clone https://github.com/pierrejochem/pikiosk
cd pikiosk
./build.sh                  # Pi 3B  -> out/sdcard.img
BOARD=rpi4 ./build.sh       # Pi 4   -> out/sdcard-rpi4.img
BOARD=rpi5 ./build.sh       # Pi 5   -> out/sdcard-rpi5.img
./build.sh app              # only kiosk-ui -> out/kiosk-ui

Try a changed UI without flashing

Build the app, copy it to /data, and point kiosk.conf at it.

./build.sh app
scp out/kiosk-ui root@kiosk.local:/data/app/
# kiosk.conf:  APP=/data/app/kiosk-ui

Run a different app

Any aarch64 binary can be the kiosk app. Qt 6, SDL2 and flutter-pi come as build fragments.

./build.sh with qt6
./build.sh with sdl2
./build.sh with flutter

Try the UI on your computer

tools/emulate.sh opens kiosk-ui in a window the size of the 5″ panel (800×480, or pass 1280x720), with the real kioskd behind it. The WiFi list, the on-screen keyboard, the admin PIN and the brightness slider all work with the mouse. Only wpa_cli is replaced, by a stand-in with made-up networks, so nothing on your own network is touched. The admin PIN is 1234, and the password wrongwrong is always refused.

tools/emulate.sh            # 800×480, the 5″ touch panel
tools/emulate.sh 1280x720   # the roomier layout

Only the emulator needs these; the SD card image does not. On Linux it needs a display and a few development packages. On Ubuntu:

sudo apt install -y pkg-config libudev-dev libinput-dev libgbm-dev libdrm-dev libegl-dev

The SD card

PartitionTypeSizeHolds
p1 · KIOSKBOOTFAT3264 MBFirmware, kernel, kiosk.conf, authorized_keys. Mounted read-only on /boot.
p2 · kioskrootext4256 MBThe OS. Mounted read-only on /.
p3 · kioskdataext4the restSSH host keys, /data/app, installed apps. Mounted read-write on /data.

The source

CrateRole
kiosk-kitDisplay, touch and glass widgets as a library. What apps build on.
kiosk-uiThe kiosk app: the pages, the Apps page and the Store.
kioskdRoot daemon: launches apps, Home gesture, installs, PIN, health gate.
kpkgThe .kpkg format and tool: build, inspect, verify, catalog.
Good to know

Notes and limits

No real-time clock

The Pi 3B and 4 have no RTC, so the clock is right only after an NTP sync. The store checks for updates only after that. The Pi 5 has an RTC with an optional battery.

WiFi

WPA3-only networks do not work: the Pi 3B’s WiFi chip does not support SAE. WPA2 and WPA2/WPA3 mixed mode work.

Pi 4 and Pi 5

Their images are built by CI and have not been run on hardware yet. The Pi 3B is the tested board.

Glass and layers

Glass refracts the wallpaper, not other UI elements underneath it. egui has no backdrop capture.