Problem
Recording shared expenses is only half the problem. A useful settlement system must calculate balances without floating-point drift, minimize payments when the group is small enough, fall back predictably when exact search becomes expensive, and keep proposed payments separate from real-world confirmation.
Constraints
- Money calculations must remain exact at the stored currency precision.
- Minimum-payment search grows too expensive to claim as exact for arbitrary group sizes.
- The fallback must settle every balance and produce the same result for the same input.
- Group roles must constrain expenses, exports, membership, and settlement actions.
- A sender marking a payment as sent is not equivalent to the recipient confirming it.
- The hosted application may use the TypeScript solver even when an optional WASM implementation exists.
Architecture
- 1
Expense ledger
Stores payers, shares, currency context, and integer-cent values inside a group boundary.
- 2
Balance reducer
Converts expense history into a zero-sum set of member balances.
- 3
Strategy selector
Uses exact minimum-payment search for up to 12 non-zero balances and deterministic fallback above that limit.
- 4
Settlement workflow
Tracks proposed, sent, confirmed, and rejected payment state separately from expenses.
- 5
Authorization and audit
Applies group roles and records relevant state-changing actions.
- 6
Optional WASM solver
Provides a matching C++ implementation without misrepresenting the hosted runtime.
Design decisions
Integer cents instead of floating point
Binary floating point can introduce fractional-cent errors during repeated splits and reductions. Integer storage makes equality, zero-sum checks, and settlement invariants exact at currency precision.
Bound the exact solver honestly
The exact search minimizes the number of payments for up to 12 non-zero balances. Above that limit, a deterministic greedy strategy settles the group without claiming global minimality.
Separate calculation from confirmation
A computed plan is a recommendation, not proof that money changed hands. Sender and recipient actions form a separate workflow with auditable state transitions.
Treat WASM as an optional engine
The C++ implementation is useful for parity and runtime experimentation, but the normal hosted build uses the tested TypeScript solver. The interface reports which strategy and engine were used.
Correctness and testing
- Solver tests check balance conservation, deterministic output, exact-search behaviour, and fallback settlement.
- Server tests cover authentication, group authorization, settlement confirmation, fixed-point storage, and upload validation.
- Web tests and production builds verify the user-facing group and settlement flows.
- The C++ implementation is kept as a matching optional solver rather than silently substituted in public claims.
Failure cases
- Exact search becomes combinatorially expensive as the number of non-zero balances grows.
- A greedy fallback can use more payments than the global minimum.
- Currency conversion context does not remove exchange-rate or rounding policy decisions.
- External receipt OCR can fail or require explicit third-party data handling consent.
Results
- Exact minimum-payment plans for groups with up to 12 non-zero balances.
- Deterministic settlement for larger groups without claiming global optimality.
- Integer-cent accounting across expense and balance calculations.
- Role-aware groups, recipient-confirmed settlements, audit history, exports, and live updates.
Limitations
The system is intentionally described within the boundaries supported by its implementation and evidence.
- CashFlow records obligations and confirmations; it does not hold funds or connect to a bank.
- The hosted application is a public beta, not an audited financial product.
- WASM is optional and is not claimed as the current hosted solver runtime.
- Users should not enter financial data they would be uncomfortable storing in a hosted PostgreSQL database.