All projects
Advisor platform · Case studyLive demo

Always-On-Advice

This platform brings an advisory firm’s client work into one place. It covers households and deals, account transfers from the client’s statement to funded accounts, the ACAT book, statement-based asset mapping, trade requests and the closeout checklist, and each role sees only what it should. The Transfer Tracker, the Statement Analyzer and Trade Request feed into it.

Role
Design, engineering, rollout
Users
Advisors, client service, operations, admins
Stack
Next.js · Prisma · Postgres · Google Cloud
The Operations view of Kofi Okafor’s account transfer shows $412,880 moving from Edward Jones to a joint account at Apex, its funding plan from the statement, the transfer at stage 2 of 3, and a link to the same transfer in the Transfer Tracker demo.The Operations view of Kofi Okafor’s account transfer shows $412,880 moving from Edward Jones to a joint account at Apex, its funding plan from the statement, the transfer at stage 2 of 3, and a link to the same transfer in the Transfer Tracker demo.

The build in numbers

Four months
June 29 to October 29, 2026
1,146
merged pull requests, 1,087 of them mine
360
production deploys, plus 671 to dev and 2,500+ green CI runs
137
database migrations behind 77 models, 5 schemas, about 350 row-level-security policies and 45 triggers
62 pages
88 API routes and about 180,000 lines of TypeScript
918
test files, about 170,000 lines, and 64 browser specs

I counted these from the production repository and its GitHub history on November 2, 2026.

Overview

This platform brings an advisory firm’s client work into one place. It covers households and deals, account transfers from the client’s statement to funded accounts, the ACAT book, statement-based asset mapping, trade requests and the closeout checklist, and each role sees only what it should. The Transfer Tracker, the Statement Analyzer and Trade Request feed into it.

The problem

What we knew about a household was spread across the CRM, a transfer spreadsheet, the custodian’s portal, chat threads and a handful of single-purpose tools. Nobody could follow one client from the signed deal to the funded account in one place, and each hand-off between tools was a chance to lose a detail.

What I built

  • A household and client directory sits on top of the CRM’s records, and each client’s accounts, AUM, beneficiaries and held-away accounts appear on one Client Portrait.
  • A funding pipeline of deals opens the account transfer when a deal commits, so a transfer starts from the sale instead of a re-keyed form.
  • Asset Transfer is a board that runs from decision to funded. Each transfer gets a funding plan built from the client’s statement, gates hold it until its checklist and paperwork are done, and it’s archived instead of deleted.
  • The ACAT book is read live from the Transfer Tracker, with its fees and position reconciliation. The custodian library goes the other way, since this platform owns it and pushes it to the tracker.
  • Asset Mapping brings in the Statement Analyzer’s results for each household, which are reviewed here and linked to the client and transfer they belong to.
  • Trade Requests are built into the platform, including the queue, approvals and audit trail. Each client’s risk score and instructions are drawn from approved requests.
  • While a transfer is in flight, a closeout checklist runs in Slack and its status comes back to the transfer.
  • The platform also has Command Center, search across the whole app, a central review queue, data-health checks, and an audit log of business events and every field change on deals and transfers.

In the app

Key decisions

Row-level security, not screen checks

Every query runs as a database role with the user’s role and book set on the connection, so a new screen can’t show an advisor another advisor’s client. There are about 350 policies, with tests that read the data as each role.

Read the other apps, don’t copy them

The ACAT book comes from the Transfer Tracker when it’s viewed, and only the links between records are stored here, so each fact has one system that owns it.

Audit in two layers

The app records each business event, and database triggers record every field change on deals and transfers, so the trail doesn’t depend on remembering to log.

Move the backend last

Trade Requests’ screens are native here, its records have been copied into this database, and there’s a backend that can serve them. In production that backend is still switched off, so the standalone service answers. The demo runs with it switched on.

Security and encryption

Every response is served over HTTPS with HSTS and a strict Content Security Policy. The app reaches its Postgres database over a private network, the database refuses any connection that isn’t encrypted, and Google Cloud encrypts the data and its backups at rest. Services sign in to each other with short-lived Google identity tokens, deploys use keyless workload identity, and secrets stay in Secret Manager rather than the code. Sensitive fields aren’t encrypted one by one; instead, row-level security in the database decides who can read them, internal client ids are withheld from everyone but operations and admins, and account numbers show only their last four digits. The demo keeps the same headers, row-level security and masking on invented data, and the Worker replaces each visitor’s IP address with a keyed hash before the app sees it.

Architecture

Production
Staffbrowser · company sign-in
Next.js appCloud Run · a role per user
Postgresrow-level security · audit triggers
Connected appstracker · statements · trades
Public demo
Cloudflare Workeraoa.jeremyhamann.dev
Containersame app · demo build
Neon Postgressynthetic seed · nightly reset

Next.js · TypeScript · Prisma · Postgres · Auth.js · Terraform

The demo

The demo runs the production app’s code on a fictional firm, with company sign-in replaced by four staff personas: an advisor with her own book, client service, operations and an admin. The Transfer Tracker, the statement pipeline and the data warehouse are replaced by stand-ins that answer in their real formats inside the container, and calls to Google are refused. Each household, transfer, statement and trade request links to the same record in the other live demos. Trade Requests runs on its in-app backend here, even though that backend is still switched off in production. Slack closeout checklists, trade-request messages and operations alerts land in an in-app Outbox instead of being sent. Uploads are off, and a sample statement stands in. Dual-View, which never ran in production, is on for the showcase households and says so. The data resets daily at 07:00 UTC.

See it running

Sign in as any demo user. Nothing you do leaves the demo, and it resets every day.

Open live demo