An open API service indexing awesome lists of open source software.

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

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.