Index / Work
Household — A self-hosted home-management PWA for families Live site ↗
Family productivity PWA

HouseholdA self-hosted home-management PWA for families

Household is a self-hosted, installable web app that turns the chores of running a home — shopping lists, recipes, loyalty cards, to-dos — into a lightly gamified shared space. A Laravel JSON API backs a Vue 3 SPA, with real-time sync over WebSockets, an integration into a separate Croatian grocery-price API, and a zero-knowledge encrypted vault for loyalty cards.

Household is a full home-management product rather than a demo: a Laravel 13 JSON API serving a Vue 3 + TypeScript single-page app that installs as a PWA. It covers shopping lists, a grocery catalog, recipes, loyalty cards, and to-dos, with real-time sync over WebSockets, Web Push notifications, and Google OAuth — all running on a single self-managed Ubuntu VPS behind Nginx, with Supervisor-managed workers and a GitHub Actions deploy.

Two pieces stand out technically. The loyalty-card vault is zero-knowledge: card numbers are encrypted in the browser with a PBKDF2-derived AES-GCM key, and the backend stores only a salt and an opaque authenticator — it can neither read nor decrypt the cards. And price comparison reaches into a completely separate Laravel + Postgres service (the prices project) over an authenticated HTTP API, so a large Croatian grocery dataset powers basket comparisons without ever being coupled into this database.

It’s built to feel rewarding without being gimmicky: an XP/achievement system turns chores into shared household progress, with guardrails so the gamification never distorts the real data or gates real features. It’s designed, integrated, and shipped end-to-end as a single coherent app — and the counts and claims above are taken directly from the source, not estimated.

RoleDesign + full-stack
Year2026
Core stackLaravel 13, Vue 3 + TS, MariaDB
SurfaceInstallable PWA + admin SPA
Livehousehold.brokenlogic.studio
Household dashboard showing a shared shopping list, household level/XP bar, and module tiles
Screenshot soon
Real-time

Shopping lists that sync live across the household

List changes broadcast over WebSockets so every member's screen updates as items get checked off — no refresh, no polling. Web Push notifications reach members even when the app is closed.

  • Laravel Reverb broadcasts ShoppingListUpdated events consumed via Laravel Echo
  • Web Push (VAPID) delivers item-added and achievement notifications to installed PWAs
  • Queued notifications run under a Supervisor-managed worker
Screenshot soon
Privacy

A zero-knowledge vault for loyalty cards

Loyalty-card numbers are encrypted in the browser with a household PIN; the server never sees plaintext or the PIN. It stores only a salt and an opaque authenticator token, so a database leak reveals nothing usable.

  • PBKDF2 (100k iterations, SHA-256) derives an AES-GCM key from the PIN client-side
  • Server persists only vault_salt and vault_authenticator — verification is done by decrypting a sentinel
  • Cards render as scannable barcodes/QR via jsBarcode and html5-qrcode
Screenshot soon
Integration

Grocery price comparison from a separate price API

Shopping-list items can be linked to real Croatian grocery products and compared across retail chains, pulling live data from a standalone prices service over an authenticated HTTP API.

  • PricesService calls product-search, basket, and chains endpoints with a bearer token
  • Basket comparison returns chain-level totals for a whole list
  • Product links stored per grocery item via a dedicated price-link table
Screenshot soon
Gamification

Households level up as they get things done

Completing lists, building the catalog, and inviting members award XP and unlock achievements — including secret and meta achievements — turning routine chores into shared progress.

  • XpService awards XP and levels; AchievementService checks thresholds per category
  • Meta achievements unlock for collecting sets of secret/non-secret achievements
  • Unlocks fan out as notifications to every household member
Screenshot soon
Onboarding & growth

Invites, referrals, and a guided first run

New households are set up through an onboarding wizard, can theme themselves, and grow through signed invite and referral links — with Google OAuth as an alternative to email signup.

  • Sanctum sessions plus Socialite Google sign-in
  • Signed share URLs for invites, referrals, recipes, and achievements
  • Referral redemptions grant credits and feed achievement progress
