Architecture
Hauser is a Svelte 5 single-page app built with Vite, plus a small Node.js server. The wall panel and the phone are two shells over one state model.
System shape
Section titled “System shape”Wall panel (kiosk browser) Phone (home-screen web app) └──────────── same origin ────────────┘ │ Hauser web app │ ┌───────────────┴────────────────┐ │ same-origin HTTP + WebSocket │ straight from the browser ▼ ▼ Hauser server (app/server.mjs) Jellyfin REST + HLS · household configuration · Home Assistant gateway (App) · room-image store, map, weather · reminders, notifications · Paperless / Notion bridges │ ▼ Home Assistant ←── or straight from the browser (Compose, direct mode)State flow
Section titled “State flow”The app never reads the WebSocket in a component. Four layers sit between the UI and the backends:
- an entity store with server truth,
- an overlay of pending intents (what you just tapped),
- a command queue, deduplicated per entity,
- a swappable backend: Home Assistant live, or the deterministic fake used by the demo.
The UI reads merged() and writes dispatch(). That seam is what makes the optimistic feel and the offline demo possible.
user input → local intent → visible update → command → authoritative state → confirm or correctServer
Section titled “Server”app/server.mjs dispatches requests; the modules under app/server/ own one area each: setup, configuration core, room images, ambient map, weather, family data, notification rules, laundry, hotel mode, pairing, remote access, Paperless, Notion. The HTTP contract in api-contract.mjs generates a typed client, and a test fails when a route literal drifts.
Persistence
Section titled “Persistence”External, versioned JSON. The household file is validated before the server listens and before the app mounts. Migrations back up the original. See Household configuration.
Security boundaries
Section titled “Security boundaries”- Service credentials are entered at runtime. None live in the repository or the build.
- In the App, no Home Assistant token reaches the browser.
- Paperless, Notion and OpenAI credentials stay on the server.
- No telemetry.
Rendering
Section titled “Rendering”- CSS custom properties in
design-tokens/are the visual source of truth. - Interaction-critical motion uses transform and opacity only.
- Heavy routes such as video playback load on demand.
- A service worker precaches the shell. Room pictures are cached when first shown.
- The server precomputes the map, the phone image variants and last week’s energy statistics overnight.
Deeper reading
Section titled “Deeper reading”The repository’s technical documents go further: architecture, design system, interaction contract, performance budget, component catalogue.