https://github.com/colonelpanic8/keepbook
Open-source personal finance manager with git-backed, human-readable storage
https://github.com/colonelpanic8/keepbook
Last synced: 14 days ago
JSON representation
Open-source personal finance manager with git-backed, human-readable storage
- Host: GitHub
- URL: https://github.com/colonelpanic8/keepbook
- Owner: colonelpanic8
- Created: 2026-01-30T08:54:55.000Z (5 months ago)
- Default Branch: master
- Last Pushed: 2026-06-11T08:18:53.000Z (16 days ago)
- Last Synced: 2026-06-11T10:12:46.976Z (16 days ago)
- Language: Rust
- Size: 7.66 MB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.org
- Changelog: CHANGELOG.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
#+TITLE: keepbook
#+AUTHOR:
#+OPTIONS: toc:2
* Overview
Keepbook is a local-first personal finance toolkit focused on data ownership.
- Your data is stored in plain JSON/TOML/JSONL files.
- The on-disk format is human-readable and easy to inspect/edit with normal tools.
- The data directory is fully portable: copy it, back it up, version it, and keep it long-term without lock-in.
* App Downloads
Android APKs and desktop app bundles are attached to the latest GitHub release:
- https://github.com/colonelpanic8/keepbook/releases/latest
* CLI
The main Rust binary is =keepbook=. Commands emit JSON.
CLI usage is intentionally not duplicated in this README. The command tree,
flags, arguments, defaults, conflicts, and help text are defined in the Rust
=clap= parser and should be read from the binary:
#+BEGIN_SRC bash
keepbook --help
keepbook --help
keepbook --help
# From a checkout:
cargo run --bin keepbook -- --help
#+END_SRC
* Configuration
Default config path resolution:
1. =./keepbook.toml= if present.
2. Platform data dir fallback via =dirs::data_dir()=:
- Linux: =$XDG_DATA_HOME/keepbook/keepbook.toml=, or =~/.local/share/keepbook/keepbook.toml= when =XDG_DATA_HOME= is unset.
- macOS: =~/Library/Application Support/keepbook/keepbook.toml=.
When no config file exists, defaults are used and the intended config directory becomes =data_dir=.
Config fields:
#+BEGIN_SRC toml
# Optional; if relative, resolved against config directory
# data_dir = "./data"
reporting_currency = "USD"
[display]
# Optional; if set, values denominated in the reporting currency are rounded to
# this many decimal places in CLI output.
# currency_decimals = 2
#
# Optional UI-oriented display fields (canonical JSON numeric values are unchanged):
# currency_grouping = true
# currency_symbol = "$"
# currency_fixed_decimals = true
[refresh]
balance_staleness = "14d"
price_staleness = "24h"
[tray]
# Start the desktop UI hidden and open it from the tray icon.
start_minimized = false
# Number of most-recent portfolio history rows shown in tray menu.
history_points = 8
# Spending lookback windows (in days) shown as "Last Nd" rows.
spending_windows_days = [7, 30, 90]
[spending]
# Ignore matching account IDs or names in default portfolio spending reports.
# ignore_accounts = ["Individual", "acct-123"]
# Ignore matching connection IDs or names in default portfolio spending reports.
# ignore_connections = ["Schwab", "conn-123"]
# Ignore accounts containing any matching account tag in default portfolio spending reports.
# ignore_tags = ["brokerage"]
[portfolio.latent_capital_gains_tax]
# Disabled by default. When enabled, portfolio snapshot subtracts a dynamic
# virtual liability account from total net worth; no account is written to disk.
enabled = false
# Decimal fraction, e.g. 0.23 for 23%.
# rate = 0.23
account_name = "Latent Capital Gains Tax"
[git]
auto_commit = false
# When omitted, auto_push defaults to auto_commit.
# Set auto_push = false to auto-commit without pushing.
auto_push = false
merge_master_before_command = false
#+END_SRC
* Encrypted Credentials
Connection credentials can use =pass=, =env=, or =age= backends. The mobile
Dioxus path should prefer =age= so credentials can live encrypted in the
keepbook data repo without depending on =pass=.
The keepbook data repo should contain a public =keys.nix= file that lists the
SSH public keys allowed to decrypt age credential files. The source flake reads
that repo-local file by default; it does not reference any external checkout at
runtime. For this data repo, =keys.nix= owns the same recipient grouping used
for existing agenix secrets:
#+BEGIN_SRC nix
# keys.nix in the keepbook data repo
{
agenixKeys = hostKeys ++ kanivanKeys;
}
#+END_SRC
Print the exact SSH public key recipients used by =age -R=:
#+BEGIN_SRC bash
# Run from the keepbook data repo.
nix run /path/to/keepbook-source#keepbook-age-recipients
#+END_SRC
Use a different key attribute when needed:
#+BEGIN_SRC bash
nix run /path/to/keepbook-source#keepbook-age-recipients -- --attr hostKeys
KEEPBOOK_AGE_KEYS_ATTR=hostKeys nix run /path/to/keepbook-source#keepbook-age-recipients
#+END_SRC
Encrypt a pass-style credential payload:
#+BEGIN_SRC bash
cd /path/to/keepbook-data
pass show finance/coinbase-api \
| nix run /path/to/keepbook-source#keepbook-age-encrypt -- \
-o connections//credentials.age
#+END_SRC
Point the connection at the encrypted file:
#+BEGIN_SRC toml
[credentials]
backend = "age"
path = "credentials/coinbase.age"
# Optional. If omitted in the Dioxus/server path, keepbook uses the saved
# [git_sync].ssh_key_path from keepbook.toml as the age identity.
# identity_path = ".ssh/keepbook_sync_key"
#+END_SRC
The decrypted payload uses the same field format as =pass show= output. For
Coinbase, the relevant fields are =key-name= and =private-key=.
* Storage Layout
Primary storage root is the resolved =data_dir=.
#+BEGIN_SRC text
data/
connections/
by-name/ # symlinks to connection dirs
{connection-id}/
connection.toml # human config
connection.json # machine state
accounts/ # symlinks to account dirs
accounts/
{account-id}/
account.json
account_config.toml # optional
balances.jsonl # append-only BalanceSnapshot rows
transactions.jsonl # append-only Transaction rows
transaction_annotations.jsonl
# market data store (also under data_dir)
assets/
index.jsonl # AssetId -> Asset registry entries
prices/
{asset-id}/
{year}.jsonl
fx/
{BASE}-{QUOTE}/
{year}.jsonl
# configured network sources
price_sources/
{source-name}/
source.toml
#+END_SRC
Notes:
- Balances are stored as full snapshots (all holdings for an account at one timestamp).
- Transaction files are append-only; read path dedupes with last-write-wins by transaction id.
- Transaction annotations are append-only patches stored separately from raw transactions.
- Symlinks are rebuilt with =keepbook sync symlinks=.
- =account_config.toml= supports per-account overrides such as
=balance_staleness=, =balance_backfill=, and =exclude_from_portfolio=.
* Price Sources
Configured in =price_sources/*/source.toml=. Source type controls required credentials and supported asset classes.
Implemented source types:
- Equities: =eodhd=, =twelve_data=, =alpha_vantage=, =marketstack=
- Crypto: =coingecko=, =cryptocompare=, =coincap=
- FX: =frankfurter=
* Development
- Rust tests: =cargo test=
Useful helper commands from =justfile=:
- =just kb -- --help=
- =just run-tray -- --help=
* Architecture
Rust modules:
- =src/storage= - storage trait + JSON file implementation.
- =src/sync= - synchronizer traits, orchestration, auth flows.
- =src/market_data= - store, source adapters, routers, service builder.
- =src/portfolio= - valuation, history, and change-point logic.
- =src/app/= - application command handlers/types.
- =src/main.rs= - CLI entrypoint.
- =src/bin/keepbook-sync-daemon.rs= - optional tray sync daemon.
* Why "Keepbook"?
Keepbook is about keeping control of your own financial records.
The point is durable ownership: plain files, readable forever, portable anywhere.
* License
MIT OR Apache-2.0.