Skip to content

Shaya16/Spent

Repository files navigation

Spent

Spent

Local-only personal finance for Israeli bank accounts. Encrypted. AI-categorized. Yours.

Next.js React TypeScript SQLite License: MIT Status: Beta Docs

Warning

Personal, local-only tool. Scraping financial institutions may violate their Terms of Service. Use only for your own accounts on your own machine. Do not deploy as a hosted service.

Spent dashboard

Why Spent?

Israeli banks have terrible exports, YNAB doesn't speak ILS gracefully, and every "cloud finance" app wants you to hand over your bank password. Spent is the answer for people who'd rather just run something on their own laptop.

Your transactions get pulled directly from your bank with israeli-bank-scrapers, stored in a local SQLite file you can cp and back up like any other file, and categorized by an AI provider you choose: paid Claude, free local Ollama, or nothing at all.

The trade-off is honest: you self-host, you trust the scraper, and you accept that banks may not love automation. In return you get a fast, beautiful, fully offline dashboard that never phones home.

Features

🏦 Israeli bank integration

Isracard, Bank Hapoalim, and Max work out of the box. Visa Cal and Bank Leumi are on the roadmap.

πŸ€– AI categorization

Choose Claude (Anthropic) for best accuracy, Ollama for fully local LLMs, or skip and categorize manually.

πŸ”’ Local-only & encrypted

Credentials encrypted with AES-256-GCM. Server binds to 127.0.0.1 only β€” never reachable from your LAN or the internet.

πŸ“Š Budgets with pacing

Hierarchical categories, monthly targets, "ahead of pace" hero card, and per-category drilldown.

πŸŒ“ Light & dark theme

Polished buttercream-and-sage palette in light mode, warm charcoal in dark. System-aware by default.

🍎 Menu bar / tray app

Native companion in your menu bar (macOS) or notification area (Windows). Status indicator, one-click open dashboard, sync, and start/stop/restart the service.

🎯 Auto-detected transfers

Credit card payments and inter-account moves are recognized and excluded from spending totals.

πŸ“… Multi-month history

Pull up to 3 months of transactions per sync (configurable). Most banks support 12 months total.

πŸ” Merchant memory

Once you correct an AI categorization, Spent remembers β€” same merchant goes to the right category next time.

Screenshots

Dashboard β€” light Dashboard β€” dark
Dashboard light mode Dashboard dark mode
Transactions Setup wizard
Transactions page Setup wizard bank picker
Categories AI provider
Category management AI provider settings
Bank accounts
Bank accounts settings

How it works

flowchart LR
    Bank["🏦 Israeli bank<br/>(Isracard / Hapoalim / Max)"]
    Scraper["Puppeteer scraper<br/>(israeli-bank-scrapers)"]
    DB[("πŸ“¦ SQLite<br/>data/spent.db<br/>(WAL mode)")]
    AI{"πŸ€– AI provider<br/>Claude Β· Ollama Β· None"}
    UI["πŸ–₯ Dashboard<br/>http://spent.local:41234"]

    Bank -->|HTTPS<br/>credentials encrypted| Scraper
    Scraper -->|new transactions| DB
    DB -->|uncategorized batch| AI
    AI -->|category proposals| DB
    DB --> UI

    subgraph local["πŸ”’ Your machine β€” 127.0.0.1 only"]
        Scraper
        DB
        UI
    end
Loading

Everything inside the dashed box stays on your laptop. The only outbound traffic is to your bank (for scraping) and optionally api.anthropic.com (if you chose Claude) or localhost:11434 (if you chose Ollama).

Supported banks

Bank Type Status
Isracard Credit card βœ… Supported
Bank Hapoalim (incl. Poalim wallets) Bank βœ… Supported
Max (formerly Leumi Card) Credit card βœ… Supported
Visa Cal Credit card 🚧 Planned
Bank Leumi Bank 🚧 Planned

Don't see your bank? Adding a scraper is a small wrapper around israeli-bank-scrapers β€” see Contributing.

AI providers

Claude (Anthropic) Ollama (local) None
Cost ~β‚ͺ0.004 per sync Free Free
Accuracy Best Good (depends on model) Manual
Network api.anthropic.com localhost:11434 Offline
Setup API key Install Ollama + pull a model Nothing

Default model when Claude is selected: claude-haiku-4-5 (cheap, fast, accurate for categorization). For Ollama, llama3.2:3b is the recommended default.