Screenshot soon
Bilingual PWA

Installable, offline-aware, and fully localized

The app installs to the home screen as a PWA with auto-updating service worker assets, and ships a complete English/Croatian localization across the whole interface.

  • vite-plugin-pwa with autoUpdate registration and a web app manifest
  • vue-i18n with two full locale files (en, hr)
  • Rich-text recipe editing via Tiptap
Architecture

A Laravel JSON API and a Vue SPA, joined by WebSockets and one external service — all on a single self-managed VPS.

Client
Vue 3 SPA · installable PWA
TypeScript SPA with TanStack Query for server state and Pinia for client state; installs and auto-updates as a PWA.
Edge
Nginx · Reverb WebSocket proxy
Nginx serves the SPA shell and proxies the /app WebSocket upgrade to Reverb; Let's Encrypt terminates TLS.
Application
Laravel 13 API · queue + broadcast workers
Stateless JSON API (Sanctum auth) with thin controllers over service classes; Supervisor keeps the Reverb and queue workers alive.
Data & external
MariaDB · prices API
MariaDB is the system of record; the standalone prices service is consumed over an authenticated HTTP API for price comparison.
Challenges solved
01

Storing loyalty-card secrets the server can never read

Problem

Loyalty-card numbers are sensitive and shared within a household, but a self-hosted app on a single VPS is one database dump away from leaking them. Server-side encryption still leaves keys on the same box as the data.

Solution

Encryption happens entirely in the browser: PBKDF2 (100k iterations) derives an AES-GCM key from a household PIN, and only a random salt plus an encrypted sentinel 'authenticator' are persisted. The server can verify nothing and decrypt nothing — a leak yields salts and ciphertext, not card numbers.

02

Keeping shared shopping lists in sync without polling

Problem

Multiple household members shop and edit lists at the same time; stale screens cause double-buys and confusion. Polling an API would be wasteful and laggy for a family-sized app on modest hardware.

Solution

List mutations broadcast ShoppingListUpdated over Laravel Reverb, consumed client-side through Laravel Echo, so UIs reconcile in real time. Web Push (VAPID) covers the closed-app case, with both broadcast and queue workers kept alive by Supervisor and gracefully restarted on each deploy.

03

Pulling live grocery prices from a separate codebase

Problem

Price comparison needs a large, frequently-updated Croatian grocery dataset that doesn't belong inside a home-management app. Coupling the two databases would entangle two unrelated projects.

Solution

Prices live in a standalone Laravel + Postgres service; Household talks to it only through a thin PricesService over an authenticated HTTP API (product search, basket, chains). Grocery items hold a link to a remote product, and basket comparison returns per-chain totals — keeping the two apps independently deployable.

04

Making chores feel rewarding without pay-to-win mechanics

Problem

Gamification can easily become noise or gate real functionality behind cosmetic 'classes'. The goal was motivation, not a paywall or a grind that distorts the underlying data.

Solution

An XpService and AchievementService award XP and unlock achievements off real activity (completed lists, catalog size, members, referrals), with abuse guardrails at the counter level so stats stay honest. Secret and meta achievements reward collecting sets — all cosmetic, with no feature gated behind progression.

How it's built
Backend & API
Laravel 13, PHP 8.3, Sanctum, Socialite, API Resources, Form Requests, PHP enums
Frontend
Vue 3, TypeScript, TanStack Query, TanStack Table, Pinia, Vue Router, Tailwind v4, shadcn-vue / reka-ui, Tiptap, vue-i18n
Real-time & integrations
Laravel Reverb (WebSockets), Laravel Echo, Web Push (VAPID), prices API (HTTP), vite-plugin-pwa
Infra & quality
MariaDB, Nginx, Supervisor, GitHub Actions deploy, Let's Encrypt, Laravel Pint, ESLint + Prettier, vue-tsc

A complete, self-hostable home-management product — real-time, encrypted where it counts, and shipped to a VPS by a single deploy script.