Personal Disposable Income & Cash Flow Companion
Pockt is a high-performance, self-hosted personal finance tracker focused on answering the single most important financial question:
"How much money can I safely spend today?"
- Net Disposable Income Engine: Calculates real-time spendable cash by factoring
Current Cash - Outstanding Bills - Outstanding Debts. - Single-Owner Authentication: First-time setup, cookie-based sessions, and route protection across all pages.
- Bilingual UI (ID/EN): One-tap language toggle that re-renders the entire app — every page, modal, and label.
- Hallmark OKLCH Dual Theme System:
- Bloom (Light): Warm Oat canvas
oklch(0.975 0.008 75)with Signal Terracotta accentoklch(0.64 0.18 40). - Aurora (Dark): Obsidian Slate canvas
oklch(0.14 0.012 250)with Teal Emerald accentoklch(0.74 0.16 170).
- Bloom (Light): Warm Oat canvas
- Side-Rail Compact Navigation (N3 Archetype): Compactable sidebar with logo toggle and 100% monochrome controls.
- 2-Row Mobile-First Layout: Guaranteed zero horizontal scrollbar or clipped content across all viewports (320px–412px+).
- Automated Testing Suite: Full API integration testing (Vitest) and End-to-End browser testing (Playwright).
- 🔐 Single-Owner Authentication: First-time setup flow creates the owner account; session-based login (cookie) protects all data; logout supported.
- 📊 Free-to-Spend Dashboard (
/dashboard): Instant visibility into net disposable income, monthly stats, and live cashflow timeline. - 💰 Payday Allocation View (
/payday): Planning matrix for incoming salary vs. upcoming commitments, bills, debts, and spent totals. - 📥 Income Tracking (
/incomes): Log salary, freelance work, investments, or reimbursements. - 💸 Expense Management (
/expenses): Category filtering, search, and fast entry under 10 seconds. - 📅 Monthly Bills Tracker (
/bills): One-click paid/unpaid toggling and automated monthly reset flow. - 🤝 Debt & Loan Register (
/debts): Installment payment log, remaining balance calculations, and repayment history. - ⚡ Quick Add Modal: Record an expense or income from anywhere in the app in seconds.
- 🌐 Bilingual UI (Bahasa Indonesia / English): Full language toggle applied across every page, modal, and label, persisted to
localStorage. - 🎨 Dual Theme System: Bloom (Light) and Aurora (Dark) themes with one-click switching.
- 📁 CSV Data Export: One-click database export for offline reporting and backup.
pockt/
├── apps/
│ ├── backend/ # Fastify 5 REST API + Drizzle ORM + SQLite + Vitest
│ └── web/ # SvelteKit 5 + Tailwind CSS v4 + Playwright E2E
├── packages/
│ └── shared/ # Shared TypeScript schemas & domain utilities
├── turbo.json # Turborepo build orchestration
└── pnpm-workspace.yaml # pnpm workspace configuration
pnpm installStarts frontend (http://localhost:5173) and backend (http://localhost:3001):
pnpm devpnpm buildPockt uses separate SQLite files so testing never touches your real data:
| Environment | DB file | When used |
|---|---|---|
| Development | pockt.dev.db |
pnpm dev (default, NODE_ENV not production) |
| Production | pockt.prod.db |
Docker deploy / pnpm start with NODE_ENV=production |
| Backend tests | pockt.test.db |
Vitest (wiped on every run) |
The schema auto-creates on server start; you can also run pnpm --filter @pockt/backend db:migrate. Override any path with the DATABASE_URL env var. In Docker, the prod DB lives inside the pockt-db-data volume, and the web container proxies /api to the backend via API_INTERNAL_URL (server-side proxy, no extra reverse proxy needed).
Backups: pnpm --filter @pockt/backend backup writes a WAL-safe snapshot into backups/ next to the DB file (keeps the last 14). In Docker, that lands inside the pockt-db-data volume (/app/apps/backend/data/backups/) — run it regularly (e.g. cron on the host: docker exec pockt-backend pnpm --filter @pockt/backend backup).
The app asks you to create the owner account on the /register page when it detects no owner exists. You can also create or reset the owner via CLI:
# Create/reset owner credentials (prompts for username + password)
pnpm --filter @pockt/backend userRegistration is locked once an owner exists (single-owner app). Reset your password with the
userCLI command if you ever forget it.
- Sessions: login issues a random 256-bit token stored server-side in the
sessionstable (30-day expiry). The cookie isHttpOnly+SameSite=Laxand gets theSecureflag in production. Logout deletes the row, so stolen cookies die at logout. - Rate limiting: login/register are limited to 10 attempts per 15 minutes per IP (honors
CF-Connecting-IPbehind Cloudflare Tunnel). - Fail-closed secret: in production the backend refuses to start without a strong
COOKIE_SECRET(placeholder values are rejected). - CORS: same-origin only (
origin: false); every/api/*route except health/auth validates the session on each request. - Password hashing: bcrypt (10 rounds); SQL is parameterized (Drizzle); frontend escapes all output (Svelte).
Pockt includes an automated testing suite for both backend APIs and frontend UI viewports. Backend tests run against an isolated pockt.test.db database that is wiped on every run; E2E tests use a global setup that ensures an owner exists and seeds dummy data when empty.
# Run backend API integration tests (17 tests via Vitest, isolated test DB)
pnpm test
# Run frontend E2E layout & responsive tests (33 tests via Playwright, 3 viewports)
pnpm test:web
# Run all backend + frontend tests
pnpm test:allDistributed under the MIT License.
