Dairify
A multi-tenant milk ledger for dairy businesses: daily sales, purchases, payments, balances, and expenses.
A web app designed and built by Hassan Ur Rahman.
Overview
Dairify is a bookkeeping web app, also installable as a PWA, for small dairy businesses that buy milk from suppliers and sell it to buyers. Each day an operator records the morning and evening milk quantity for every party, the rate per litre, and the amount paid or received. The app keeps running balances for every party and builds daily and date-range reports.
It replaces paper registers. Each business gets its own private workspace, and the first account becomes the admin, who can add staff as operators or read-only viewers. It is set up for Pakistan out of the box (PKR, local time zone and date format), and also tracks expenses and keeps registers of workers, vehicles, and shops.
Key features
Daily milk entry
One sale or purchase entry per party per day, with separate morning and evening quantities, rate per litre, and the amount paid or received.
Party ledger and balances
Buyers and suppliers have opening balances. Remaining amounts carry forward and overpayments show as advances.
Reports
Today, last 7 or 15 days, this month, or a custom range, for the whole business or one party, with milk sold vs purchased, cash in and out, expenses, and net cash. Reports are printable.
Dashboard
The selected day's entries plus a chart that switches between money and litres over 7, 15, or 30 days.
Expenses
Categorised expenses such as feed, fuel, salary, and rent, optionally linked to a worker, vehicle, or shop, with CSV and print/PDF export.
Workers, vehicles, and shops
Registers of staff with role and monthly salary, vehicles with registration numbers, and shops with contact details.
Users and roles
Admins add staff as admin, operator, or viewer. Operators only see the parties and entries they created, and viewers are read-only.
How it's built
- Full-stack TanStack Start (React 19 with file-based routing), deployed to Cloudflare Workers. All data access goes through typed server functions.
- Cloudflare D1 (SQLite) with Drizzle ORM and versioned SQL migrations. SQL views calculate party balances and daily and date-range reports.
- Money is stored as integer paisa and milk as millilitres, and line totals are recalculated on the server, so floating-point errors never reach a balance.
- Custom authentication: scrypt password hashing, hashed session tokens in HttpOnly cookies, login throttling with lockouts, and revocation of other sessions when a password changes.
- Middleware adds CSRF protection and strict security headers (CSP, HSTS, frame denial). A service worker pre-caches static assets and serves an offline fallback page.
Challenges & solutions
- Keeping tenants isolated in one database
- Every query is scoped to the workspace from the session, and database triggers add a second check by rejecting any record that would link data across workspaces.
- Getting money arithmetic exactly right
- Amounts are integers rather than floats, totals are computed on the server instead of trusted from the browser, and reports come from SQL views, so balances and daily totals always agree.
- Secure logins without an auth provider
- Passwords are hashed with scrypt, login timing is equalised for unknown users, failed attempts are throttled, and changing a password revokes all other sessions.
Tech stack
- TanStack Start
- Cloudflare Workers
- Cloudflare D1
- Drizzle ORM
- TypeScript
- TailwindCSS