Inside Nopan’s Ledger - Part 1
At high payment volumes, unusual events stop being unusual.
Messages arrive twice. A client retries after receiving no response. A confirmation is delayed. A service restarts halfway through an operation. An infrastructure failure occurs at exactly the wrong moment.
The payment experience may last only a few seconds, but the resulting financial record must remain correct indefinitely.
Every balance movement must be recorded once, attributed to the event that caused it and available for investigation or reconciliation later. The system must preserve those properties as volumes grow, products change and failures inevitably occur.
This article is for the payment specialists, architects and operators responsible for that infrastructure at high-volume merchants.
It is the first in a series examining Nopan’s ledger: the system of record for money moving through our platform. We will begin with its central architectural idea - a separation between the part that decides what should happen and the much smaller part permitted to change a balance.
The system of record for money in motion
Every meaningful development in the lifecycle of a payment is represented as a financial event.
The ledger’s job is to record the accounting effect of those events in the correct order, without losing or rewriting their history.
Two principles are fundamental:
Immutable: once an entry has been recorded, it is never edited or deleted.
Append-only: the financial story can only be changed by adding another entry.
If an amount is booked incorrectly, we do not go back and modify the original entry. We record an equal and opposite entry that reverses it. Both remain part of the permanent history.
The idea is centuries old. In a bound paper ledger, an accountant would record a correction rather than tear out the original page. A modern ledger applies the same discipline while making the record durable, searchable and enforceable by software.
For payment specialists, this provides an essential operational property: the ledger always shows both what happened and what was subsequently done about it.
The rule that cannot be broken
Nopan’s ledger uses double-entry bookkeeping. Every debit must be matched by an equal credit.
Within the ledger, value cannot appear or disappear without an equal and opposite entry. An operation must balance to the smallest unit of the relevant currency.
Debits must equal credits. If an operation does not balance, it is rejected in full and never applied in part.
This rule is enforced by the booking engine, not left to individual application services.
A service cannot accidentally book one half of an operation and promise to complete the other half later. The complete set of transfers either commits atomically or does not happen.
This is the foundation. But maintaining that invariant is only part of operating a trustworthy ledger at scale.
The system must also decide which accounting treatment applies, prevent retries from producing duplicate bookings, preserve the context behind every movement and recover safely when the outcome of a request is initially uncertain.
That is why Nopan’s ledger is divided into two planes.
Two planes: one decides, one moves the money
The ledger consists of a control plane and a data plane.
The control plane interprets and orchestrates. It receives an event, determines its accounting treatment, prepares a balanced booking instruction and records the lineage surrounding it.
The data plane executes. It receives the prepared instruction and commits the corresponding balance movements atomically.
The separation is intentionally asymmetric.
Most of the system belongs to the control plane. The data plane is a single, tightly controlled component: the TigerBeetle booking engine.
Only the data plane can change a balance.

