The real Beancount and Fava run on the device, so the figures you see are the ones
bean-check and Fava give you on a desktop. No account, no sign-in, no server run by this project.
Website · What it is · See it · What it does · How a save works · Editions · Privacy · Build · Docs
Plain-text double-entry accounting on Android, for Beancount
ledgers. The app embeds CPython and runs the real Beancount and
Fava on the device, so what you see on a phone is what
bean-check and Fava produce on a desktop. It can also act as a client for a Fava server you
host yourself.
Note
Beanbook is in early development and changes often. There may be bugs and inconsistencies, and some screens or behaviours may change between releases. Your ledger stays plain Beancount text files, so keep a backup of it. Bug reports and ideas are welcome in the issues; see the roadmap for what is planned.
Beanbook is an independent project. It is built on Beancount and Fava, and is not affiliated with or endorsed by them.
Your books remain a folder of ordinary Beancount text files. There is no account to create, no sign-in, and no server run by this project.
People who already keep, or want to keep, books in Beancount and want to work on them from a phone without converting to some app's private format. If you have never used plain-text accounting, the Beancount documentation is the place to start; this app assumes you know what a directive is.
It is not a general expense tracker, and it will not guess your categories for you.
Most mobile bookkeeping apps import your data into a database you cannot read. This one edits the files directly.
|
The real engines, not a copy |
Nothing of yours is thrown away |
|
A form when it helps, the file when it does not |
Getting your records out is free |
Home, Journal, the new-entry preview, Balances, Reports, Documents and More.
More screens: an entry, the entry form, Balances, Documents, Plugins, widgets
|
Guided entry for common directives, plus editing, deleting, and duplicating existing entries. The form shows the exact text it will write before you save. Every included file is directly editable as text. Nothing is reported as saved until it has been written and read back successfully, and a write that fails leaves the previous version of the file in place. The last change can be undone. |
The full Fava surface: balance sheet, income statement, trial balance, holdings, commodities, account and payee and tag views, charts, and BQL queries. Home, Balances and the widgets show figures in Units, At cost or Market value, the way Fava's own switch does. |
|
Capture receipts with the camera, crop them, assemble PDFs, and file them against entries. Documents live inside the portable project tree. |
Import a folder tree or an archive, export a copy to a folder you choose, write backup archives, and optionally mirror to a folder or schedule backups. |
|
A deliberately narrow client for a Fava server you host. It gives you reports and quick-add while the server is reachable. It does not keep an offline copy, edit existing remote entries, or attempt two-way sync. |
Open your Reports from another device on the same private network, in a browser. Off until you turn it on, behind a username and password you set, with a notification for as long as it runs. |
|
Beancount plugins and Fava extensions load with your ledger, and you can run your own Python scripts against it. A safe mode loads the ledger with no plugins. docs/plugin-compatibility.md says which were tested and how. |
Home-screen widgets for net worth and balances. Light, Dark and AMOLED themes, optional dynamic colour. |
Every change, from the entry form, the file editor or a delete, takes the same path. Success is reported only at the end, after Beancount has read the ledger back.
The one exception is the editor inside Fava's own pages in Reports: Fava saves those itself, with its own check that the file has not changed underneath it, and the app re-reads the ledger afterwards. Those saves get no snapshot or Undo.
flowchart TD
subgraph before["Before anything changes"]
direction LR
A["Lock the project"] --> B["Keep a copy<br/>to go back to"] --> C["Note the change<br/>about to be made"]
end
subgraph write["The write"]
direction LR
D["Write a temporary file<br/>and flush it to disk"] --> E["Swap it into place"]
end
before --> write
write --> F{"Beancount reads<br/>the ledger back"}
F -- "reads cleanly" --> G(["Saved."])
F -- "fails" --> H["Put the copy back"]
H --> I(["Not saved: the previous<br/>ledger stands"])
Play and Expert read the same projects, are both entirely free, and share every feature except three points that Google Play policy decides. They also update differently:
| Play | Expert | |
|---|---|---|
| Local Beancount and Fava | Included | Included |
| User plugins and extensions | Pure Python only | Pure Python and compatible native packages |
| Android bridge in user Python | Withheld | Available |
| Optional package downloads | None | User-confirmed |
| App updates | Google Play | Signed package, confirmed by you |
|
Choose Play if you want store-managed updates and your own plugins do not need native packages or Android APIs. |
Choose Expert if your plugins need compatible native packages or the Android bridge, you want packages fetched on demand, and you are comfortable installing a signed build directly. |
The Play restrictions are compliance with Google Play's rules about shipping executable code, not a security boundary: plugins you supply run as ordinary Python in both editions, with your ledger in reach. The Play edition tells you the Expert edition exists and what it adds. docs/editions.md explains which to pick.
No account, no sign-in, no telemetry, no analytics, no crash reporting, and no ads, in either edition. Local mode makes no network requests at all.
flowchart LR
subgraph phone["Your phone"]
app["Beanbook"] -- "127.0.0.1 only,<br/>a new token each run" --> fava["Fava, inside the app"]
files[("Your ledger files")] --- app
end
app -. "only if you enter its address" .-> server[("Your own Fava server")]
app -. "Expert only, when you ask" .-> pkg[("Python package index<br/>or GitHub")]
other["Another device<br/>on your Wi-Fi"] -. "while Share on Wi-Fi is on,<br/>after sign-in" .-> app
The app reaches out to the network only in cases you start:
- Your own Fava server, if you enter its address in remote mode.
- A plugin or an optional Python package, in the Expert edition only, when you ask for one. The Play edition never downloads code from anywhere.
Share on Wi-Fi is the one way in. It is off until you turn it on, listens only on the phone's
private network address, shows nothing until someone signs in with the username and password you
set, locks out repeated wrong passwords, and keeps a notification up for as long as it runs. The
connection is plain http://, so use it only on a network you trust.
If you type an https:// address, certificate errors are fatal and cannot be overridden in
any build. If you type an http:// address, that traffic is unencrypted and can be read or
modified on the network path, including any password you set. The app says so on screen before
connecting. Cleartext is never inferred and never used as a fallback after a failed HTTPS
connection; it happens only when you type http:// yourself.
docs/privacy.md has the full position, and docs/threat-model.md has the reasoning behind it.
- Android 10 (API 29) or later.
arm64-v8aorx86_64.
Create a ledger or import an existing project, then work on it. The short version:
- Open Projects and create a new ledger, or import a folder tree or archive.
- Import the whole tree rather than a single
.beancountfile, so includes and document links stay valid. - Fix any parse errors in the raw editor, which reports file, line, and column.
- Set up an export folder, a backup, or mirroring before you rely on the app.
docs/getting-started.md covers each step, where your files actually live, and what to do when something goes wrong.
Where your files live
Projects live in app-private storage, which is canonical. That is not an aesthetic choice: Python and Fava need real POSIX paths, and Android's folder picker hands out content URIs. Modern file managers cannot browse app-private storage, so the app provides explicit routes out: an in-app editor for every included file, export to a folder you pick, backup archives, and optional mirroring. Caches, logs and credentials are kept outside the portable project tree, so an exported copy contains your ledger and nothing else.
Beanbook is built for plain-text ledgers kept on your own device, and it is honest about where its edges are today:
- Version control is yours. Keep the exported or mirrored folder in Git, or any sync tool you like; the app works on the files and does not run Git itself.
- Remote mode is live. It shows your own Fava server as it is right now, so it needs a connection to that server.
- Receipts are kept as PDFs. Reading amounts off them automatically is not part of the app yet.
- Plugins. The Expert edition offers a catalogue of tested plugins; the Play edition loads plugin files you provide.
- English. The interface is in English.
- Large ledgers. It is designed and measured around ledgers of about 25,000 transactions; bigger ones work, with a longer first load after a change.
docs/known-limitations.md has the full list.
What is planned, as a checklist. Each line is an issue; ROADMAP.md has the reasoning and the status of every item.
- 1.0: local Beancount and Fava on the phone, entry forms, documents and scanning, backup, mirror, remote Fava, plugins and extensions, Share on Wi-Fi
- 1.0.1: one APK per processor type, Trash for deleted projects, and fixes from the repository review
- 1.0.2 Trust and polish: the scanner reworked, account details, encrypted scheduled backups, a document trash, and many fixes
- Review every file in the repository: 15 of 15 areas done (#16)
- A guided first run and an Accounts page, merged for 1.0.2 (#128, #130)
- Fixes from daily use: filter, widgets, amounts, documents, buttons, merged for 1.0.2 (#138, #141)
- Safety fixes from a second look: remote retry, backups, PDF pages, plugin list, import, templates, merged for 1.0.2 (#156, #157, #158)
- The document scanner reworked: auto-capture, review screen, styles, crop, detection, page size, merged for 1.0.2 (#179 to #184)
- A smaller OpenCV build, measured and deferred for its small gain (#185)
- The rest of the 1.0.2 bugs and tidy-ups (milestone)
- Fava's own editor stays Fava's: it saves as Fava does, and the limitation is documented (#17)
- 1.0.3 Trust and polish: the scanner's zoom slider, one Filters button for Home, Journal and Balances, the basis choice on the Journal and the widgets, a notice for an old WebView, and many fixes
- 1.0.4 Trust and polish, continued (now)
- 1.1.0 Capture: add an entry from shared text, photos and PDFs (#23); a Quick Settings tile and shortcut (#24); templates and recurring reminders (#25); rules that suggest the account for a shared message (#283); settling an entry with a linked refund or pay-back (#242)
- 1.2.0 Reconcile and import: a reconcile screen (#26); bank statements on the phone (#27); Syncthing conflict files (#28)
- 1.3.0 Reach: a send queue for a remote Fava (#29); price updates on request (#30); translations (#31); Git sync that never merges for you (#154)
Not planned: merging conflicting edits for you, cloud OCR or text AI, accounts, analytics and ads.
Every feature is free in both editions: no trial, no paid tiers, no keys, no ads. If Beanbook is useful to you, you can help it along:
The build needs a little more than a checkout because of the embedded Python runtime: a JDK and
the Android SDK, a host CPython 3.13 for Chaquopy's pip, and the cross-built beancount and
watchfiles wheels. CONTRIBUTING.md walks through it.
. spikes/env.sh # JDK, SDK and Gradle paths on the build machine
tools/build-wheels.sh --out ~/tools/wheels # pinned sdists, pinned build backends
./gradlew :core-domain:test :core-data:test :core-engine:test :app:testPlayDebugUnitTest
./gradlew :app:assemblePlayDebug :app:assembleExpertDebug
./gradlew :app:verifyPlayFlavorBoundaries # the Play edition's build gate
./gradlew detekt # gates CI; run it before pushingMore checks: release builds, lint, Python tests, the golden corpus, instrumentation
./gradlew :app:assemblePlayRelease :app:assembleExpertRelease
./gradlew :app:lintPlayDebug :app:lintExpertDebug
# Python layer: the interpreter must have beancount importable
~/tools/bc-desktop/bin/python3 -m unittest discover -s engine-local/src/test/python
# Golden corpus: generate fixtures, then record or verify against upstream Beancount
~/tools/bc-desktop/bin/python3 tools/corpus/generate.py --out corpus
~/tools/bc-desktop/bin/python3 tools/corpus/differential.py verify
# Instrumentation, on a device or emulator
./gradlew :app:connectedExpertDebugAndroidTestA change is done when it builds in both editions, debug and release; unit and instrumentation tests pass on API 29 and 36; the golden corpus round-trips byte-clean; and new screens pass the accessibility checks. CONTRIBUTING.md lists the commands and what each one needs.
| Document | Contents |
|---|---|
| docs/getting-started.md | First project, importing an existing ledger, moving data, troubleshooting |
| docs/editions.md | Play against Expert, and supporting the project |
| docs/privacy.md | What stays on the device and when the network is used |
| docs/known-limitations.md | What the app cannot do, and why |
| docs/on-demand-packages.md | Fetching extra Python packages, Expert only |
| docs/plugin-compatibility.md | Which plugins and extensions work, and how that was tested |
Bug reports and patches are welcome. CONTRIBUTING.md covers the build, which needs a little more than a checkout because of the embedded Python runtime, and the boundaries a change has to keep: the data invariants, the edition split, and the licence. Everyone taking part is expected to follow the Code of Conduct. Security reports go through SECURITY.md. What is planned is in ROADMAP.md.
GPL-2.0-only.
The full text is in COPYING, and the copyright holders are listed in AUTHORS.
Neither edition links any proprietary library for payments: nothing is sold. There is no additional licence permission: Beanbook is GPL-2.0-only, with nothing added.
Beancount and Fava are independent projects. This app is built on their work and is not affiliated with or endorsed by them.
The "Get it on F-Droid" badge is by F-Droid, licensed CC BY-SA 3.0. Only its empty margin was trimmed.











