Skip to content

← All guides

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/health and /api/ready: health checks for load balancers.
  • /api/cron/jobs: runs scheduled work. Needs Authorization: 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.

Proventory documentation