A blazing-fast Rust implementation of Beancount
Parse and validate your ledger faster than Python beancount.
| 10-30x faster | Parse and validate large ledgers in milliseconds (see benchmarks) |
| No dependencies | No Python runtime, no libraries to install |
| Drop-in replacement | Compatible bean-* CLI commands for easy migration |
| Full compatibility | Parses any valid beancount file |
| Editor support | LSP server for VS Code, Neovim, Helix, and more |
| AI-ready | MCP server for Claude, Cursor, and other AI assistants |
| Runs anywhere | WebAssembly support for browser and Node.js |
| Better errors | Detailed error messages with source locations |
| 20 built-in plugins | Plus Python plugin compatibility via WASI sandbox |
Comparison with other tools
| Feature | rustledger | Python beancount | hledger | ledger-cli |
|---|---|---|---|---|
| Language | Rust | Python | Haskell | C++ |
| Speed | Very fast | Slow | Fast | Fast |
| Beancount syntax | Native | Native | Via conversion | No |
| Query language | BQL (100% compat) | BQL | Custom | Custom |
| LSP server | Built-in | No | Via plugin | No |
| WASM support | Yes | No | Partial | No |
| Plugin system | Native + Python | Python | Haskell | Custom |
| Active development | Yes | Maintenance | Yes | Limited |
When to use rustledger:
- You use Beancount syntax and want speed
- You want a single binary with no runtime dependencies
- You need LSP editor integration
- You want to use existing Python plugins
When to use Python beancount:
- You need Fava web interface (until rustledger integration)
- You have complex Python plugins with C extensions
When to use hledger:
- You prefer hledger's syntax and reports
- You need time-tracking features
| Platform | Command |
|---|---|
| macOS/Linux | brew install rustledger |
| Arch Linux | yay -S rustledger-bin or yay -S rustledger (from source) |
| Windows | scoop bucket add rustledger https://github.com/rustledger/scoop-rustledger && scoop install rustledger |
| Cargo | cargo binstall rustledger or cargo install rustledger |
| Fedora/RHEL | sudo dnf copr enable robcohen/rustledger && sudo dnf install rustledger |
| Nix | nix run github:rustledger/rustledger |
| Docker | docker run --rm -v "$PWD:/data" ghcr.io/rustledger/rustledger /data/ledger.beancount |
| Binaries | GitHub Releases |
| npm (WASM) | npm install @rustledger/wasm |
| npm (MCP) | npx @rustledger/mcp-server (Model Context Protocol server) |
Missing your platform? Open an issue to request it.
Coming from Python beancount? See the Migration Guide for command equivalents and plugin mapping.
rledger check ledger.beancount
rledger query ledger.beancount "SELECT account, SUM(position) GROUP BY account"| Command | Description |
|---|---|
rledger check |
Validate ledger files with detailed error messages |
rledger query |
Run BQL queries (interactive shell or one-shot) |
rledger format |
Auto-format beancount files |
rledger report |
Generate balance, account, and statistics reports |
rledger doctor |
Debugging tools for ledger issues |
rledger extract |
Import transactions from CSV/OFX bank statements |
rledger price |
Fetch commodity prices from online sources |
rledger-lsp |
Language Server Protocol for editor integration |
Python beancount users can also use bean-check, bean-query, etc.
Report subcommands
| Subcommand | Alias | Description |
|---|---|---|
balances |
All account balances | |
balsheet |
bal |
Balance sheet report |
income |
is |
Income statement |
journal |
register |
Transaction register |
holdings |
Investment holdings | |
networth |
Net worth over time | |
accounts |
List all accounts | |
commodities |
List all commodities | |
prices |
Price entries | |
stats |
Ledger statistics |
Doctor subcommands
Debugging and diagnostic tools:
| Subcommand | Description |
|---|---|
lex |
Dump lexer tokens (alias: dump-lexer) |
parse |
Parse and show directives |
context |
Show transaction context at a line number |
linked |
Find transactions by link (^link) or tag (#tag) |
missing-open |
Generate missing Open directives |
list-options |
List all available beancount options |
print-options |
Print options parsed from a file |
stats |
Display ledger statistics |
display-context |
Show inferred decimal precision context |
roundtrip |
Round-trip parse/format test |
directories |
Validate directory hierarchy against accounts |
region |
Print transactions in a line range with balances |
generate-synthetic |
Generate synthetic test files |
# Debug a parsing issue at line 42
rledger doctor context ledger.beancount 42
# Find all transactions with a link
rledger doctor linked ledger.beancount ^trip-2024
# Generate Open directives for accounts missing them
rledger doctor missing-open ledger.beancount >> ledger.beancountRun rledger <command> --help for all options.
CLI examples
# Validate with plugins
rledger check --native-plugin auto_accounts ledger.beancount
# Interactive query shell
rledger query ledger.beancount
# One-shot query
rledger query ledger.beancount "SELECT date, narration WHERE account ~ 'Expenses:Food'"
# Reports
rledger report ledger.beancount balances
rledger report ledger.beancount stats
# Format in place
rledger format --in-place ledger.beancount| Crate | Description |
|---|---|
rustledger |
CLI tool (rledger check, rledger query, etc.) |
rustledger-core |
Core types: Amount, Position, Inventory |
rustledger-parser |
Lexer and parser with error recovery |
rustledger-loader |
File loading and includes |
rustledger-booking |
Interpolation and 7 booking methods |
rustledger-validate |
27 validation error codes |
rustledger-query |
BQL query engine |
rustledger-plugin |
20 built-in plugins + Python plugin support |
rustledger-importer |
CSV/OFX import framework |
rustledger-lsp |
Language Server Protocol for editor integration |
rustledger-wasm |
WebAssembly bindings for JavaScript/TypeScript |
Booking methods (7)
| Method | Description |
|---|---|
STRICT |
Lots must match exactly (default) |
STRICT_WITH_SIZE |
Exact-size matches accept oldest lot |
FIFO |
First in, first out |
LIFO |
Last in, first out |
HIFO |
Highest cost first |
AVERAGE |
Average cost basis |
NONE |
No cost tracking |
Built-in plugins (20)
| Plugin | Description |
|---|---|
auto_accounts |
Auto-generate Open directives |
auto_tag |
Automatically tag transactions |
check_average_cost |
Validate average cost bookings |
check_closing |
Zero balance assertion on account close |
check_commodity |
Validate commodity declarations |
check_drained |
Ensure accounts are drained before close |
close_tree |
Close descendant accounts |
coherent_cost |
Enforce cost OR price (not both) |
commodity_attr |
Validate commodity attributes |
currency_accounts |
Enforce currency constraints on accounts |
document_discovery |
Auto-discover document files |
implicit_prices |
Generate price entries from transaction costs |
leafonly |
Error on postings to non-leaf accounts |
noduplicates |
Hash-based duplicate transaction detection |
nounused |
Warn on unused accounts |
onecommodity |
Single commodity per account |
pedantic |
Enable all strict validations |
sellgains |
Cross-check capital gains against sales |
unique_prices |
One price per day per commodity pair |
unrealized |
Calculate unrealized gains |
Python plugins: Run existing Python beancount plugins via CPython-WASI sandbox.
Plugin support
rustledger supports three types of plugins:
Native plugins (built-in, fastest):
# Run a native plugin from CLI
rledger check --native-plugin implicit_prices ledger.beancount
# Or declare in your beancount file (auto-detected as native):
# plugin "beancount.plugins.auto_accounts"Python file plugins (via WASM sandbox):
# Declare in your beancount file:
# plugin "/path/to/my_plugin.py"WASM plugins (sandboxed WebAssembly):
# Load a WASM plugin
rledger check --plugin /path/to/plugin.wasm ledger.beancountHow Python plugins work:
- File-based plugins (
.pyfiles) run in a sandboxed CPython compiled to WebAssembly - No system Python installation required
- Plugins cannot access the filesystem or network (sandbox isolation)
- Compatible with most pure-Python beancount plugins
Limitations:
- Module-based plugins (
beancount.plugins.xyz) only work if rustledger has a native implementation - Plugins with C extensions won't work (numpy, pandas, etc.)
- No network access (price fetching plugins need alternatives)
rustledger includes a full-featured Language Server (rledger-lsp) for IDE support:
- Real-time diagnostics
- Autocompletion (accounts, currencies, payees)
- Go to definition / find references
- Hover information with account balances
- Rename refactoring
- Document formatting
See LSP setup guide for VS Code, Neovim, Helix, Zed, and Emacs.
Benchmarks run nightly on 10K transaction ledgers. View workflow →
Benchmark details
What's measured:
- Validation: Parse ledger + validate (balance assertions, account opens, etc.)
- Balance Report: Parse + compute all account balances
Memory efficiency: rustledger typically uses 3-5x less memory than Python beancount thanks to Rust's zero-cost abstractions and efficient data structures.
Run locally:
# Quick comparison (requires nix)
nix develop .#bench
./scripts/bench.sh
# Criterion micro-benchmarks
cargo bench -p rustledger-core
cargo bench -p rustledger-parserSee BENCHMARKING.md for detailed benchmark documentation.
See CONTRIBUTING.md for development setup and guidelines.
Documentation:
- Architecture - Crate structure and data flow
- BQL Reference - Query language guide
- Importing - CSV/OFX bank import tutorial
- Validation errors - Error code reference
- API docs - Rust API documentation
By submitting a pull request, you agree to the Contributor License Agreement.
Commercial licensing available - contact us for proprietary license options.
rustledger is free and open source. If you find it useful, consider supporting development: