Monity is a secure personal finance tracker built with Next.js and InsForge.
- Accounts & Balances: Track multiple accounts (bank, cash, cards) with real-time balances and account management.
- Transactions: Add, edit, and filter income or expenses. Advanced search, full-screen editing, and category assignment.
- Budgets: Set monthly budgets per category, track utilization, and get alerts when limits are exceeded.
- Recurring Expenses: Automate regular expenses with flexible scheduling and automatic transaction generation.
- Categories: Organize your finances with system and custom categories, color coding, and easy editing.
- Dashboard & Insights: Visualize balances, income, expenses, and trends with interactive charts and analytics.
- Authentication & Security: Secure login, email verification, Google OAuth, and privacy-first data protection.
- Localization: Multi-language support and locale-aware money formatting for a global experience.
- Profile Management: Edit your display name, avatar, and email; profile data is persisted securely.
- Collapsible Panels: Dashboard quick add and transaction filter/add panels are collapsible and persist state locally.
- Mobile Friendly: Responsive design and refined mobile finance interactions.
- Next.js 16 (App Router)
- React 19
- TypeScript (strict mode)
- Tailwind CSS 4
- InsForge SDK (
@insforge/sdk)
- Dashboard quick add is collapsible and persisted in local storage.
- Transactions page now has collapsible Add and Filters sections, both persisted in local storage.
- Transactions supports advanced client-side filters:
- text search by description/account/category
- type and category filters
- date range filters
- amount range filters
- Transaction editing moved to a full-screen centered modal with editable category, amount, date, and description.
- Custom category editing added through a modal (name and color only; type remains read-only).
- Money values are now formatted with locale-aware currency formatting for improved clarity.
- Language switcher and mobile finance interactions were refined for better usability.
- Recurring expenses now support flexible scheduling and automatic transaction generation.
- Dashboard analytics and charts improved for better insights.
- Enhanced security and privacy-first data handling.
Root
├── AGENTS.md
├── CLAUDE.md
├── README.md
├── eslint.config.mjs
├── next.config.ts
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── postcss.config.mjs
├── proxy.ts
├── skills-lock.json
└── tsconfig.json
app/
api/
auth/
login/route.ts
logout/route.ts
me/route.ts
oauth/google/
callback/route.ts
start/route.ts
refresh/route.ts
register/route.ts
resend-verification/route.ts
verify-email/route.ts
accounts/
[id]/route.ts
route.ts
budgets/
[id]/route.ts
route.ts
categories/
[id]/route.ts
route.ts
dashboard/route.ts
internal/recurring-expenses/run/route.ts
recurring-expenses/
[id]/route.ts
route.ts
transactions/
[id]/route.ts
route.ts
user/me/route.ts
accounts/page.tsx
budgets/page.tsx
categories/page.tsx
dashboard/page.tsx
globals.css
layout.tsx
page.tsx
recurring-expenses/page.tsx
settings/profile/page.tsx
sign-in/page.tsx
sign-up/page.tsx
transactions/page.tsx
verify-email/page.tsx
apple-icon.png
favicon.ico
icon.png
components/
auth/
auth-shell.tsx
credentials-form.tsx
logout-button.tsx
verification-form.tsx
finance/
accounts-manager.tsx
action-button.tsx
budgets-manager.tsx
categories-manager.tsx
dashboard-charts.tsx
dashboard-overview.tsx
finance-shell.tsx
profile-settings-form.tsx
recurring-expenses-manager.tsx
sidebar-account-section.tsx
styled-select.tsx
transactions-manager.tsx
ui-dark.ts
ui.ts
i18n/
language-switcher.tsx
theme-toggle.tsx
ui/
toast-provider.tsx
insforge/
migrations/
001_auth.sql
002_finance.sql
003_recurring_expenses.sql
004_transactions_pagination_indexes.sql
lib/
finance/
formatting.ts
pdf-export.ts
recurring.ts
use-dashboard-export.ts
use-transaction-export.ts
validation.ts
i18n/
client.ts
config.ts
index.ts
server.ts
dictionaries/
en.ts
es.ts
insforge/
api.ts
client.ts
cookies.ts
route-session.ts
session.ts
theme/
theme-provider.tsx
public/
file.svg
globe.svg
image-guideline.png
monity-icon.png
monity-logo.png
monity-logo_black.png
next.svg
vercel.svg
window.svg
icons/
google.svg
Brand assets live in public/ and should be used consistently across app surfaces.
Available files:
public/monity-logo.png: primary logo (icon + wordmark) for light backgrounds.public/monity-logo_black.png: navy wordmark variant for neutral/light UI where stronger text contrast is needed.public/file.svg: primary SVG logo for scalable UI placements when vector output is preferred.public/monity-icon.png: icon-only mark for compact placements (favicon-like, app tiles, sidebar, avatar-style badges).public/image-guideline.png: reference board showing approved color and composition variants.public/icons/google.svg: Google brand icon used in auth UI.
Usage rules:
- Prefer
.svgfor sharp rendering at varied sizes when no raster effects are required. - Do not stretch logos non-uniformly; preserve original aspect ratio.
- Keep clear space around logos (at least the icon's smallest bar width on each side).
- Use icon-only assets only when horizontal space is constrained.
- On dark surfaces, use gradient/bright variants with sufficient contrast.
- Avoid adding drop-shadows, recoloring, or overlay effects that alter brand colors.
Implementation notes (Next.js):
- Use
next/imagefor raster assets (.png) to get automatic optimization. - Place static brand references in
public/and load them via absolute paths (for example,/monity-logo.png).
Create .env.local:
NEXT_PUBLIC_INSFORGE_URL=https://your-project.insforge.app
NEXT_PUBLIC_INSFORGE_ANON_KEY=your-anon-key
NEXT_PUBLIC_APP_URL=http://localhost:3000Supported aliases in code:
- InsForge URL:
NEXT_PUBLIC_INSFORGE_URLorINSFORGE_URLorINSFORGE_BASE_URL - Anon key:
NEXT_PUBLIC_INSFORGE_ANON_KEYorINSFORGE_ANON_KEY - App URL:
NEXT_PUBLIC_APP_URLorAPP_URL
- Install dependencies.
- Configure
.env.local. - Apply SQL migrations in
insforge/migrations/to your InsForge Postgres database. - Start the app.
pnpm install
pnpm devProduction commands:
pnpm build
pnpm start
pnpm run deployLint:
pnpm lintMonity uses InsForge auth and stores session tokens in HTTP-only cookies:
monity_access_token(1 day)monity_refresh_token(30 days)monity_oauth_code_verifier(10 minutes, OAuth handshake)
Supported auth flows:
- Register with email/password
- Login with email/password
- Verify email with OTP
- Resend verification email
- Refresh session using refresh token
- Logout
- Google OAuth sign-in (
/api/auth/oauth/google/startand callback)
The app upserts profile data in public.user_profiles on successful auth.
- Creates
public.user_profiles - Enables RLS
- Adds policies so authenticated users can only read/write their own profile
- Adds shared
public.set_updated_at()trigger function
Creates:
public.accountspublic.categoriespublic.transactionspublic.budgets
Also includes:
- Validation constraints (enum-like checks, positive amounts, period month first-day check)
- Indexes for user/date/category/account lookups
- Seeded system categories (Food, Transport, Housing, etc.)
- RLS policies to scope data by authenticated user
- Triggers for
updated_at
Creates and configures recurring-expense support:
public.recurring_expensespublic.recurring_expense_amountspublic.recurring_expense_occurrences- transaction link fields for recurring traceability
- recurring generation function and supporting constraints/indexes
- RLS policies for recurring entities
All finance/profile endpoints require an authenticated session cookie.
POST /api/auth/register- body:
{ email, password, name? }
- body:
POST /api/auth/login- body:
{ email, password }
- body:
POST /api/auth/logoutPOST /api/auth/refresh- body:
{ refreshToken? }(falls back to refresh cookie)
- body:
POST /api/auth/verify-email- body:
{ email, otp }
- body:
POST /api/auth/resend-verification- body:
{ email }
- body:
GET /api/auth/meGET /api/auth/oauth/google/startGET /api/auth/oauth/google/callback
GET /api/user/mePATCH /api/user/me- body:
{ displayName }(1-80 chars after normalization)
- body:
GET /api/accountsPOST /api/accountsPATCH /api/accounts/:idDELETE /api/accounts/:id
Account payload:
{
"name": "Main Checking",
"type": "bank",
"initialBalance": 1200,
"currency": "USD",
"isActive": true
}Allowed account type values:
bankcashcredit_carddebit_card
GET /api/categoriesPOST /api/categoriesPATCH /api/categories/:idDELETE /api/categories/:id
Category payload:
{
"name": "Groceries",
"type": "expense",
"color": "#22C55E",
"icon": "cart"
}Allowed category type values:
incomeexpense
System categories are read-only; only user-owned non-system categories can be updated/deleted.
GET /api/transactions?accountId=&categoryId=&limit=POST /api/transactionsPATCH /api/transactions/:idDELETE /api/transactions/:id
Transaction payload:
{
"accountId": "uuid",
"categoryId": "uuid",
"type": "expense",
"amount": 34.9,
"description": "Lunch",
"transactionDate": "2026-04-12"
}Validation highlights:
amount > 0transactionDateformatYYYY-MM-DD- account must belong to current user
- category must be either system category or current user's category
GET /api/budgets?periodMonth=YYYY-MM-01POST /api/budgetsPATCH /api/budgets/:idDELETE /api/budgets/:id
Budget payload:
{
"categoryId": "uuid",
"periodMonth": "2026-04-01",
"limitAmount": 500
}Validation highlights:
periodMonthmust be first day of month (YYYY-MM-01)limitAmount > 0- upsert conflict key is
(user_id, category_id, period_month)
GET /api/recurring-expensesPOST /api/recurring-expensesPATCH /api/recurring-expenses/:idDELETE /api/recurring-expenses/:id
Recurring payload (create):
{
"name": "Gym membership",
"accountId": "uuid",
"categoryId": "uuid",
"amount": 29.99,
"frequency": "monthly",
"startDate": "2026-04-01",
"isActive": true
}GET /api/dashboard
Returns aggregated finance data:
- totals (
total_balance,month_income,month_expense,month_net) - account balances (
initial_balance, computedcurrent_balance) - recent transactions (latest 10)
- spending by category (current month)
- budgets with utilization and exceeded flag
Public/auth:
/(landing, redirects to/dashboardwhen authenticated)/sign-in/sign-up/verify-email
Protected finance pages:
/dashboard/accounts/transactions/recurring-expenses/categories/budgets
Core request parsing lives in lib/finance/validation.ts:
- Currency must be 3-letter uppercase code
- Amounts are numeric and rounded to 2 decimals
- Date parsing enforces
YYYY-MM-DD - Transaction and category types are strictly validated
next.config.tscurrently uses default configuration.- TypeScript path alias
@/*maps to project root. strictmode is enabled intsconfig.json.
Private project.