rustledger

June 29, 2026 · View on GitHub

rustledger

A blazing-fast Rust implementation of Beancount

Parse and validate your ledger faster than Python beancount.

crates.io npm Packaging status docs.rs

CI Compatibility

License: GPL v3 Liberapay


Why rustledger?

10-30x fasterParse and validate large ledgers in milliseconds (see benchmarks)
No dependenciesNo Python runtime, no libraries to install
Drop-in replacementCompatible bean-* CLI commands for easy migration
Full compatibilityParses any valid beancount file
Editor supportLSP server for VS Code, Neovim, Helix, and more
AI-readyMCP server for AI assistants, plus ag-rledger — an agent-native CLI with JSON envelopes
Runs anywhereWebAssembly support for browser and Node.js
Better errorsDetailed error messages with source locations
31 built-in pluginsPlus Python plugin compatibility via WASI sandbox
Bank importCSV/OFX import with auto-detection, dedup, and categorization
Comparison with other tools
FeaturerustledgerPython beancounthledgerledger-cli
LanguageRustPythonHaskellC++
SpeedVery fastSlowFastFast
Beancount syntaxNativeNativeVia conversionNo
Query languageBQL (100% compat)BQLCustomCustom
LSP serverBuilt-inNoVia pluginNo
WASM supportYesNoPartialNo
Plugin systemNative + PythonPythonHaskellCustom
Active developmentYesMaintenanceYesLimited

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

Install

PlatformCommand
macOSbrew install rustledger
Linuxbrew install rustledger
Arch Linuxyay -S rustledger-bin
Fedora/RHELsudo dnf copr enable robcohen/rustledger && sudo dnf install rustledger
Windowsscoop bucket add rustledger https://github.com/rustledger/scoop-rustledger && scoop install rustledger
Cargocargo binstall rustledger or cargo install rustledger
Nixnix run github:rustledger/rustledger
Dockerdocker run --rm -v "$PWD:/data" ghcr.io/rustledger/rustledger /data/ledger.beancount
BinariesGitHub 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.

AI & agents

Two complementary ways to drive rustledger from AI tools:

  • MCP servernpx @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):

    $ ag-rledger check --json ledger.beancount
    {"ok":true,"exit_code":0,"result":{"data":{"error_count":0}},"next_action":"…"}
    

    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

CommandDescription
rledger checkValidate ledger files with detailed error messages
rledger queryRun BQL queries (interactive shell or one-shot)
rledger formatAuto-format beancount files
rledger reportGenerate balance, account, and statistics reports
rledger addAdd transactions interactively or via quick mode
rledger doctorDebugging tools for ledger issues
rledger extractImport transactions from CSV/OFX bank statements with auto-detection, dedup, and categorization
rledger priceFetch commodity prices from online sources
rledger lintNon-fatal advisory passes (e.g., detect inter-account transfer pairs)
rledger configManage rustledger configuration (alias: cfg)
rledger completionsGenerate shell completions
rledger-lspLanguage Server Protocol for editor integration

Python beancount users can install bean-check, bean-query, etc. wrapper scripts via rledger compat install.

Report subcommands
SubcommandAliasDescription
balancesAll account balances
balsheetbalBalance sheet report
incomeisIncome statement
journalregisterTransaction register
holdingsInvestment holdings
networthNet worth over time
accountsList all accounts
commoditiesList all commodities
pricesPrice entries
statsLedger statistics
Doctor subcommands

Debugging and diagnostic tools:

