DOC: WORK-RUBIBUILDING IN PUBLIC
REV 2026.08

Rubi — multi-currency money, built for two lives.

A personal finance tracker for people earning in one currency and spending in another — the "japa generation" holding NGN and USD side by side. Offline-first, built solo across mobile and backend.

Personal · ~85%React Native · ExpoSpring BootPostgreSQLZustand · TanStack Query

Approach & Execution

Built solo, mobile and backend both: accounts across multiple currencies with net-worth aggregation, transactions with cross-currency transfers, budgets (fixed or fill-up-to, with rollover capped at 2x), savings goals with multi-currency contributions, recurring transactions, reminders, and reports — plus a daily check-in habit loop, offline-first throughout.

The two problems that don't show up on a feature list, but decide whether an app like this can be trusted with real money, are auth across two very different platforms, and keeping exchange rates fresh without hammering a third-party API on every transaction.

Two Platforms, Two Threat Models

Same login, two different security models — because the platforms demand it, not because of a shared abstraction. On web, the JWT lives in an httpOnly cookie: JavaScript can't read it, so it's protected against XSS. React Native has no browser cookie jar, and httpOnly cookies don't carry the same guarantee on native storage — so the token goes in the response body instead, and the client owns secure storage.

There's also a Google OAuth angle: without provider-separation logic, someone who signs up with email and password, then later signs in with Google using an account tied to the same email, can silently link into or take over an account that isn't theirs. Rubi keeps providers separated per account instead of merging on email match alone.

Cutting exchange-rate calls by 92%

Every transaction touching an exchange rate needs a rate lookup — naively, that's an external API call every time. The fix: a daily scheduled job pre-fetches rates for every currency your accounts actually use, so the API gets hit once a day instead of once a transaction. On read, a cache miss doesn't call the API — it falls back to the inverse rate, then bridges through USD, before ever reaching a live call. Volatile currencies get a shorter TTL than a stable pair, so a real move doesn't sit uncorrected for a week. Net result: a 92% cut in external exchange-rate API calls, with no risk of balances going stale.

Where It Stands

~85% complete. Core accounts, transactions, budgets, savings goals, and reporting are built and working end to end, offline-first throughout. What's left before launch: onboarding wired fully end-to-end, the backend deployed, and the Budgets/Analytics tabs hidden for a tighter MVP scope.

Key Takeaways

“The same auth system” across platforms is a trap — the delivery mechanism has to match the platform's actual threat model, not a shared abstraction pretending web and native have the same attack surface. And caching isn't “add Redis.” It's deciding exactly what's allowed to be stale, for how long, and what happens the moment it is.

© 2026 HENRY TAIWO