Skip to content

Architecture at a glance

One app in front of one database, and the database, not the app, decides who can do what. (A Next.js application over a single PostgreSQL database; the app is a client of it.)

How it's put together

Veristep is a Next.js 16 application (App Router, server components, server actions) deployed on Vercel, in front of a single PostgreSQL database on Supabase with its storage and authentication. There is no separate API tier and no message bus: a request runs as the signed-in person, every read and write goes through PostgREST (the database's own web interface) or a direct server-side connection, and Postgres decides what that person may do.

That last sentence is the design. The rules live in the database itself: authorization, evidence rules, freezes and audit are triggers, policies and security-definer functions there. The app gives friendly refusals, but it's never the only thing standing between a person and a row.

Application
Next.js on Vercel. Pages read as the signed-in person and forms write as that person (server components and server actions); a handful of route handlers stream exports and run scheduled jobs (crons).
Database
PostgreSQL on Supabase, AWS us-east-2. Every table checks who may see each row (row-level security). 79 idempotent, checksummed migrations.
Files
Private storage. A file opens only through a signed link that expires after an hour, and who can see it follows the record that points at it (Supabase Storage, private bucket, lt_evidence_ref).
Auth
Supabase Auth: email and password, with an optional second factor from an authenticator app (TOTP, AAL2 step-up). Passwords never touch our application code.
AI
Ten AI assistants run on a model provider behind one interface (Gemini today; Anthropic and OpenAI-compatible endpoints supported). They can act only through a fixed list of tools, each permission-checked (the tool registry).

Every module can come out (sidecars)

Each part of the product is a self-contained module you could take out without breaking the rest. The codebase is divided into named modules in a manifest (modules.json): every source file and every migration belongs to exactly one, a module may import only from those it declares, the graph is acyclic, and a module marked non-core is never depended on by a core one. An automated test enforces all of that, so the AI assistants, billing, drawings, RFID and the rest can be removed without the quality platform noticing.

Where to check it yourself
  • · modules.json and test/architecture.test.mjs
  • · db/ — read the migrations in order; each file states its purpose at the top
  • · 01-product/architecture/ — the threat model, ADRs and role model

These are files in our codebase. If you’re a customer under agreement, we’ll walk you through any of them.