Inspiration
Most savings apps feel like spreadsheets with a pastel coat of paint. They track numbers, but they don't change behaviour. The real problem with saving money isn't a lack of tools — it's a lack of accountability, discipline, and motivation that compounds over time.
We wanted to build something that felt less like a finance app and more like having a sharp, honest friend in your corner who actually works in finance. Someone who knows your numbers, calls out your patterns, and encourages you without sugarcoating the truth.
That idea became Vault.
What We Built
Vault is a full-stack personal savings application with five core pillars:
1. Goal-based Vaults Users create individual savings "vaults" for specific goals — a holiday, a car, a laptop. Each vault is isolated, named after the dream, and tracks progress independently.
2. Withdrawal Policies The feature that sets Vault apart from any standard savings tool. When creating a vault, users choose a withdrawal policy:
- Free — withdraw anytime
- Goal-locked — funds are inaccessible until the target is reached
- Cooling period — a time lock (7 days to 1 year) from creation date
- Goal + Cooling — both conditions must be satisfied
This introduces genuine friction that mirrors real-world savings discipline. The policy is enforced server-side, not just in the UI.
3. Streak & Habit Tracking Vault tracks daily deposit activity and computes current and longest saving streaks. A 30-day activity calendar, streak milestones, and 12 unlockable badges gamify consistency over big one-time contributions.
4. Marcus — AI Financial Advisor Marcus is a conversational AI advisor built on Llama 3.1 via the Groq API, presented in a WhatsApp-style chat interface. What makes Marcus different: before every response, the server injects the user's live financial data — vault balances, goal progress, streak, deposit history, and withdrawal policies — directly into the system prompt. Marcus doesn't give generic advice. He references your actual numbers.
5. Goal Celebration When a deposit pushes a vault balance over its target for the first time, Vault fires a multi-burst confetti animation and a celebration modal. Small moments of delight matter for habit formation.
How We Built It
| Layer | Technology |
|---|---|
| Backend | Node.js + Express |
| Database | SQLite via Node's built-in node:sqlite (no native compilation) |
| Auth | bcryptjs password hashing + express-session |
| AI Advisor | Groq API — Llama 3.1 8B Instant |
| Frontend | Vanilla HTML/CSS/JS — no framework |
| Typography | Cormorant Garamond + DM Sans (Google Fonts) |
| Animations | canvas-confetti |
| Icons | Font Awesome |
The entire frontend is a single-page application running inside dashboard.html, with tab-based navigation and no client-side routing library. All API routes return Cache-Control: no-store to ensure stats update instantly after every deposit or withdrawal without stale GET responses.
The database layer uses Node's built-in node:sqlite module — available from Node 22+ — which requires zero installation, zero native compilation, and works identically on Windows, macOS, and Linux. Every balance update is wrapped in an atomic BEGIN/COMMIT transaction so a crash mid-operation can never leave inconsistent state.
Challenges
Withdrawal policy enforcement Designing a policy system that feels intuitive to set up but is enforced server-side without loopholes took several iterations. The client mirrors the check for UI feedback, but the server is the single source of truth — the withdraw endpoint re-evaluates the policy on every request regardless of what the client sends.
Making Marcus feel human Getting an LLM to give specific rather than generic financial advice required careful prompt engineering. The system prompt is rebuilt on every request with the user's live data, and Marcus is explicitly instructed never to acknowledge being an AI, never to use bullet points, and to keep responses to 2–4 short paragraphs — matching the WhatsApp conversational format.
Native SQLite on Node 25
better-sqlite3 failed to compile against Node v25 due to missing C++20 support in the build toolchain. We solved this by switching entirely to node:sqlite — Node's own built-in SQLite module — eliminating the native dependency entirely.
Streak calculation edge cases A saving streak should remain alive if the user deposited yesterday even if they haven't deposited today yet. The streak algorithm accounts for this by checking from yesterday when today has no deposits, so a streak isn't broken at midnight.
What We Learned
- Behavioural friction, when designed well, is a feature — the withdrawal policies are the most impactful thing in the app because they change real behaviour, not just how data is displayed.
- Prompt context density matters more than model size. Injecting precise, structured user data into every LLM request produced far more useful responses than any amount of prompt tuning without it.
- The
node:sqlitebuilt-in is production-ready and dramatically simpler to ship than any native addon.
Built With
Node.js Express SQLite (node:sqlite) Llama 3.1 Groq API HTML5 CSS3 JavaScript bcryptjs express-session canvas-confetti Font Awesome Google Fonts
"Try it out" links
- Live demo: https://vault-production-b8c6.up.railway.app/
- Source code: https://github.com/LaplaceMkhabela/Vault

Log in or sign up for Devpost to join the conversation.