I wanted one place to see what was in my accounts, what my investments were worth and how much I was earning and spending each month. And I wanted to open it on my phone without logging into each bank.
My money.
Read onlySeptember 2026
What makes up this total
In this example, the panel has identified both accounts as belonging to the same person.
Cash in accounts
My investments
This is a demo, with no connection to a bank account.
Getting the accounts connected
We tried a few ways of getting data from the banks. The setup we ended up with combines open banking and an experimental investment reader, using Finanze for the first sign-in on the computer. In that flow, the password is entered locally and the server receives an encrypted session it can renew.
Once it's linked, I can check the panel on my phone with the computer switched off. The server keeps the latest successful read. If the bank asks me to confirm access again, the connector stops and waits for me to do it.
- 01API
Banks
Authorised accounts and investments
- 02GET
The connector
Fetches data and renews the session
- 03AES
Encrypted data
AES-256-GCM, tied to the owner and purpose
- 04PWA
Private panel
The server checks who can access it
The income figure looked too high
One of the monthly income figures looked higher than I expected. We found that the first version counted unrecognised credits as income. Moving money between my own accounts increases one balance, but doesn't mean I've earned anything. A refund or money coming back from an investment needs a different category too.
Now, anything unclear goes to review and confirmed transfers between my accounts stay separate. I can open each monthly total and see the transactions behind it, then correct a category if needed. Those corrections don't change the bank's original data. Savings stay pending while there are transactions that still need sorting out.
We use exact decimal arithmetic for the sums and match accounts by IBAN so the same balance isn't counted twice. We checked how the investment platform's own website handles currency too: a position's value can be reported in euros while its unit price uses another currency.
It needed to work on my phone
On the phone, long fund names were being squeezed into a narrow column. The amount and currency took up most of the row. We put the name and amount on separate lines, made the dates easier to read and added a portfolio total, split between cash and investments.
Connection settings now have their own section, and website analytics has a separate panel. For everyday use, there are four screens: overview, months, transactions and portfolio.
Keeping the data private
The server checks access
The panel requires the owner's sign-in. The server checks it when loading a protected page and when the panel asks the API for data.
The pairing link expires
With the local reader, I enter the password and complete the CAPTCHA on the computer. The server receives a session it can renew, through an encrypted handoff using ECDH P-256 and AES-GCM. The pairing link expires and works only once.
Sessions and records are encrypted
Sessions, balances and transactions are saved with AES-256-GCM encryption. Each record is bound to its owner, its bank and its purpose. The encryption key stays on the server.
An icon on my home screen
Installing the PWA gives me an icon on my phone. It still gets financial data from the server: responses use no-store, and the service worker doesn't cache them. Balances and transactions aren't saved in localStorage either.
A few more technical details
Changes to settings or transaction categories need a CSRF token and must come from the same site. A short lock on each connection keeps a refresh from racing a disconnect. The local reader uses its own single-use authorisation to hand over the session.
Read only is a limit we've put on the panel. A bank session may allow more, so the server also restricts which operations it can request and where it can send them.
Going back beyond the last few months
The first version requested the last 90 days of transactions. To go further back, we added longer reads where the bank allows them, followed the result pages and saved an encrypted checkpoint. That means recovery can stop and carry on later. Older transactions are kept when the next bank response covers a shorter period.
The archive code passed the local tests but failed on the live server. We changed the write sequence: save the data in chunks, then update the index once those chunks are in place. A compare-and-swap (CAS) check prevents an older update from overwriting a newer one. We then checked the fix with real bank refreshes.
How far back we can go still depends on the bank, and it may ask for a new authorisation. The panel shows the dates it has received. It can't recreate an old fund valuation just from cash transactions.
Working with Codex
We also split the work into parallel tasks: reading bank data, the Windows connection, the interface and security checks. Once the changes were brought together, we tested things like expired links, repeated pages, duplicate accounts and a disconnect happening during an update.
I kept trying it on my phone and pointing out what didn't work for me: a number that looked too high, a name that was cut off or a screen that was hard to follow. Codex helped turn those observations into code and check that the changes didn't break what already worked.
Built with Codex, based on what I needed and what came up while using it. The screens in this post are recreations with fictional data.
Tools and documentation: Enable Banking · Finanze · Web Crypto API.