A self-hosted shopping-list app in the spirit of Bring!, extended with price comparison across Swiss supermarkets and a monthly household budget. Installable as a progressive web app.
The whole household shares one shopping list. Items are added by tapping tiles — the catalogue and the history of past purchases sit as a grid, and a tap moves an item onto the current list. No drag-and-drop, no swiping; the interaction is deliberately simple enough for everyone in the family to use.
Underneath, the app tracks every shopping trip with date, store, and receipt total, and shows how much of the monthly budget is left at any time.
One list for the household, grouped by category in shopping-aisle order. Past purchases become reusable tiles; a tap brings an item back. AI suggestions for the current list are clearly marked and can be accepted or declined.
A fixed monthly budget with a reset. Each trip is recorded with date, store, and receipt total. The remaining amount is calculated live rather than stored, and a widget shows budget, spent, and remaining at a glance.
Per item and per basket across three Swiss supermarkets. Where pack sizes differ, the comparison uses unit price so the result is not misleading. The time of each price lookup is shown, so you know how current it is.
The app learns how often recurring items are bought — household staples like toilet paper or pet food — and suggests them when they are due again. The detection is deterministic, based on the interval between past purchases.
The app runs as a container stack on local hardware: a web frontend and API, a background worker for price lookups and rhythm calculation, and a PostgreSQL database that is never exposed outside the stack. The language-model work is sent to the local AI servers over an internal API.
Product matching — especially mapping a generic item to a supermarket's own-brand equivalent — is handled by a local language model with user confirmation, after which it is editable. The price comparison and rhythm detection run deterministically in code, not in the model, so the numbers are always reproducible.
Tile selection over drag-and-drop, because it is the interaction the whole family can use without friction. No real-time sync between users, by choice — the list refreshes on reload and when a new product is selected, which is enough for a shared household list and avoids a lot of complexity.
The price lookups are the most fragile part: supermarket sites change their structure without warning. Each store has its own independent adapter, so a broken one does not take the others down with it. Roles separate admin functions — setting the budget, managing users, editing product mappings — from the everyday tapping and checking off that everyone can do.