Parse and validate your ledger faster than Python beancount.
Why rustledger?
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 AI assistants, plus ag-rledger — an agent-native CLI with JSON envelopes
Runs anywhere
WebAssembly support for browser and Node.js
Better errors
Detailed error messages with source locations
31 built-in plugins
Plus Python plugin compatibility via WASI sandbox
Bank import
CSV/OFX import with auto-detection, dedup, and categorization
Investment returns
Money-weighted (XIRR) and time-weighted returns, robust over imperfect ledgers: a built-in beangrow replacement
Budgeting
Budgeted vs actual spending from Fava-compatible custom "budget" directives, calendar-accurate pro-rating
Capital gains
Realized gains per tax lot, short vs long term, with exact @@ proceeds, calendar-correct holding periods, and optional per-lot annualized return (--irr)
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)
Coming from Python beancount? See the Migration Guide for command equivalents and plugin mapping.
AI & agents
Two complementary ways to drive rustledger from AI tools:
MCP server — npx @rustledger/mcp-server exposes rustledger to AI assistants (Claude, Cursor, …) over the Model Context Protocol. Best for interactive assistants that call tools mid-conversation.
ag-rledger — an agent-native CLI that mirrors every rledger command but emits a single structured JSON envelope per call instead of human-readable text, so coding agents and scripts parse results directly (no output scraping). Each envelope carries ok, a typed exit_code, the command's output under result, and a HATEOAS next_action hint; pass the inner command's --json to get fully structured result.data. Best for headless agents and shell pipelines. It ships beside rledger in the release archives (or build it with cargo build -p rustledger --bin ag-rledger --no-default-features --features ag-rledger):
Same subcommands as rledger (check, query, format, report, extract, …) — only the I/O is JSON.
Quick Start
rledger check ledger.beancount
rledger query ledger.beancount "SELECT account, SUM(position) GROUP BY account"
CLI Commands
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 add
Add transactions interactively or via quick mode
rledger doctor
Debugging tools for ledger issues
rledger extract
Import transactions from CSV/OFX bank statements with auto-detection, dedup, and categorization
rledger price
Fetch commodity prices from online sources
rledger lint
Non-fatal advisory passes (e.g., detect inter-account transfer pairs)
rledger config
Manage rustledger configuration (alias: cfg)
rledger completions
Generate shell completions
rledger-lsp
Language Server Protocol for editor integration
Python beancount users can install bean-check, bean-query, etc. wrapper scripts via rledger compat install.
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
returns
Investment returns: money-weighted (XIRR) + time-weighted, with --by-group breakdown (a built-in beangrow replacement)
capgains
Realized capital gains/losses per tax lot (short vs long term)
budget
Budgeted vs actual spending (Fava-compatible custom "budget")
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.beancount
Run 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
Import & Categorization
rustledger includes a complete bank import pipeline (rledger extract) with automatic CSV/OFX parsing, duplicate detection, transaction categorization, and balance reconciliation.
CSV Import with Auto-Detection
# Auto-detect delimiter, date format, and column roles
rledger extract bank-statement.csv --auto -a Assets:Bank:Checking
# Check against existing ledger to avoid duplicates
rledger extract statement.csv --auto -a Assets:Bank --existing ledger.beancount
# Append balance assertion from bank statement
rledger extract statement.csv --auto -a Assets:Bank --balance 5000.00
# List available importers from config
rledger extract --list-importers --config importers.toml
Importers Configuration
Create importers.toml for reusable import profiles:
Config is searched in: current directory, the user config directory ($RLEDGER_CONFIG_DIR if set, else ~/.config/rledger/importers.toml), or via --config.
Transaction Categorization
A 3-tier pipeline automatically categorizes transactions:
Rules engine — substring, regex, and exact match patterns from importers.toml
ML categorization — TF-IDF + a self-contained Multinomial Naive Bayes classifier (pure std, no ML framework dependency)
Transfer Detection
Automatically identifies inter-account transfers by matching opposite-sign amounts within date windows, with keyword boosting for common transfer indicators.
Duplicate Detection
Bank transaction ids (^ofx-…, ^csv-… links) and matching on date + the account's amount + payee/narration prevent importing the same transaction twice; identical transactions you really made twice are kept. Structural fingerprinting (BLAKE3) provides stable hashes for comparison.
Balance Reconciliation
The --balance flag appends a balance assertion directive matching your bank statement's ending balance, helping verify import accuracy.
Custom WASM Importers
Beyond the built-in CSV and OFX importers, third parties can ship importers as sandboxed .wasm modules — no need to fork the workspace:
# Single file
rledger extract --wasm-importer ./my-bank.wasm statement.dat -a Assets:Bank
# Or scan a directory of .wasm modules (also configurable via importers.toml)
rledger extract --wasm-importer-dir /etc/rledger/importers.d statement.dat -a Assets:Bank
WASM importers implement the same Importer trait as the built-ins via the wasm_importer_main! macro and run inside the same wasmtime sandbox as directive plugins (no FS / network / WASI; configurable memory + fuel caps). See examples/wasm-importer-csv-example for a reference implementation.
Pure operations — ML categorization, LLM prompts, dedup, transfer detection, balance reconciliation, merchant dictionary
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 (31)
Plugin
Description
auto_accounts
Auto-generate Open directives
auto_tag
Automatically tag transactions
box_accrual
Accrual accounting for boxed periods
gain_loss
Split capital gains into gain/loss accounts
long_short
Split capital gains by holding period
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
Auto-generate currency trading postings
effective_date
Override posting date via metadata
forecast
Generate recurring transactions
generate_base_ccy_prices
Create base currency price entries
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
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.beancount
How Python plugins work:
File-based plugins (.py files) 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)
Editor Integration
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.
Performance
Benchmarks run nightly on 10K transaction ledgers. View workflow →
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.