-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
daybook is a pnpm-workspace monorepo with five packages:
daybook/
├── packages/
│ ├── ledger/ — Core data model, types, SQLite storage
│ ├── sources/ — Source adapters (Coinbase, Kraken, Binance, generic CSV, EVM)
│ ├── classifier/ — Event classification (8-rule chain)
│ ├── tax/ — Cost basis, gain/loss, wash sale, pricing, CSV export
│ └── cli/ — CLI commands (commander + Ink)
├── docs/ — Design docs and research
├── decisions.md — Locked-in product decisions
└── package.json — Root workspace config
Packages depend in one direction. No cycles.
cli → tax → classifier → ledger
sources → ledger
- ledger is the foundation — types, DB, repo. No internal dependencies.
-
sources depends only on ledger (for
RawEventtypes). -
classifier depends on ledger (consumes
RawEvent, producesLedgerEntry). -
tax depends on ledger (consumes
LedgerEntry). Pricing module lives here. - cli depends on all four packages.
daybook uses a two-layer event model that separates source data from classified output:
Raw events are produced by source adapters and stored permanently. They represent exactly what the source reported, normalized into a common shape. Re-syncing the same data is a no-op (idempotent via INSERT OR IGNORE).
Each RawEvent has:
- A deterministic ID (
{source}:{nativeId}) - One or more
AssetLegentries (signed amounts — positive = received, negative = spent) - Optional metadata: txHash, counterparty, notes
Ledger entries are produced by the classifier from raw events. They represent the intent of a transaction (trade, transfer, income, etc.). The classifier can be re-run at any time — ledger entries are fully rebuilt from raw events + overrides.
Source Data → Source Adapter → RawEvent[] → Repository (append-only)
↓
Classifier (8 rules + overrides)
↓
LedgerEntry[] → Repository (rebuild)
↓
Pricing (source → CoinGecko → manual)
↓
Tax Engine (FIFO/HIFO/Specific ID)
↓
Wash Sale Pass
↓
CSV Export
- User runs
daybook sync— source adapter producesRawEvent[], persisted idempotently - User runs
daybook classify— classifier runs 8-rule chain, producesLedgerEntry[] - User runs
daybook export— pricing resolves USD values, tax engine computes cost basis, CSV written
All data lives in a single SQLite database at ~/.daybook/data.db (WAL mode).
Key tables:
-
raw_events+raw_event_legs— append-only source data -
ledger_entries— classifier output (rebuilt on each classify run) -
classifier_overrides— user corrections that survive re-classification -
prices— cached USD prices from the pricing chain -
price_overrides— manual price entries
- Language: TypeScript 5.5+ (strict mode, ESM-only)
- Runtime: Node.js >= 20
- Build: tsup (ESM + DTS per package)
-
Test: Vitest 2 (colocated
*.test.tsfiles) - Storage: better-sqlite3
- Math: decimal.js (never floating-point for amounts)
- CLI: commander + Ink (React for terminals)
- EVM data: alchemy-sdk + viem
- Validation: zod
See Source Adapters, Classifier Rules, Tax Engine, and Pricing for detailed documentation of each package.
Getting Started
Usage
Architecture