Finances Hub (finances.justinsforge.com)¶
STATUS (2026-07-09): DISMANTLED. (Retired 2026-07-05; code + runtime torn down 2026-07-09.) Removed: stray orphaned
forge_finances_app.pyproc squatting :8096 for 3d (killed), the 36 Flask web + Python data-layer scripts (forge_finances_app.py, allforge_finances_ui_*,forge_finance_{sync,categorize,recurring,holdings,alerts,networth,snapshot,db,accounts,ingest,history_grid,migrate_to_lifeos,set_category,sheet_mirror,receipt_reconcile,recurring_action}.py, tests/js_check), and the 4 retiredforge-finance-*systemd units. KEPT (not part of the Flask app):forge_finance_query.py(live Telegram-brain tool; REWIRED 2026-07-09 from the frozenfinance.dbto live lifeos Postgresfinance.*scoped to ws67 viaforge_workspace_db.admin_connect(); verified net worth $215,743 / spend / recurring all live),forge_simplefin_client.py(canonical SimpleFIN claim CLI),forge_google_sa.py(SA token helper). CreatorTrack only ever referenced the deleted scripts in "ported from" comments, never at runtime. Live timers are the separateforge-creatortrack-finance-*units. Deletions uncommitted (Justin reviews). The full native rebuild shipped: ingest, writes, icons, alerts, snapshots and books integration all live in CreatorTrack against thefinance.*Postgres schema, which is now the SOURCE OF TRUTH. See reference_creatortrack_finance_module.forge-finances.serviceis disabled, :8096 closed, legacy timers off;finances.justinsforge.com/receipts.justinsforge.comredirect toapp.creatortrack.ai/finances. Archives (pre-cutover):/home/justinwieb/forge-backups/finances-cutover-20260705/(finance.db + pg_dump).finance.dbon disk is FROZEN history; do not write to it. Everything below is retained as historical reference for the Python-era implementation.2026-07-05 mirror outage (fixed): the workspace re-slug (
justins-workspace->justin) brokeforge_finance_migrate_to_lifeos.py(exit 1 on every sync since 07-03), freezing thefinance.*Postgres mirror while SQLite kept syncing. Fixed by updatingWORKSPACE_SLUGdefault tojustin(ws id 67); mirror truncate-reloads from SQLite each sync, safe because ALL writes still land in SQLite via the bridge (verified receipts empty both sides first).
Household + business finance dashboard. Net worth, cash flow, budget, spending, recurring, accounts, transactions, alerts. Rocket Money / Mint replacement. Built 2026-06-08; refactored from a 6k-line monolith into flat blueprint modules + modern UI refresh 2026-06-10 (plan: finances-hub-refactor-redesign-2026-06-10).
Web-layer module map (post 2026-06-10 split)¶
forge_finances_app.py is a ~110-line entry: registers blueprints, owns
/sync, /health, favicons. Pages live in flat forge_finances_ui_* modules:
| Module | Owns |
|---|---|
forge_finances_ui_base.py |
BASE template (CSS/JS), NAV_ITEMS, ICONS, money/escape helpers, pills, donut + bar + spend charts, shell() |
forge_finances_ui_icons.py |
account/merchant logo + icon subsystem and its routes |
forge_finances_ui_data.py |
shared read accessors (spend_overview, account_summary, recent_transactions, sync widgets); all txn SQL filters COALESCE(superseded,0)=0 |
forge_finances_ui_charts.py |
_line_chart, _multi_line_chart, SEG_COLORS (shared by accounts + networth) |
forge_finances_ui_dashboard.py |
/ |
forge_finances_ui_networth.py |
/networth (+ sparklines, change badges, by-class grouping) |
forge_finances_ui_cashflow.py |
/cashflow (net-flow hero, 30-day balance forecast from confirmed recurring, safe-to-spend, upcoming bills) |
forge_finances_ui_investments.py |
/investments (holdings hero + allocation bars + per-account positions; empty state has export instructions) |
forge_finances_ui_spending.py |
/spending |
forge_finances_ui_budget.py |
/budget (buckets, targets, autoset, left-to-spend + daily pace, last-month compare) |
forge_finances_ui_recurring.py |
/recurring + ~20 sub-routes |
forge_finances_ui_accounts.py |
/accounts, /account/ |
forge_finances_ui_transactions.py |
/transactions (filter bar incl. min/max $, bulk edit via POST /transactions/bulk), /transaction/ |
forge_finances_ui_categorize.py |
/categorize + rules engine routes |
forge_finances_ui_business.py |
/business (ERPNext P&L + balance sheets per company, read-only, deep links to edit; WEOP-Operating displays as Wiebelhaus, Gus never consolidated) |
forge_finances_ui_alerts.py |
/alerts |
Verification gates: python3 scripts/forge_finances_route_sweep.py (every GET
route 200) and python3 scripts/forge_finances_tests.py (money-math fixtures:
forecast, alert checks, net-worth classing). Run both after any change +
service restart.
Proactive alerts (2026-06-10)¶
forge_finance_alerts.py runs as ExecStartPost on every sync: bills due
within 24h, large transactions (default $500+, posted_at-guarded so bulk
imports can't flood), flex-budget 80/100% crossings, recurring price
increases (>=15% and >=$2), low checking balances. Once-per-key dedup in
data/finance/alerts_state.json (90d prune), feed for the /alerts tab in
alerts_feed.json, thresholds overridable in alerts_config.json. Pushes
one digest per run via forge_notify.sh info.
Investments / Fidelity holdings (2026-06-10)¶
No Fidelity API for individuals, so: Justin exports the Positions CSV
(fidelity.com -> Accounts -> Positions -> download) and drops it in
/mnt/workspace/Forge/Fidelity Positions/ (Drive-synced). forge_finance_holdings.py
--auto (ExecStartPost on every sync) imports new drops (snapshot-replace per
account label, archives to processed/), then marks every symbol to market via
Yahoo's keyless chart endpoint (SPAXX/money market pinned at $1). DB table:
holdings (UNIQUE account_label+symbol). Re-export is only needed after
trades; prices refresh daily on their own. Account labels match to SimpleFIN
accounts by last4 then name. Parser is regression-tested in
forge_finances_tests.py.
Serving + DB hardening (2026-06-10)¶
Waitress (not Flask dev server) bound to 127.0.0.1:8096; the CF tunnel
origin is local so nothing on LAN/tailnet reaches it unauthenticated.
forge_finance_db.connect() sets WAL + busy_timeout 5000 (web reads overlap
timer writes). Sync logs N balances written (missing/errors) and pages a
warning when a run writes zero balances (net worth silently froze 06-09/06-10
before this).
One-shot split tool (historical): forge_finances_refactor_mover.py.
Full pre-refactor backup: /home/justinwieb/forge-backups/finances-20260609-231319/.
Where the data lives (the answer to "how is this stored/accessed/modified")¶
| Layer | What | Location |
|---|---|---|
| Source of truth | SQLite DB, all accounts/transactions/balances/manual values/recurring/snapshots | forge/data/finance/finance.db on Console (gitignored) |
| Secrets | SimpleFIN access URL; Google service-account key | ~/.forge-secrets/simplefin.env, ~/.forge-secrets/google-sa.json (chmod 600) |
| Bank feed | SimpleFIN Bridge (read-only), ~90-day window per pull; we retain everything pulled forever | forge_simplefin_client.py |
| Human mirror | Google Sheet "Wieb Finances" (new, Forge-owned), id 1u7zTvLJI2pom-h_e55ueSLO8K5plCUA46NqaA2rYLBQ |
shared to [email protected] |
| Web surface | Flask app on Console:8096, finances.justinsforge.com via VR Alliance CF tunnel + CF Access ([email protected]) |
forge_finances_app.py, forge-finances.service |
DB tables: accounts, transactions, balances, manual_values,
weekly_snapshots, recurring. Schema + migrations in forge_finance_db.py
(app runs d.init() on startup to auto-apply migrations).
How it's modified¶
- Daily 07:00 CT
forge-finance-sync.timer: SimpleFIN sync -> categorize -> recurring scan (fail-loud notify). - Weekly Sun 18:00 CT
forge-finance-snapshot.timer: writes signed net-worth contributions per account intoweekly_snapshots. - In the web UI: per-transaction edit (category/scope/note/tax-deductible/ignore/flag); per-recurring edit (nickname/description/category/Personal-Business). User edits are sticky (
user_category,user_editedflags) and survive re-sync/re-scan. - CLIs:
forge_finance_accounts.py(manual accounts + labeling),forge_finance_categorize.py --apply,forge_finance_recurring.py --scan,forge_finance_import_history.py(read-only sheet history import).
Scripts¶
forge_simplefin_client.py- SimpleFIN client (UA header required; CDN 403s python-urllib)forge_google_sa.py- mints Google API tokens from the SA key (PyJWT, no google-auth)forge_finance_db.py- schema + helpers + migrationsforge_finance_sync.py- SimpleFIN -> DB (idempotent on simplefin_txn_id)forge_finance_categorize.py- merchant/keyword -> transactions.category (16 cats; preserves user_category)forge_finance_recurring.py- cadence detection + amount_cv (fixed vs varies) + categoriesforge_finance_networth.py- net worth rollup (live balances + manual values, signed)forge_finance_snapshot.py- weekly snapshotsforge_finance_import_history.py- parse Wieb Spending Plan export -> 2026 history (READ-ONLY source)forge_finances_app.py- the web app
Pages¶
Dashboard (live accounts + spend chart + recent txns + upcoming recurring), Net Worth (total + per-entity + history chart + 1M-ALL ranges + per-account sparklines/change badges + Assets/Debt by-entity or by-class views), Spending (donut + category table + month nav + Personal/Business + txn list, income-vs- spend bars), Budget (fixed/flex/non-monthly buckets, targets with autosave, left-to-spend + daily pace, last-month compare), Accounts (grouped, clickable -> per-account page), Transactions (filter bar: search/date/category/scope/account/direction/min-max $; Select mode bulk edit for category/scope/ignore), Recurring (Fixed-monthly hero + calendar + confirm/ignore + per-item edit), Categorize (triage + rules engine), Alerts (flagged transactions). UI: dark Material look, card depth, page fade-in, fixed glass bottom tab bar on mobile.
Data model notes¶
- Net worth = live SimpleFIN balances (signed as reported, cards negative) + latest manual values (+asset/-liability). Only
active=1 AND include_in_nw=1. - Dashboard Accounts card = SimpleFIN accounts only; manual/static assets show on Net Worth.
- Manual assets seeded from the spreadsheet import: home $430k (garage conversion folded in), cars, HSA, Krystal 401k=Fidelity Rollover (dedup'd), loans, parents loan, tax bill.
- SimpleFIN returns NO holdings, so individual Fidelity positions aren't available.
Open / roadmap¶
- Multi-year history: workbook has Net Worth 2023/2024/2025/2026 tabs; rclone
gdrive:exports the full xlsx (rclone --drive-export-formats xlsx backend copyid gdrive: <id> <out>). Import 2023-2025 next. - Google Sheet mirror write-back (Task 10) once SA has Editor on the new sheet.
- ERPNext: bulk-ingest 12-24 months of historical bank transactions into
transactions(idempotent) as ERPNext data becomes available. - Net Worth Assets/Debt class groupings + 1M/3M/6M/1Y/ALL range toggle.
- Transactions top-filter bar (dates/categories/accounts/amounts) like Rocket Money.
[Claude Code, 2026-06-08]