# Provenance Axis

Case study 04 | Co-built | Live

A private multi-user fintech platform that puts a household's whole financial picture in one place: investments across exchanges and currencies, trading, dividends and loans, goals and a long-term wealth plan, expense tracking with categories, payment methods and budgets, reports, and an AI assistant that answers questions from your own data. I co-built it, leading the interface redesign, historical pricing and the sign-in experience.

- Role: Co-built. Led the UI revamp, historical pricing and the pre-login experience.
- Timeline: June 2026 to July 2026
- Stack: React, TypeScript, Node, Express, PostgreSQL, Prisma
- Live site: [provenanceaxis.com ↗](https://provenanceaxis.com)

1,800+ automated tests

10 app sections, from holdings to expenses

15 min access token lifetime

4 checks on every request

The problem

## Investments in many places, no single number to trust.

Holdings sat across several exchanges, in several currencies, tracked in disconnected sources. Getting **one true view of net worth **meant reconciling numbers by hand, and history was guesswork.

Before

- × Holdings spread across exchanges
- × Several currencies, converted by hand
- × No reliable history
- × Spreadsheets with extra steps

After

- → One consolidated portfolio
- → Live and historical FX conversion
- → Net worth charted from real dates
- → Investments, loans and spending in one app

What we built

## What we built

A TypeScript app end to end, built with the most formal security and release process of anything on this site.

### Portfolio

- → Overview with consolidated net worth
- → Holdings with transfers between portfolios
- → Dividends with reinvestment
- → Loans with repayments
- → Sub-portfolios with their own goals

### Markets and insight

- → Live prices and FX rates
- → Historical prices imported in bulk
- → Net worth snapshots by date
- → Trading desk and cross-listed positions
- → Reports with export, and an AI assistant

### Household money

- → Expense and income ledgers per household
- → Categories and payment methods
- → Monthly budgets for needs, wants and investments
- → A wealth plan: the pace needed to reach a target

### My part

- → Full UI revamp and design tokens
- → Pre-login page and sign-in modal
- → Historical FX rates per snapshot date
- → Mobile pass and a full-repo correctness review

The hard part

## Security checks on every request

Real financial data lives here, so nothing reaches a route without passing** the same checks in the same order**, every time.

Security headers Locked CORS Rate-limited sign-in

1

Verify

A short-lived token, held in memory and never in local storage.

2

Role

Admin and co-manager actions are checked on the server.

3

Portfolio

Every request is scoped to portfolios the user can access.

4

Validate

Body, params and query are checked against a schema.

Token expires: the app refreshes silently from a secure, httpOnly cookie.

Too many sign-in attempts: sign-in is rate limited to 10 tries per 15 minutes.

How it's built

## Engineering decisions

### Refresh-token rotation

Long sessions without long-lived tokens sitting in the browser.

### Hashed passwords

Passwords are hashed with bcrypt and never logged.

### Schema on every route

Zod validation on every input, not just the sensitive ones.

### Tested in CI

Vitest and Supertest run on GitHub Actions before anything merges.

### Gated releases

Changes move through testing and review branches before main.

### One deploy

Express serves the API and the compiled app from one service.

Screens

![Provenance Axis positions table](https://abbasvirjee.com/static/img/provenance-wide.webp)

Holdings across exchanges and currencies, with returns that stay current.

![Provenance Axis wealth plan](https://abbasvirjee.com/static/img/provenance-a.webp)

A wealth plan: the pace needed against where the portfolio actually is.

![Provenance Axis trading desk](https://abbasvirjee.com/static/img/provenance-b.webp)

A trading desk for short-term and cross-listed positions.

Holdings across accounts and currencies now add up to one number.

Holdings that lived across separate accounts now sit in one consolidated view, with history built from real transaction dates. Shown here with sample figures only.
