Most apps tell you what you spent yesterday. Proviso models what you will be worth tomorrow.
Personal finance for Australian households. It covers budget and cashflow, debts and assets, super and retirement, long-term projections, EOFY tax planning and CGT-aware investments. Proviso is for wealth building, tax efficiency and retirement planning, not day-to-day expense tracking.
Where it's going: app first, local-first. The plan is in docs/architecture.md:
- one app for iOS, Android and the web
- your data kept on your own devices
- sync between devices that's end-to-end encrypted, through a relay that only ever sees ciphertext; you can host the relay yourself
What runs today:
- The self-hosted NAS web app (Docker; e.g. Unraid, reached privately over Tailscale). It's the full product.
- The app (
apps/client). It's catching up feature by feature.
Both use the same calculation engine (packages/core), so they give the same numbers.
- Next.js 16 (App Router) + React, TypeScript
- SQLite via Prisma 5
- Self-hosted auth (scrypt + DB-backed sessions), household RBAC (CFO / Partner)
- Chart.js for visualisations
The repo is an npm workspace (see docs/architecture.md): apps/web is the Next.js app, packages/core holds the calculations and data rules.
npm install # from the repo root — installs and links every workspace
npx prisma migrate deploy # apply the schema (apps/web/prisma) to a local SQLite db
npm run dev # http://localhost:3000
npm test # tests for every workspace
npm run typecheck
# The app (apps/client) in a browser, for development:
npm run web -w @proviso/client # http://localhost:8080The Expo app for iOS, Android and the web. It's local-first (data on the device) and is being built towards the plan in docs/architecture.md. It has:
- a setup questionnaire that fills in typical costs
- Home, Spending, Wealth and Future, with What if? and the life-course projection to age 95
- Settings, with a 24-word recovery phrase and encrypted backups
- sync between devices through the relay (below)
Android development builds come from EAS (Expo's build service); iOS waits on an Apple Developer account.
To try it on a phone, install Expo Go, run npm run start -w @proviso/client and scan the QR code (same Wi-Fi; add -- --tunnel if the phone can't connect). If Expo Go is signed in to an Expo account, sign the CLI in to the same one first (npx expo login; on Windows with a Google sign-in use $env:BROWSER="none"; npx expo login --browser). To bring your NAS data across, use Settings → Download all your data on the NAS, then Choose the file on the app's welcome screen.
Set DATABASE_URL (e.g. file:./dev.db, relative to apps/web/prisma/) in apps/web/.env for local work — Next.js reads .env from the app's own folder.
No source code required. The latest image is published to GHCR on every push to master.
docker run -d \
--name proviso \
--restart unless-stopped \
-v proviso-db:/data \
-p 3000:3000 \
ghcr.io/jorget43/proviso:latestOr use the docker-compose.yml in this repo — docker compose up -d (no build needed, it pulls the image by default).
First visit creates the initial CFO account at /setup; thereafter the app requires login.
Run ./update.sh on a schedule instead of a Watchtower sidecar. It's two lines — docker compose pull proviso && docker compose up -d proviso — and up -d only recreates the container when the image actually changed, so a no-op night is silent, not a restart.
We tried Watchtower first and don't recommend it here: it checks for updates with an anonymous HEAD request, and GHCR returns 403 Forbidden on that specific request even for a fully public image (GET works fine — that's exactly what docker compose pull uses). Watchtower then fails silently, with nothing surfaced except its own debug logs — this ran undetected for two months on our own deployment before anyone noticed the version banner was stale. update.sh doesn't hit that code path at all, so it has no equivalent failure mode, and it needs no registry credentials.
Schedule it however your platform prefers:
# crontab -e — 3am daily
0 3 * * * /path/to/proviso/update.sh >> /var/log/proviso-update.log 2>&1Unraid: install the User Scripts plugin (Community Applications), add a new script pointing at update.sh, set its schedule to daily.
systemd: a timer unit calling update.sh works too if you'd rather not use cron.
If you'd rather have updates land the moment they're published instead of on a schedule, and don't mind managing a credential:
docker login ghcr.io -u <your-github-username> -p <PAT-with-read:packages-scope> # once, on the host
docker compose -f docker-compose.yml -f docker-compose.watchtower.yml up -dThe PAT isn't for access — the image is already public — it's a workaround for GHCR rejecting Watchtower's anonymous HEAD check specifically; an authenticated request doesn't hit the same wall. See docker-compose.watchtower.yml for details. If you ever suspect it's not working, docker logs watchtower will show auth: "not present" / 403 Forbidden on every failed check.
docker compose up -d --buildThis builds the standalone image locally and starts the proviso container on port 3000, backed by the named volume proviso-db (SQLite at /data/proviso.db). On first run the entrypoint applies migrations and seeds; on later starts it only applies new migrations.
Behind HTTPS (e.g. Tailscale Serve) set COOKIE_SECURE=true so session cookies carry the Secure flag and browsers get an HSTS header (remember HTTPS for this host for a year). Over a plain-http tailnet address leave it unset.
The Proviso phone app keeps the household on each phone and shares changes between a household's devices through a small sync relay. The relay only ever stores encrypted changes: it can't read the household's data, and it holds no names or email addresses.
docker compose --profile sync up -d # starts proviso-relay on port 8787
tailscale serve --bg --https=8443 http://localhost:8787Phones refuse plain http, so the relay needs HTTPS; Tailscale Serve gives it a certificate for the machine's tailnet name. In the app, open Settings → Sync and enter https://<machine>.<tailnet>.ts.net:8443, then on the other phone choose "Join a household" and scan the code the first phone shows. Its data lives in the proviso-relay volume; ./update.sh updates it alongside the main container once it's running.
Everything Proviso knows lives in one SQLite file, /data/proviso.db, in the proviso-db volume, alongside the three automatic .bak copies the container makes before each upgrade. Passwords and sign-in tokens in it are hashed, so the file can't be used to log in, but the household figures (budget, balances, transactions) are stored as plain data.
Proviso doesn't encrypt the file itself. Protect it where it's stored instead: that covers the backups too, and a key kept in the app's own settings would sit on the same machine as the data anyway.
- Unraid: put the disk or pool that holds Docker's data (usually the cache pool with
appdataand the Docker image) on an encrypted file system — XFS - encrypted or BTRFS - encrypted. Changing a disk's file system erases it, so move its data off first. Unraid then asks for the passphrase or keyfile each time the array starts. Keep that passphrase somewhere safe: without it the data can't be recovered. - Other Linux hosts: keep the Docker volume on a LUKS-encrypted disk or partition.
- Synology / QNAP: use the NAS's encrypted volume or encrypted shared-folder feature for the folder holding the volume.
- Copies that leave the NAS (cloud sync, USB drives): encrypt them, e.g. with an encrypting backup tool such as restic or an encrypted archive.
Built for phones first (Add to Home Screen opens it like an app), with the same layout on desktop:
- Home — what's left over each month, things worth a look, where you stand, bills coming up
- Spending — Budget · Actual spending (bank/card statement import)
- Wealth — Own & owe · Super · Investments
- Future — Long term (net worth, money in and out, home, school fees and retirement, projected until you're 95, with "What if?") · Next 2 years (cashflow)
The projection follows a life course:
- pay stops when each person retires
- super grows inside the projection and is paid out by the drawdown strategy you choose (spend what you need, the 4% rule, a set percentage, or the legal minimum)
- it warns if savings run out before super can be reached at 60
The NAS web app is the full product today. CLAUDE.md (Backlog → "Where each feature lives") tracks which features the app has so far.
Plus a seasonal EOFY view (May/June) and Settings, where "Your situation" switches on renting, childcare, school fees and parental leave. See CLAUDE.md for architecture and engine details.