FinOS is a privacy-first personal finance assistant.
It connects to your Gmail inbox, extracts purchase receipts with the help of an LLM, and serves a dashboard that lets you review spending without sending data to a third-party service.
yeah i know... i spend quite alot :,)
| Component | Description | Tech |
|---|---|---|
backend/ |
Rust service that handles Google OAuth, receipt ingestion, JWT auth, and MongoDB persistence. | Axum, Tokio, MongoDB, Ollama, Google OAuth |
frontend/ |
Next.js Progressive Web App for visualizing receipts and managing accounts. | Next.js 15, React 19, Chakra UI |
The backend exposes a REST API protected by JWT session tokens. A background job periodically syncs mailboxes and stores new transactions. The frontend consumes those APIs and can be installed as a PWA.
User → Google OAuth (PKCE)
→ FinOS backend issues JWT + stores Gmail tokens
→ Background job pulls unread emails via Gmail API
→ Ollama converts receipt emails into structured data
→ MongoDB stores users, tokens, receipts
→ Frontend fetches data with JWT-authenticated requests
Protected endpoints expect either the finos_session cookie or an Authorization: Bearer <token> header.
- Rust toolchain (≥ 1.70)
- Node.js 20+ (for the Next.js app)
- MongoDB instance
- Ollama installed with the model referenced by
OLLAMA_MODEL - Google Cloud project with Gmail API enabled and a downloaded
client_secret_web.json
Create backend/.env (copy from .env-example if available) and define:
MONGO_URI=mongodb://localhost:27017
DATABASE=fin-os-db
OLLAMA_MODEL=llama3.1
ISSUER_EMAILS=["receipts@example.com","orders@merchant.com"]
JWT_SECRET=super-secret-change-mePlace client_secret_web.json in backend/ (contains the Google OAuth credentials).
The redirect URI you configure in Google Cloud must match the one served by the backend (http://localhost:3000/auth/google/callback during local development).
If the frontend needs runtime configuration (e.g. API base URL), create frontend/.env.local with keys such as:
NEXT_PUBLIC_API_BASE_URL=http://localhost:3000-
Start the backend
cd backend cargo runThe service listens on
http://localhost:3000, merges auth + receipt routes, and spawns the ingestion job (interval configurable viastart_sync_job). -
Start the frontend
cd frontend npm install # or pnpm install npm run dev # runs Next.js dev server on port 3001 by default if 3000 is taken
-
Visit the frontend (
http://localhost:3001) and follow the Google sign-in prompt. After OAuth completes, a session JWT is stored and subsequent requests to protected endpoints succeed.
Make sure Ollama is running (ollama serve) and that the configured model is pulled (ollama pull llama3.1).
finOS/
├── backend/
│ ├── src/
│ │ ├── common/ # app state, JWT helpers, Mongo connector
│ │ ├── domain/ # auth, email, ingestor, receipt, user
│ │ └── main.rs # entrypoint, background job launcher
│ └── README.md # backend-specific documentation
└── frontend/
├── src/ # Next.js app routes & components
└── README.md # frontend notes
Consult the component READMEs for API details and development tips.
- Google OAuth tokens are persisted in Mongo via
TokenRecord. - The backend issues short-lived JWTs signed with
JWT_SECRETand validates them through middleware (common::jwt::require_jwt). - The receipt API (
/receipts/:email) demonstrates how to protect routes; replicate the middleware for additional endpoints. - Rotate
JWT_SECRETin production and ensure TLS termination before forwarding cookies/headers.
app::start_sync_job schedules IngestorService::sync_receipts() on a Tokio interval. Adjust the cadence or swap in a cron scheduler if you need specific run times. Sync logic:
- Fetch active users from Mongo.
- Build Gmail search queries based on
last_synced. - Fetch new messages, parse receipts with Ollama.
- Store transactions and update
last_synced.
- Categorisation & budgeting views
- Web push notifications for new receipts
- Multi-provider email connectors (IMAP/Outlook)
- Improved frontend UX (Tailwind layout, charts)
Pull requests and design discussions are welcome. Open an issue with context and proposed changes before large contributions.


