A short tour of how SumaFlow Kaban is built. Everything that touches your household's money runs on your own phone. Kaban is in open beta, and this page describes the code running on testers' devices right now — the same pattern as its siblings, SumaFlow Minutes and SumaFlow Digest, applied to a problem where the usual answer is to ask for your bank login.
The architecture in one diagram
The whole product fits in a single picture. Everything inside the boundary runs on your device. There is no inbound path for your data at all — no bank feed, no sync, no account — and the only outbound path is an export or a backup you ask for and place yourself.
The network manifest
The same contract in machine terms. Kaban's own code opens no socket and speaks no HTTP; the single entry below is Google Play Billing, which is the only way Android permits an app to sell anything, and which carries no ledger content:
outbound_hosts: google play billing only # a product id and a purchase token
ledger_uploaded: none
bank_connections: none
accounts: none
telemetry: none
analytics: none
crash_reporting: none The promises
These are the product. A violation is a defect, not a trade-off to be weighed against a feature.
- No bank credentials, ever. There is no screen that asks for a banking username, password, one-time code or card number, and no aggregator behind the app. This is a rejection, not a missing feature.
- No financial data leaves the phone. Accounts, transactions, budgets, goals, members and net worth are created and kept on your device. We operate no server that could receive them.
- The ledger is encrypted at rest — a SQLCipher database whose 256-bit key is generated on your device at first launch and held in the Android Keystore.
- No account, and nothing to sign up for. There is no login, no profile and no customer record anywhere, because there is nowhere to keep one.
- It works with the radio off. Every feature functions in airplane mode, because none of them was reaching out in the first place.
- Your data is portable and yours. Full CSV and JSON export plus an encrypted backup in a standard format, free on every tier, forever.
- The boundary is tested, not asserted. The checks described below run on every build.
What runs where
| Stage | Where it runs | How |
|---|---|---|
| Entry | On-device | Quick add in three taps, splits, transfers — written straight to the encrypted ledger |
| Statement import | On-device | A CSV file you downloaded yourself, parsed locally; column mapping is remembered per bank, and the whole commit is undoable |
| Categorization | On-device | Your rules plus payee memory — deterministic, and no cloud model is consulted |
| Budgets, goals, net worth | On-device | Derived from your own ledger by the envelope engine; nothing is fetched to compute them |
| Reports and the family PDF | On-device | Rendered on the phone from the same ledger the screens read |
| Storage | On-device | SQLCipher (encrypted SQLite); the app refuses to open the ledger at all if the SQLCipher library is not loaded |
| Export / backup | On-device → where you put it | User-initiated, confirmed with a biometric or device screen lock, and limited to the steward and partner roles |
The network posture — the honest part
Kaban's own code makes no network calls. It opens no socket, contains no HTTP client, and declares no network-capable package beyond the one named below. Try it in airplane mode — nothing degrades, because nothing was reaching out.
The release build nonetheless carries Android's INTERNET
permission, and it is worth understanding exactly why.
Google Play Billing — the only mechanism Android
permits for selling anything in-app — depends on Google's telemetry
transport and constructs it during billing setup. Excluding it was
tried and fails the build outright. So the permission arrives
transitively with billing, and the app's own manifest never asks for
it. The three permissions Kaban does declare for itself are all local:
biometric authentication, local notifications, and the ability to
re-arm those notifications after a reboot.
What that costs and does not cost, plainly: billing carries a product id and a purchase token. By construction it has no access to your accounts, balances, transactions, payees, envelopes or goals. Google learns that a purchase happened — as it would for any paid Android app, because Google processes the payment — and nothing about your money. During the open beta nothing is even for sale: the billing library ships dormant and no purchase is ever attempted.
An earlier version of the Kaban privacy policy said the release build
requested no INTERNET permission at all. That stopped being
true when SumaFlow Kaban Pro was built. The
Kaban privacy policy now explains what changed rather
than quietly dropping the paragraph, and the test below was rewritten to
assert the new boundary instead of exempting it.
The tests that hold the line
Claims on a marketing page age badly; a test that fails the build does
not. test/data/no_network_test.dart asserts four things on every run:
- No network package sneaks in.
http,dio,grpc,web_socket_channelandconnectivity_plusare banned as dependencies, and the billing package is named as the single documented exception — so adding a second one is a deliberate act, visible in a diff. - Kaban never asks for
INTERNETitself. The app's own manifest must stay clean, which keeps the permission traceable to exactly one cause. - No sockets or HTTP clients anywhere in the source. The whole app directory is scanned.
- The ledger never crosses the billing boundary. Nothing in the billing code may import the ledger repository, the database, or even a money formatter. If that ever fails, financial data is one refactor away from leaving the phone — so it is caught at the import level, before it can be.
A fifth check pins the font library's runtime-download switch to off, so typography loads from bundled assets rather than fetching at runtime.
Encryption, in brief
| What | How it is protected |
|---|---|
| The ledger | A SQLCipher database. The 256-bit raw key is generated with a secure random source on first launch and stored in Keystore-backed secure storage — StrongBox where the device supports it. The key never leaves the device, and the app refuses to open the database unencrypted if the SQLCipher library is missing. |
| Member PINs | Stored only as Argon2id hashes. The PIN itself is never written down, and a hash cannot be turned back into one. |
| Backups | A standard SQLCipher database re-keyed to a passphrase you choose, produced with sqlcipher_export. SQLCipher runs its own key derivation with a per-file random salt. Any SQLCipher tool can open it with your passphrase; SumaFlow cannot, because we never see it — and there is nothing custom here to lose the recipe for. |
| Restore | Happens inside one transaction. A wrong passphrase or a truncated file leaves your current books untouched rather than half-replaced. |
| The app itself | An optional app lock using the device biometric with a PIN fallback, plus per-member PINs for switching profiles. |
The household is a permission model, not a mailing list
Kaban is built for several people sharing one set of books, which makes "who can see what" an architectural question rather than a setting. One role-by-action matrix lives in a single file and is enforced in the domain layer, so the screens cannot quietly disagree with it:
- Steward — everything, including deleting the household.
- Partner — everything except deleting the household. Partners can export.
- Member — adds their own entries and contributes to goals; sees shared budgets and goals; sees account balances only when the household turns open books on. No management, export or backup.
Members are unlimited and free on every tier. Removing someone is a soft delete, because the database refuses anything else — entries reference the member who recorded them, and the ledger is an audit trail. Removing a person must never rewrite what they recorded.
What this design does not protect against
The honest limits. A privacy page that lists only strengths is marketing.
- A compromised device. Encryption at rest protects a phone that is off or locked. Malware running as you, on an unlocked and rooted device, is outside what any app of this kind can defend.
- Anyone who knows your PIN. Per-member PINs separate profiles; they are not a defence against someone who watched you type one.
- The home-screen widget. If you place the Pulse widget, the amount left and the days remaining are rendered on your home screen, outside the app lock — by design, since a glanceable number is the point. Leave it off if that is not a trade you want.
- Backups you place badly. The backup is encrypted with your passphrase, but where it lands is your decision. A weak passphrase, or a copy synced to a cloud drive by another app, is a risk we cannot reach.
- Losing everything. There is no server copy. Uninstalling deletes the household, and a forgotten backup passphrase is unrecoverable — nobody holds a spare. This is the direct cost of the architecture, not an oversight.
- The purchase record. When Pro eventually goes on sale, Google will know a purchase happened. We cannot make that untrue while selling through Play.
What is not built yet
Stated here rather than left for you to discover: there is no sync between devices (one phone per household for now), no PDF statement parsing (CSV works today), and no price refresh for investments — holdings are valued by hand, because refreshing them would mean a network call, and that is a decision with a privacy cost rather than a small feature. If sync ever ships, it ships end-to-end encrypted, with the relay seeing ciphertext only, or it does not ship.
Read the policy too. This page describes the design; the Kaban privacy policy is the commitment. Privacy questions go to [email protected], and beta reports to [email protected].