SubcommandDescription
lexDump lexer tokens (alias: dump-lexer)
parseParse and show directives
contextShow transaction context at a line number
linkedFind transactions by link (^link) or tag (#tag)
missing-openGenerate missing Open directives
list-optionsList all available beancount options
print-optionsPrint options parsed from a file
statsDisplay ledger statistics
display-contextShow inferred decimal precision context
roundtripRound-trip parse/format test
directoriesValidate directory hierarchy against accounts
regionPrint transactions in a line range with balances
generate-syntheticGenerate 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:

[[importers]]
name = "chase"
account = "Assets:Bank:Chase"
date_column = "Transaction Date"
amount_column = "Amount"
date_format = "%m/%d/%Y"

[importers.mappings]
"AMAZON" = "Expenses:Shopping"
"WHOLE FOODS" = "Expenses:Groceries"
rledger extract --importer chase chase-statement.csv

Config is searched in: current directory, ~/.config/rledger/importers.toml, or via --config.

Transaction Categorization

A 3-tier pipeline automatically categorizes transactions:

  1. Rules engine — substring, regex, and exact match patterns from importers.toml
  2. Merchant dictionary — ~75 built-in patterns across 10 categories (groceries, dining, transport, subscriptions, etc.)
  3. 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

Fuzzy matching on date + amount + payee/narration prevents importing the same transaction twice. 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.

Crates

CrateDescription
rustledgerCLI tool (rledger check, rledger query, etc.)
rustledger-coreCore types: Amount, Position, Inventory
rustledger-parserLexer and parser with error recovery
rustledger-loaderFile loading and includes
rustledger-bookingInterpolation and 7 booking methods
rustledger-validateValidation error codes
rustledger-queryBQL query engine
rustledger-completionEditor-agnostic completion logic (shared by LSP + WASM)
rustledger-plugin31 built-in plugins + Python plugin support
rustledger-plugin-typesShared plugin type definitions
rustledger-importerImport framework: built-in CSV/OFX, plus a WasmImporter host loader for third-party .wasm importers
rustledger-opsPure operations — ML categorization, LLM prompts, dedup, transfer detection, balance reconciliation, merchant dictionary
rustledger-lspLanguage Server Protocol for editor integration
rustledger-wasmWebAssembly bindings for JavaScript/TypeScript
rustledger-ffi-componentPrimary embedding surface — FFI via WASI Preview 2 / Component Model (typed WIT contract)
rustledger-ffi-wasiLegacy WASI (wasip1) JSON-RPC FFI for embedding — slated for removal
Booking methods (7)
MethodDescription
STRICTLots must match exactly (default)
STRICT_WITH_SIZEExact-size matches accept oldest lot
FIFOFirst in, first out
LIFOLast in, first out
HIFOHighest cost first
AVERAGEAverage cost basis
NONENo cost tracking
Built-in plugins (31)
PluginDescription
auto_accountsAuto-generate Open directives
auto_tagAutomatically tag transactions
box_accrualAccrual accounting for boxed periods
gain_lossSplit capital gains into gain/loss accounts
long_shortSplit capital gains by holding period
check_average_costValidate average cost bookings
check_closingZero balance assertion on account close
check_commodityValidate commodity declarations
check_drainedEnsure accounts are drained before close
close_treeClose descendant accounts
coherent_costEnforce cost OR price (not both)
commodity_attrValidate commodity attributes
currency_accountsAuto-generate currency trading postings
effective_dateOverride posting date via metadata
forecastGenerate recurring transactions
generate_base_ccy_pricesCreate base currency price entries
implicit_pricesGenerate price entries from transaction costs
leafonlyError on postings to non-leaf accounts
noduplicatesHash-based duplicate transaction detection
nounusedWarn on unused accounts
onecommoditySingle commodity per account
pedanticEnable all strict validations
rename_accountsRename accounts via metadata
rx_txn_pluginLink related transactions
sellgainsCross-check capital gains against sales
split_expensesSplit expenses across accounts
unique_pricesOne price per day per commodity pair
unrealizedCalculate unrealized gains
valuationMark-to-market valuation
zerosumGroup transactions that sum to zero

Additionally, document_discovery auto-discovers documents from option "documents" directories.

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.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

Validation Benchmark Balance Report Benchmark

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-parser

See benchmarking.md for detailed benchmark documentation.

Contributing

See CONTRIBUTING.md for development setup and guidelines.

Documentation:

By submitting a pull request, you agree to the Contributor License Agreement.

License

GPL-3.0-only

Commercial licensing available - contact us for proprietary license options.

Funding

rustledger is free and open source. If you find it useful, consider supporting development:

Support on Liberapay