This boundary limits the surface on which financial state can be changed. The control plane can interpret an event and decide what should happen, but it cannot move money itself. The data plane can move money, but it does not decide the business meaning of the movement.
The control plane: deciding and explaining
Consider a payment event entering the ledger.
Other Nopan services communicate in the language of their own domains. The first responsibility of the control plane is therefore to translate the incoming domain event into a canonical financial event: one internal representation that the rest of the ledger understands, regardless of where the event originated.
The event is then routed to the appropriate account in the Chart of Accounts. The routing decision considers attributes such as account type, currency, payment provider and operation. When several accounts qualify, the most specific match is selected.
Once routed, the ledger determines the relevant booking rule.
A booking rule describes how a particular financial event becomes debits and credits. Exactly one rule may apply. The rules service evaluates all possible matches so that an ambiguous second match becomes a visible error rather than a silent and potentially incorrect booking.
The selected rule produces a concrete, balanced booking request:
Which accounts should be debited?
Which accounts should be credited?
For what amount and currency?
Under which accounting code?
Before the booking engine is called, the control plane creates an immutable lineage record for the event.
That record connects the original financial event to the booking entries and balance changes it produces. It also preserves the richer context that does not need to be stored inside the accounting engine: external references, payment provider, operation, reason and other information required for investigation or reconciliation.
The lineage progresses through a small state machine as the operation advances. It records that the booking has been submitted, that the movement has been durably confirmed and that the resulting events have been published downstream.
This allows Nopan to answer an important question in either direction:
Given a payment event, which balances did it change, and given a ledger entry, which event and business context caused it?
For a high-volume merchant, that relationship is essential. A balance alone can say what the financial position is. Lineage helps explain how it got there.
The data plane: changing balances
Once the control plane has prepared a valid booking request, it passes that request through a narrow interface to the data plane.
The data plane is implemented using TigerBeetle, a database designed specifically for financial transactions.
Its responsibility is deliberately focused: translate the booking request into transfers and commit them as an atomic linked batch.
The entire batch succeeds or the entire batch fails. Partial financial writes are prohibited.
The data plane also manages the accounting lifecycle of a movement. A booking may begin as pending and later become posted, cancelled or reversed. Those states are mapped onto TigerBeetle’s native transfer model while preserving the complete financial history.
Only the minimum information needed to perform the accounting write enters the engine:
Accounts
Amount
Currency
Accounting code
The broader business and payment context remains in the control plane’s lineage record. When entries are read back, that context can be reunited with the raw accounting information.
This keeps the money-moving core narrow and predictable while allowing the surrounding payment system to retain the information required by operations, finance and reconciliation teams.
Two protections against duplicate bookings
At scale, systems must assume that events will be delivered more than once.
A queue may redeliver a message. A client may resend a request because it never received the response. A connection may fail after the booking was committed but before the caller learned that it succeeded.
A retry is necessary for reliability. But without idempotency, the retry itself can become a financial error.
Nopan therefore applies a separate idempotency control in each plane.
In the control plane, the event’s identifier is associated with its immutable lineage record. If the same event arrives again, the existing record identifies it as a duplicate before another booking is prepared.
The data plane provides an independent backstop. Each write includes a deterministic guard derived from its idempotency key. If the same write reaches the engine again, the guard prevents the entire batch from being applied a second time.
The two mechanisms address different failure points.
The control-plane record catches most duplicates early and preserves their history. The data-plane guard protects the balance movement at the final point of execution.
This means that deduplication does not depend on a single application check succeeding under every possible failure condition.
A narrow and asynchronous boundary
The two planes communicate through a narrow interface.
A prepared booking request travels from the control plane to the data plane. Once the engine has committed it, confirmation travels back asynchronously.
The confirmation contains the engine-generated identifiers, timestamps and outcome needed to advance the lineage record. The control plane can then mark the booking as confirmed, publish the resulting events and record their publication.
This asynchronous return path keeps slower contextual work away from the critical money-moving path. A delay writing additional metadata, for example, should not expand the time required to commit a financial movement.
The responsibilities remain clear:
The control plane decides what should move and records why.
The data plane commits the movement.
The confirmation reconnects the durable accounting result with its full financial context.
Neither plane needs to take on the other’s role.
What distinguishes Nopan’s approach
Double-entry bookkeeping is not unique to Nopan. Neither is using a specialised financial database.
What distinguishes the implementation is how narrowly we define the component allowed to change balances and the independent controls we construct around it.
The architecture provides several practical properties.
A smaller money-moving surface
Only one component can change a balance. This reduces the amount of code and infrastructure on which financial correctness directly depends.
Atomic financial operations
A balanced operation commits completely or not at all. The system cannot expose half of a double-entry movement.
Defence against retries in both planes
The lineage record prevents an event from being processed twice, while the engine-level guard prevents the underlying movement from being committed twice.
End-to-end explainability
Every financial event is connected to the entries and balance changes it caused, while the full payment and business context remains available outside the accounting engine.
Accounting rules separated from execution
The decision about which accounts to debit and credit is made before the booking reaches the engine. The engine executes a balanced instruction without needing to understand the payment product that produced it.
Freedom to evolve
The data plane sits behind a uniform engine interface. The wider ledger is not required to understand the internal implementation of TigerBeetle, and another engine could be introduced without changing the upstream event-processing model.
Why this matters at high volumes
Architecture only matters when it changes operational outcomes.
For a merchant processing payments across products, providers, markets and currencies, this separation is designed to provide:
Safer handling of retries and uncertain outcomes, without turning reliability mechanisms into duplicate bookings.
More reliable reconciliation, because financial events can be traced to their exact accounting effects.
Clearer incident investigation, with an immutable record of the original event, the resulting entries and subsequent corrections.
Controlled accounting change, because booking decisions remain separate from the mechanism that commits balances.
A smaller financial core to test and protect, reducing the number of components trusted to move money.
A foundation that can evolve with volume and complexity, without weakening the ledger’s central invariant.
These properties do not eliminate failure. Payment infrastructure will always encounter timeouts, duplicate messages, interrupted processes and unavailable dependencies.
The objective is to make those failures predictable and to ensure that none of them can quietly rewrite financial truth.
Coming up in this series
This article introduced the ledger’s central mental model. The next articles will examine the controls and design decisions behind it in greater detail:
Two Planes, One Ledger
The mental model, the two planes and the rule that every operation must obey. (You are here.)
Exactly Once, However Many Times You Ask
Retries, duplicate events and the guard that prevents a payment from being booked twice.
The Database That Refuses to Lose Your Money
TigerBeetle, replication, consensus and what it means for committed financial state to remain committed.
Bookkeeping as Configuration
How declarative rules express the accounting treatment of financial products as data.
The Core Doesn’t Know What a Database Is
How ports and adapters keep the accounting domain independent of Spring, TigerBeetle and AWS.
If you're building payment infrastructure at scale and these are the kinds of challenges you're solving, we'd love to compare notes. Get in touch - we're always happy to exchange ideas.

