Wishvaults
Every year around birthdays and Christmas, my friends and family end up asking each other what we want. We usually fall back to Amazon lists, random notes, or apps like Elfster. These always felt clunky and feature-bloated and I hated that I would need to share my data with every new service.
This motivated me to build something that is simple and focused on the core intention: to create and share a wishlist.

Goal
My goal was to build something intentionally minimal, private, and simple to use while removing the friction of traditional email-based accounts:
- low-friction (no accounts needed)
- simple sharing (just a link)
- respect privacy (no data mining)
- minimalist UI
Monetization of some sort would be nice (wishful thinking), but not at the cost of user privacy.
Full Stack Overview
Wishvaults is full stack, with a backend-heavy architecture and a deliberately simple frontend. This was also a learning project as an opportunity to build a complete Scala service with Typelevel libraries, rather than contributing to pieces of a larger codebase.
Backend
- Scala 3
- Typelevel libraries: cats-effect, http4s, doobie, circe
- PostgreSQL - model+store relational wishlist records
- Flyway: DB migrations
- JWT auth and bcrypt password hashing
- Docker and Docker Compose for local/dev deployment
The core backend capabilities include:
- create password-protected wishlists without forcing traditional accounts
- authenticate to perform edit operations
- track wish state (pending or granted)
Frontend
The frontend lives in a separate repository.
It is intentionally simple: clean layout, minimal visual noise, and straightforward flows that keep the wishlist itself front and center. I wanted the UI to feel light and obvious, not overloaded.
Result
Wishvaults is a working full stack app living at wishvaults.com.
The frontend is deployed on Vercel. It provides a fairly simple interface to connect a repository, and deploy on commit. The backend was first deployed to Render on the free tier. This meant that very often, visitors would encounter a cold start. To get around it, the web app needed to be aware of the backend state (up/down) to block form submissions until the backend was up. To go one step further, I added a simple ping on app load to trigger the backend to wake up if it was down.
Ultimately, I moved the backend to a self hosted environment on my home kubernetes cluster and put it behind a Cloudflare tunnel.
Future Work
- optional email-based recovery for people who want it
- stronger abuse prevention around public/custom URLs
- tighter authorization boundaries and expanded test coverage