Developer resources
A technical overview for people deploying, customising or extending Proventory.
Stack
Next.js 15 (App Router) with React 19, TypeScript in strict mode, Tailwind CSS, Prisma 6 on PostgreSQL 15 or 16, Zod for validation, Vitest for unit and integration tests, Playwright for browser tests, pdf-lib for PDFs and exceljs for spreadsheets.
Code layout
| Folder | What is in it |
|---|---|
src/app |
Pages, server actions and the few API routes |
src/domains |
Business logic by area (sales, inventory, invoices, accounting, hr, crm, marketplace, sales-pages and more) |
src/platform |
Shared plumbing: database client, tenant context, permissions, money, errors, audit |
src/industry-registry |
The eleven industry packs and their contract |
src/ui |
Shared interface components |
prisma |
Schema and migrations |
scripts |
Database role setup, build and demo scripts |
tests |
Unit, integration and browser tests |
Multi-tenancy and security
- Every business is a tenant. Tenant tables carry a
tenantId, and PostgreSQL row-level security (enabled and forced) hides other tenants' rows even if application code forgets a filter. - The app connects as a restricted role (
bmt_app), never as the table owner. The owner connection (DIRECT_URL) is used only for migrations. - Stock and accounting ledgers are append-only: database triggers refuse updates and deletes.
- Passwords use scrypt. Session tokens are stored hashed. Two-factor sign-in uses TOTP with encrypted secrets and recovery codes.
HTTP surface
There is no public REST API in 1.0.0. The routes under /api are:
/api/healthand/api/ready: health checks for load balancers./api/cron/jobs: runs scheduled work. NeedsAuthorization: Bearer $CRON_SECRET./api/webhooks/...: payment provider callbacks, verified by signature.
Environment variables
| Name | Purpose |
|---|---|
DATABASE_URL |
Runtime connection, user bmt_app |
DIRECT_URL |
Owner connection for migrations |
APP_URL |
Public address, used in emails and payment callbacks |
APP_SECRET |
32+ characters. Encrypts stored secrets. Do not change casually |
CRON_SECRET |
16+ characters. Protects the scheduled-job route |
PAYMENT_PROVIDER, PAYSTACK_SECRET_KEY |
Subscription billing |
EMAIL_PROVIDER, RESEND_API_KEY, EMAIL_FROM |
Email delivery |
DEMO_MODE |
true on a demo deployment only |
Everyday commands
npm run dev # local development
npm run check # lint, type-check and tests
npm run test:e2e # browser tests (build first)
npm run db:deploy # apply migrations and refresh the app role
npm run demo:reset # rebuild the demo business (demo deployments only)
Adding or changing an industry pack
A pack is one file in src/industry-registry/packs. It lists default units, categories, roles, product attributes, workflows and reports, and it is validated against a schema when the app starts. Copy a similar pack, change it, and register it in the registry.
Testing
The suite has more than 400 unit and integration tests (they run against a real PostgreSQL database) and 27 browser tests. Payment providers and email are tested with simulated replies.