You can change providers any time from Settings β†’ AI provider. Existing categorizations are kept.

Requirements

  • Node.js 22+
  • macOS 13+, Ubuntu 22+ (with systemd), or Windows 11
  • Build tools for the menubar (only if you want the tray; npm run setup will offer to install these for you if they're missing):
    • macOS: Xcode Command Line Tools (xcode-select --install)
    • Windows: .NET 8 SDK (winget install Microsoft.DotNet.SDK.8)
  • A bank account with 2FA disabled (most Israeli banks require this for automation β€” OneZero is the exception)

Install

Prefer a screenshot-by-screenshot walkthrough? The step-by-step install guides on the docs site cover macOS and Windows separately, with build-tool setup and tray-app gotchas spelled out.

git clone https://github.com/Shaya16/Spent.git
cd spent
npm install
npm run setup

npm run setup does everything: builds the Next.js app, installs the always-on service (LaunchAgent on macOS / systemd on Linux / Task Scheduler on Windows), adds 127.0.0.1 spent.local to your hosts file, builds the platform menubar from source, installs it to the standard location, registers it to auto-start at login, and opens the dashboard. The hosts edit is the only step that asks for sudo / Administrator.

On Linux there is no native menubar. npm run setup installs the service and opens the browser. Control the service with npm run service:* (see below).

First launch of the menubar on macOS/Windows shows an unsigned-binary warning (Gatekeeper / SmartScreen). That's expected: you built it locally and didn't pay for a code-signing certificate. Right-click β†’ Open (macOS) or "More info" β†’ "Run anyway" (Windows). One-time.

Open http://spent.local:41234 and bookmark it.

First-time setup

In the browser:

  1. Connect your bank β€” credentials are AES-256-GCM encrypted before they touch disk.
  2. Choose an AI provider β€” Claude (default), Ollama, or none.
  3. Set your monthly ceiling β€” total spend you want to stay under each month.
  4. Set per-category budgets β€” type an amount on any category to budget it; leave blank to track without a limit.
  5. Done. Sync starts automatically: 3 months of transactions, then AI categorization.

How you'll use it

What you want Run
Just use the app (no coding) Open http://spent.local:41234
Code and see changes instantly npm run dev β†’ http://127.0.0.1:3000
Update the always-on app after editing npm run service:reload

Rare cases:

  • Changed the menu bar app source β†’ npm run menubar:install:mac (or :windows) to rebuild and reinstall.
  • Changed install scripts or hostname β†’ npm run service:uninstall && npm run service:install.

Service commands

Command What it does
npm run service:status Running? Bound to loopback?
npm run service:start / :stop Start/stop now
npm run service:reload Rebuild and restart
npm run service:logs Tail server logs
npm run service:open Open the app in your browser
npm run service:uninstall Remove auto-start and hosts entry. data/ is untouched.

Uninstall

npm run uninstall

Reverses everything npm run setup installed:

  • Stops the background service and removes the LaunchAgent / Task Scheduler entry / systemd unit.
  • Removes the 127.0.0.1 spent.local line from your hosts file (asks for sudo / Administrator).
  • Quits the menubar, removes the installed app, and removes it from Login Items / Startup.

Kept on purpose:

  • data/: your transactions, budgets, and encryption key. To wipe your data: rm -rf data/.
  • The repo itself. To remove Spent entirely: rm -rf data/ && cd .. && rm -rf spent/.

If you only want to remove the menubar but keep the always-on web app:

  • macOS: rm -rf ~/Applications/Spent.app and remove "Spent" from System Settings β†’ General β†’ Login Items.
  • Windows: delete %LOCALAPPDATA%\Programs\Spent\ and remove Spent.lnk from shell:startup.

If you only want to remove the always-on service but keep the menubar (so it's there if you reinstall later): npm run service:uninstall.

Security at a glance

Concern Defense
Credentials at rest AES-256-GCM, encryption key file mode 0600 (server refuses to start otherwise)
Network exposure Bound to 127.0.0.1 only β€” not reachable from your LAN or the internet
Browser CSRF Origin / Referer validation on every mutation
Bot detection Chromium sandbox on by default (SPENT_DISABLE_CHROMIUM_SANDBOX=1 to opt out)
Bundle integrity israeli-bank-scrapers, better-sqlite3, and @anthropic-ai/sdk pinned to exact versions
Browser hardening Strict CSP, X-Frame-Options: DENY, Permissions-Policy locks down camera/mic/geo/payment

Turn on full-disk encryption (FileVault / BitLocker / LUKS). The encryption key file sits next to the database, so disk-level protection is your last line of defense if the laptop is lost.

Full threat model and responsible-disclosure policy β†’ SECURITY.md.

Where your data lives

  • data/spent.db β€” transactions, categories, budgets, settings
  • data/.encryption-key β€” 32-byte AES key, mode 0600
  • ~/Library/Logs/Spent/ (macOS) / ~/.local/state/spent/log/ (Linux) β€” service logs

Back up data/ like any other folder. To migrate to a new machine, copy data/ over and run npm run service:install.

Architecture & code map

spent/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ app/                  Next.js App Router (routes + API)
β”‚   β”‚   β”œβ”€β”€ (dashboard)/      Dashboard, transactions, settings pages
β”‚   β”‚   β”œβ”€β”€ api/              Sync (SSE), summary, transactions, setup
β”‚   β”‚   └── setup/            First-run wizard
β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”œβ”€β”€ dashboard/        Hero card, category grid, budget drawer
β”‚   β”‚   β”œβ”€β”€ setup/            Bank, AI, target, budgets steps
β”‚   β”‚   └── settings/         Per-tab settings panels
β”‚   β”œβ”€β”€ lib/                  Shared client-side types and helpers
β”‚   └── server/
β”‚       β”œβ”€β”€ ai/               Claude + Ollama provider implementations
β”‚       β”œβ”€β”€ db/               SQLite singleton, migrations, query helpers
β”‚       β”œβ”€β”€ lib/              Encryption, dedup, transfer detection, pace
β”‚       └── scrapers/         Wrapper around israeli-bank-scrapers
β”œβ”€β”€ menubar/                  Tray companions (built locally by `npm run setup`)
β”‚   β”œβ”€β”€ mac/                  Swift MenuBarExtra app
β”‚   └── windows/              C# WinForms NotifyIcon app
β”œβ”€β”€ scripts/service/          LaunchAgent / systemd / Task Scheduler installer
β”œβ”€β”€ website/                  Astro + Starlight docs site (auto-deploys to GitHub Pages)
β”œβ”€β”€ .github/workflows/        CI β€” docs site deploy
β”œβ”€β”€ Spent.sln                 Visual Studio solution for the Windows menubar project
└── data/                     SQLite + encryption key (gitignored)

Troubleshooting

The Troubleshooting docs cover Defender, Gatekeeper, Cloudflare bot challenges, and bank-specific quirks in more depth.

  • Port 41234 in use β†’ lsof -nP -iTCP:41234 -sTCP:LISTEN (Unix) or netstat -ano | findstr :41234 (Windows). Kill the offender and re-run install.
  • Gatekeeper blocks Spent.app β†’ right-click β†’ Open β†’ Open. One-time.
  • Linux: "systemd user instance not available" β†’ loginctl enable-linger $USER.
  • Windows: hosts edit fails / spent.local doesn't resolve β†’ re-run install from an elevated PowerShell (Win+X β†’ "Terminal (Admin)") so it can edit C:\Windows\System32\drivers\etc\hosts. After the edit, the installer flushes the DNS cache automatically; if you edited hosts manually, run ipconfig /flushdns. http://127.0.0.1:41234 always works as a fallback.
  • Bank scrape fails with "Cloudflare" β†’ temporarily run with SPENT_DISABLE_CHROMIUM_SANDBOX=1 to let Puppeteer use a real Chrome profile.

Roadmap

  • Visa Cal scraper
  • Bank Leumi scraper
  • CSV / OFX export
  • Custom user-defined categories
  • Hebrew UI
  • Mobile companion (Phase 2)
  • Multiple workspaces in the menu bar / tray app

Contributing

Spent is built for personal use first, open-source second. PRs welcome for:

Conventions:

  • TypeScript strict mode. No any without a comment.
  • Conventional commits: feat:, fix:, chore:, docs:, refactor:.
  • Comments only where the "why" isn't obvious. No em dashes in code, commits, or docs.

License

MIT

Acknowledgments

Built on the shoulders of:

About

No description, website, or topics provided.

Resources

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors