Field notes
Engineering ·

One core, many apps: how a whitelabel banking platform works

A whitelabel banking platform is one banking core with a replaceable front end. The core is the part that has to be correct: a double-entry ledger, customer accounts, KYC and onboarding, card issuing and processing, crypto and stablecoin rails, and the operator console that runs behind all of it. The app is the part the customer sees — its name, its colors, the shape of its screens, the subset of features it turns on. Whitelabel means one core can wear many faces without being rebuilt under each one. One core, many apps.

Said plainly, that is a claim about where the seams sit. Everything that touches money is shared, audited, and reconciled once; everything that touches the customer is configured per tenant. The engineering that matters is keeping that line honest — letting the front end differ freely while the core it stands on never forks. This is a teardown of how the line gets drawn, and why drawing it with design tokens beats drawing it with a fork.

What never varies: the core

Start with the half that is not allowed to be creative. A transfer posts the same way whether the app labels it a send, a payment, or a payout: two entries on a double-entry ledger, and nothing is ever deleted — the ledger only adds, so a refund is a counter-entry that references the original, not an erasure. Accounts, balances, and authorization holds live here. So do KYC and onboarding, card issuing and settlement, and the crypto and stablecoin rails. Behind them sits one operator console where every privileged action is reason-required and lands in an audit log.

There is exactly one of each of these, and that is the point. Settlement, reconciliation, and compliance are hard to get right and expensive to get wrong, so you build them once and let every app inherit the result. When a tenant onboards a customer, the KYC tier the core decides follows that customer into the console no matter which app's screens captured the documents. The core does not know or care what the app on top is called — it sees accounts and entries, not brands.

What varies: design language, information architecture, feature mix

Everything above the core is free to move, along three axes. The first is design language: color, typography, corner radius, spacing, motion — the visual grammar of the brand. The second is information architecture: what sits on the home screen, how flows are named and ordered, which action is one tap away and which is three. The third is feature mix: which modules are switched on at all. A lean transfers app and a dense super-app can run the identical core and share not a single screen.

The product page at /products/pay walks five worked directions, and they read best as examples of infinitely many rather than a menu of five. Halcyon is a clean exchange — restrained, few surfaces, the trade front and center. Lumen is a super-app where money moves inside the conversation and chat payments, subscriptions, and earn share the home screen. Fern leads with transfers — batch payouts and rule-based FX conversion first. Volt is a stablecoin-dark build with a salary sorter and team cards. Meridian is multi-currency: a EUR account beside USDC, a GBP travel balance, and a BTC account where card rewards land. Five brands, five information architectures, five feature mixes — one ledger, one KYC, one card processor underneath all of them.

None of that means the product is five apps. The five are directions the same pipeline can take; the number that matters is not five, it is however many tenants you onboard. Each one is proof that the front end can differ down to the shape of the home screen while the core beneath it never notices.

The core is the part you are not allowed to reinvent; the app is the part you are not allowed to constrain. A whitelabel platform is the discipline of keeping both of those sentences true at once.
— Protocore · Platform engineering

Why token-level theming beats forking the codebase

There are two ways to make one product look like many. The obvious one is to fork: copy the codebase per customer, restyle it, ship it. It works for exactly one release. After that, every bug fix and every regulatory change has to be applied N times across N drifting copies, the copies fall out of sync, and the security surface you have to reason about multiplies by the number of customers. Forking turns one system you understand into many you do not.

Token-level theming inverts it. There is one codebase. A brand is a set of values — tokens for color, type, radius, and spacing; a component configuration; a feature-flag set — resolved at build time into a branded app. Change a token and every screen that reads it moves together, because nothing hardcodes the value it should be pulling from a token. A fix to the transfer flow ships to every tenant at once, because there is only one transfer flow to fix. The brands differ in their configuration, not in their source.

Where the design system sits

The tokens are not a loose idea; they are a shipped artifact. The Protocore Design System — /products/pds — is 144 design tokens and 889 utility classes: the vocabulary every app is themed in. It is dark-first, token-driven, and radius zero by default, and the same token set is emitted for web, .NET, and native, so a color defined once means the same color in a React screen, a desktop console, and a native mobile build. A tenant's brand is expressed as overrides against that vocabulary, which is why re-skinning an app is a configuration change and not a rewrite.

This is also what makes the front end genuinely free rather than free-within-a-template. Because the design language lives in tokens and utility classes instead of in one-off stylesheets, a designer can push a tenant a long way from the reference build — its own palette, type, density, and rhythm — without touching the core or forking the app. The paint goes deep, but it is still only paint; the plumbing under it is shared.

The app is a configuration, not a build

Feature mix follows the same rule. Every module — chat payments, earn, crypto, cards, subscriptions, team cards, referrals — is a switch thrown per tenant, and a flag can roll out to a slice of users before it goes wide. Operators flip them from a config panel, not from a deploy. So the difference between a bare transfers app and a full super-app is not two projects; it is two configurations of one project. The reference app every tenant starts from is a neutral, Protocore-branded build with the whole menu present — each whitelabel is that build with tokens overridden and modules turned on or off.

The honest boundary

One core, many apps is worth the machinery only when you actually have many apps — or one app you intend to re-skin, re-arrange, and re-scope over time without rebuilding the bank each round. If you are shipping a single fixed app that will never wear a second brand, you do not need a whitelabel platform; you need an app. Where it earns its keep is the case it was built for: a platform standing up branded banking products tenant after tenant, each one visibly its own, none of them a fork. The full teardown — the five directions, the reference app, and the operator console behind them — is at /products/pay, and the design system underneath is at /products/pds.

Have a system to build?

Tell us the problem. We'll come back with an architecture and a plan.

Contact us