Execution-first compliance platform for ISO 9001:2015 & 21 CFR Part 11. Engineered for banking operations, built for auditability, styled with premium fintech aesthetics.
QMSentry is not a passive policy archive — it is the primary daily operational workspace where quality management, risk mitigation, and continuous improvement are executed in real time. Every user action is cryptographically signed, hash-chained, and immutably logged before any database commit. Every endpoint is authenticated, every input Zod-validated, every file magic-byte verified.
Built with Next.js 14 App Router, TypeScript (strict), Tailwind CSS v4, Prisma ORM v6, PostgreSQL 16, TipTap v3, BullMQ, Redis, and 58 automated tests covering hash chain integrity, RBAC, Zod schemas, and file upload security.
21 fully integrated modules mapping to international regulatory standards. Every module has a dedicated list page, a /new creation page, and a /[id] detail page — no modals for primary workflows.
| Module | Route | ISO 9001 Clause | 21 CFR Part 11 |
|---|---|---|---|
| Executive Dashboard | /dashboard |
§9.1 | View-only |
| Document Management (EDMS) | /documents |
§7.5 | Active Signatures |
| Process Mapping | /processes |
§4.4 | Audit Trail |
| Risk Register | /risks |
§6.1 | Audit Trail |
| FMEA Worksheets | /fmea |
§6.1 | Audit Trail |
| Non-Conformities (NC) | /nc |
§8.7 | Active Signatures |
| CAPA Management | /capa |
§10.2 | Active Signatures |
| Kaizen Suggestions | /kaizen |
§10.3 | Audit Trail |
| Training & Competency | /training |
§7.2 | Active Signatures |
| Internal Audits | /audits |
§9.2 | Audit Trail |
| KPI Dashboards | /kpis |
§9.1 | View-only |
| Management Review | /management-review |
§9.3 | Active Signatures |
| Change Requests | /change-requests |
§8.5.6 | Active Signatures |
| Supplier Quality | /suppliers |
§8.4 | Audit Trail |
| Customer Complaints | /complaints |
§8.2.1 | Audit Trail |
| Task Tracker | /tasks |
— | View-only |
| Traceability Matrix | /traceability |
§7.5.3 | View-only |
| Digital Signatures | /signatures |
21 CFR §11 | Core Engine |
| CBS Integration Layer | /cbs |
— | Cryptographic |
| Settings / Admin | /settings |
— | Configurable |
| Production Stack | /settings/infrastructure |
— | Super Admin |
Document Management (EDMS) Full lifecycle management (Draft → Review → Approved → Released) enforced by a fixed 4-layer approval chain:
- Document Manager (DOCCTRL department) — completeness review
- Approval Group (uploader selects at submission): Group A (QM / Audit / Compliance / Risk / AML) or Group B (QM / Audit / Compliance / Risk) — all members in the chosen group approve in parallel
- Board Committee (BOARD department) — final sign-off; transitions status to
PENDING_MOM - Minutes of Meeting (MOM) — mandatory attachment upload before the document is promoted to
RELEASED
Custom TipTap A4-layout editor with Arabic/RTL support, inline feedback threads, and automatic PDF rendering. Signatures are PKI-backed with RFC 3161 timestamps. Document numbers are generated atomically (no duplicate-number race conditions) via a per-prefix-year sequence counter in the database.
Process Mapping Full process profiles with 10 ordered sections per process: Purpose, Scope, Interfaces, Inputs & Outputs, Activities table, Risks & Controls, KPIs with live progress bars, Applicable Documents (L1–L4 hierarchy), Competency Requirements, and BPMN-style swimlane flow diagram. A "View as Document" toggle renders any process as a formal ISO QMS document — complete header table, numbered sections, approval signature table — ready to print or export as PDF.
Risk Register Interactive 5×5 Risk Matrix computing raw and residual scores (impact × likelihood). Each risk links to mitigations, linked documents, and CAPAs.
FMEA Worksheets Failure Mode and Effects Analysis with automated RPN calculation (Severity × Occurrence × Detection), corrective control tracking, and CAPA linking. The worksheet table includes a second header row with guiding questions under each column (e.g. "What is the process step?", "What could go wrong?", "What is the impact on the customer?") matching the standard FMEA Excel template layout.
Non-Conformities Auto-routing by department, immediate containment capture, integrated 5-Whys root cause analysis, and direct CAPA generation.
CAPA Management Closed-loop corrective and preventive actions with milestone task lists, evidence attachments, and formal verification sign-off. Linked to NCs, audit findings, complaints, and change requests.
Kaizen Suggestions 10-step improvement pipeline: current situation, root cause (5-Whys), proposed solution, benefit/risk analysis, implementation plan, resource budget, KPI targets, AI-assisted review, solution testing, and document linking.
Internal Audits Audit scheduler with auditor assignment, ISO-clause-linked checklist generation, findings with direct CAPA triggers, and annualized schedule progress tracking.
Training & Competency Per-user training records with expiry tracking, status workflow (Pending → Completed / Overdue), competency matrix grouped by role, and privileged admin view with bulk assign and delete. Stats cards: Total, Completed, Overdue, Expiring in 30 days.
CBS Integration Layer Secure Core Banking System connector handling ISO 20022 XML messages (Pacs.008, Camt.053), HMAC-verified webhook payloads, and AES-256-GCM credential storage. Sandbox and production environments.
Settings — Security
Password policy, session timeouts, account lockout thresholds, and audit log retention — all persisted to the SystemSetting table and editable by ADMIN without a deployment.
Production Stack (Infrastructure View)
Visual architecture dashboard at /settings/infrastructure showing all 13 production layers with status badges, feature checklists, file references, environment variable requirements, and live deployment commands.
Every data modification receives a monotonically sequenced entry. Each row computes a SHA-256 hash over its own content and the previousHash of the preceding row — creating a cryptographic chain that breaks if any historical entry is altered:
entryHash = SHA-256(sequenceNumber ‖ action ‖ entityId ‖ userId ‖ createdAt ‖ previousHash)
- Hash computation and INSERT happen atomically inside a single Prisma transaction — no race condition possible.
- In production PostgreSQL:
REVOKE UPDATE, DELETE ON "AuditLog" FROM <app_role>— the DB engine physically rejects any modification. SQL provided inscripts/db-init.sql. - Verified by 7 automated unit tests covering tampering of every field in the chain.
Users generate RSA-PSS (2048-bit) or ECDSA (P-256) key pairs. Private keys are AES-256-GCM encrypted at rest. Each signature records: signer name, high-precision UTC timestamp, SHA-256 hash of the document snapshot at signing time, and the administrative meaning (Author / Reviewer / Approver). RFC 3161 TSA tokens provide qualified, externally-verifiable timestamp proof.
| Control | Implementation |
|---|---|
| Password hashing | bcrypt with cost factor 12 |
| Session strategy | JWT, never stored server-side |
| Session expiry | 8 h (standard roles), 4 h (ADMIN / APPROVER) |
| Cookie flags | httpOnly, sameSite: strict, secure in production |
| Account lockout | 5 failed attempts → 30-minute lockout (stored in DB) |
| Password complexity | Min 12 chars, uppercase, lowercase, digit, special character |
| Forced reset | mustChangePassword flag enforced at middleware — cannot be bypassed by URL |
| Startup validation | App refuses to start if NEXTAUTH_SECRET < 32 chars or CBS_ENCRYPTION_KEY ≠ 32 bytes |
8-level role hierarchy enforced in both middleware and every Server Action via requireRole() / requirePermission():
| Role | Level | Capabilities |
|---|---|---|
VIEWER |
0 | Read-only within own department; can submit Kaizens |
Q_DEPT_VIEW |
0 | Read-only scoped to Quality department |
AUDIT |
1 | Conduct audits, view audit logs |
EDITOR |
2 | Full create/edit within own department |
REVIEWER |
3 | Cross-department read for audits and compliance |
APPROVER |
4 | Cross-department approval with cryptographic signature |
DEPARTMENT_ADMIN |
5 | Administrative rights scoped to own branch |
ADMIN |
6 | Global configuration, user lockout, database diagnostics |
Verified by 17 automated unit tests covering all roles against all permission actions.
- Every
/api/route checks session before processing — including AI chat, PKI generation, signing, and file uploads. withApiAuth()middleware handles session check, role validation, Zod body validation, and audit log in one place.- Rate limiting: Nginx (IP-level) + Upstash Redis REST (user-level). Auth: 10 req/min, Uploads: 5 req/min, Standard: 60 req/min.
- Response headers on every route:
X-Frame-Options: DENY,X-Content-Type-Options: nosniff,Referrer-Policy: strict-origin-when-cross-origin,Permissions-Policy: camera=(), microphone=(), geolocation=(),Strict-Transport-Security, fullContent-Security-Policy.
- Magic-byte verification: content signature checked against declared MIME type — a
.pdfcontaining PE/ELF bytes is rejected. - Extension whitelist: only
pdf, doc, docx, xls, xlsx, txt, png, jpg, jpeg, gif, webpaccepted. - Size limits: documents max 25 MB, images max 5 MB — enforced before reading content.
- Filename sanitization: null bytes,
../,..\, and non-safe characters stripped; max 255 chars. - S3-compatible storage: files uploaded to Cloudflare R2 or AWS S3 in production; served via signed URLs (1-hour TTL max). Falls back to authenticated local API route in development.
- Key format:
{module}/{entityId}/{uuid}.{ext}— no user-controlled path segments. - Verified by 14 automated security tests.
The full production stack is implemented and documented in /settings/infrastructure (visible to ADMIN). Configuration files are committed to the repo.
| # | Layer | Technology | Status |
|---|---|---|---|
| 1 | Routing / Reverse Proxy | Nginx 1.27, TLS 1.3, HTTP/2 | Configured |
| 2 | Web / Application | Next.js 14.2, React 18.3, TypeScript 5 | Live |
| 3 | API / Backend | Next.js Route Handlers, Server Actions, Zod | Live |
| 4 | Database | PostgreSQL 16, Prisma ORM 6, Migrations | Live |
| 5 | Database Pooling | PgBouncer 1.22, Transaction Pool Mode | Configured |
| 6 | Caching | Redis 7, ioredis, LRU eviction | Configured |
| 7 | Background Jobs | BullMQ 5, Redis, Cron repeats | Configured |
| 8 | Rate Limiting | Upstash Redis REST + Nginx zones | Live |
| 9 | Object Storage | Cloudflare R2 / AWS S3, Presigned URLs | Live |
| 10 | Security | NextAuth, RBAC, AES-256-GCM, RSA-PSS, Hash Chain | Live |
| 11 | Testing & Quality | Vitest 4, Playwright 1.60, TypeScript strict | Live |
| 12 | Observability | Pino 10 (JSON), pino-pretty (dev), redacted fields | Configured |
| 13 | Backup & Recovery | pg_dump + gzip + S3, 30-day retention, crond | Configured |
Key production files: Dockerfile, docker-compose.prod.yml, nginx/nginx.conf, scripts/backup.sh, scripts/restore.sh, scripts/db-init.sql, src/lib/queue/index.ts, src/lib/cache/index.ts, src/lib/observability/logger.ts.
| Layer | Technology |
|---|---|
| Framework | Next.js 14 App Router, React 18 |
| Language | TypeScript 5 (strict mode, zero errors) |
| Styling | Tailwind CSS v4 |
| ORM | Prisma v6 (fully typed — no raw SQL in src/) |
| Database | PostgreSQL 16 + PgBouncer (transaction pool mode) |
| Auth | NextAuth.js v4 |
| Rich Text | TipTap v3 (RTL, tables, inline comments) |
| Object Storage | S3-compatible (Cloudflare R2 / AWS S3) with local dev fallback |
| Caching | Redis 7 + ioredis (in-process Map fallback for dev) |
| Background Jobs | BullMQ 5 + Redis |
| Rate Limiting | Upstash Redis REST + Nginx limit_req_zone |
| Logging | Pino 10 (structured JSON) + pino-pretty (dev) |
| Forms | React Hook Form + Zod v4 |
| AI | Vercel AI SDK — streams from local Ollama (private inference) |
| Unit Tests | Vitest v4 |
| E2E Tests | Playwright |
| Container | Docker multi-stage build (deps → builder → runner) |
| Reverse Proxy | Nginx 1.27 (TLS 1.3, HSTS, rate limiting, gzip) |
qmss/
├── Dockerfile ← Multi-stage production build
├── docker-compose.db.yml ← Dev: PostgreSQL 16 only
├── docker-compose.prod.yml ← Prod: Nginx + App + PgBouncer + Redis + Backup
├── nginx/nginx.conf ← TLS, rate limits, security headers
├── scripts/
│ ├── backup.sh ← Daily pg_dump → gzip → S3 + 30-day pruning
│ ├── restore.sh ← S3 download + gunzip + psql restore
│ └── db-init.sql ← Least-privilege role + AuditLog REVOKE
├── run_qms.bat ← Windows smart launcher (dev)
├── vitest.config.ts
├── playwright.config.ts
├── prisma/
│ ├── schema.prisma ← PostgreSQL schema (21 modules)
│ ├── migrations/
│ │ ├── 20260518000000_init_postgres/ ← Full base schema
│ │ ├── 20260621000001_document_approval_layers/ ← 4-layer approval + departments
│ │ └── 20260621000002_document_sequence/ ← Atomic doc-number counter
│ ├── seed.ts ← Idempotent seeder (upsert-based)
│ └── reset.ts
├── tests/
│ ├── unit/ ← Hash chain, Zod schemas, RBAC
│ ├── security/ ← File upload, env validation
│ ├── integration/
│ └── e2e/ ← Playwright auth + security headers
└── src/
├── app/
│ ├── (auth)/ ← Login, forced password reset
│ ├── (dashboard)/ ← 21 modules + settings
│ │ └── settings/
│ │ ├── users/ ← User management (soft-delete, pagination)
│ │ ├── security/ ← Password policy, sessions, lockout, audit retention
│ │ ├── infrastructure/ ← Production stack architecture view (13 layers)
│ │ ├── departments/
│ │ ├── ai/
│ │ └── profile/
│ ├── (public)/ ← Public complaint form, signature verification
│ └── api/ ← REST endpoints (all require session)
├── components/
│ ├── layout/ ← Sidebar, navbar, RTL wrappers
│ ├── shared/ ← TipTap editor, PKI modals, AI overlay
│ ├── processes/ ← SwimlaneBuilder, ProcessProfileForm
│ ├── charts/ ← KPI visualizers (Recharts)
│ └── ui/ ← Atomic primitives (Radix + shadcn)
└── lib/
├── core/
│ ├── auth.ts ← NextAuth config (session expiry, cookie flags)
│ ├── audit.ts ← SHA-256 hash-chained immutable logger
│ ├── db.ts ← Prisma client + PgBouncer support
│ ├── startup.ts ← Env validation at cold start
│ └── schemas/ ← Zod schemas for every module
├── security/
│ ├── permissions.ts ← 8-role RBAC matrix + 50+ permission actions
│ ├── ratelimit.ts ← Upstash Redis rate limiter
│ ├── pki-crypto.ts ← RSA-PSS / ECDSA / AES-256-GCM / TSA
│ └── cbs-crypto.ts ← HMAC webhook verification
├── storage/
│ └── index.ts ← S3/R2 upload + signed download URLs
├── cache/
│ └── index.ts ← Redis cache (withCache, CacheKeys, dev fallback)
├── queue/
│ └── index.ts ← BullMQ queues (notifications, docs, scheduled jobs)
├── observability/
│ └── logger.ts ← Pino structured logger with field redaction
├── middleware/
│ ├── api-handler.ts ← withApiAuth() — session + role + Zod + audit
│ └── api-middleware.ts ← API key validation for external integrations
└── actions/ ← React Server Actions (one file per module)
├── operations/ ← processes, risks, FMEA, KPIs, training
├── compliance/ ← audits, nc, capa, complaints
├── admin/ ← users, departments, settings
├── integration/ ← PKI, CBS
└── shared/ ← attachments (magic-byte + S3), utils
# Run all unit + security tests (58 tests, ~700ms)
npm test
# Watch mode
npm run test:watch
# Coverage report
npm run test:coverage
# E2E browser tests (Playwright auto-starts dev server)
npm run test:e2e
# Playwright interactive UI
npm run test:e2e:ui| Suite | File | Tests | Status |
|---|---|---|---|
| SHA-256 hash chain integrity | tests/unit/audit-hash-chain.test.ts |
7 | ✅ Pass |
| Zod schema validation | tests/unit/schemas.test.ts |
11 | ✅ Pass |
| RBAC permission matrix | tests/unit/rbac.test.ts |
17 | ✅ Pass |
| File upload security | tests/security/file-upload.test.ts |
14 | ✅ Pass |
| Startup env validation | tests/security/startup-validation.test.ts |
9 | ✅ Pass |
| E2E auth + security headers | tests/e2e/auth.spec.ts |
10 | Requires server |
| Total | 68 | 58/58 unit pass |
- Node.js v20+
- Docker (for PostgreSQL)
- Git
# 1. Start PostgreSQL
docker compose -f docker-compose.db.yml up -d
# 2. Install dependencies
npm install
# 3. Configure environment
cp .env.example .env
# Edit .env — generate secrets with the commands shown in the file
# 4. Apply database migrations
npx prisma migrate deploy
# 5. Seed initial data (idempotent — safe to re-run)
npx prisma db seed
# 6. Start dev server
npm run devOpen http://localhost:3000.
.\run_qms.batKills processes locking the Prisma DLL, clears stale .next cache, syncs DB schema, and starts the dev server. Requires Docker running first.
| Role | Password | Scope | |
|---|---|---|---|
| Super Admin | admin@qms.com |
password123 |
Global |
| CEO / Approver | ceo@qms.com |
password123 |
Executive |
| Quality Approver | approver@qms.com |
approver123 |
Compliance & QA |
| Department Editor | editor@qms.com |
editor123 |
Operations |
| General Viewer | viewer@qms.com |
viewer123 |
Retail Banking |
Change all passwords immediately after first login in any non-development environment.
Copy .env.example to .env. The app validates all required variables at startup and refuses to boot if any are missing or use placeholder values.
| Variable | Required | Purpose | Generation |
|---|---|---|---|
DATABASE_URL |
✅ | PostgreSQL connection string | — |
PGBOUNCER |
No | Set true when routing through PgBouncer (port 6432) |
— |
NEXTAUTH_SECRET |
✅ | JWT encryption salt — min 32 chars | openssl rand -base64 48 |
NEXTAUTH_URL |
✅ | Canonical app URL | — |
JOB_RUNNER_SECRET |
✅ | Secures scheduled job routes | openssl rand -hex 32 |
CBS_ENCRYPTION_KEY |
AES-256-GCM key — must decode to exactly 32 bytes | openssl rand -base64 32 |
|
PKI_ENCRYPTION_KEY |
AES-256-GCM key for PKI private key encryption | openssl rand -base64 32 |
|
UPSTASH_REDIS_REST_URL |
Distributed rate limiting | Upstash dashboard | |
UPSTASH_REDIS_REST_TOKEN |
Rate-limiting auth token | Upstash dashboard | |
REDIS_URL |
BullMQ queues + Redis cache | redis://localhost:6379 |
|
REDIS_PASSWORD |
No | Redis AUTH password (production) | — |
LOG_LEVEL |
No | Pino log level: debug/info/warn/error |
Default: info |
S3_ENDPOINT |
Object storage endpoint | https://<id>.r2.cloudflarestorage.com |
|
S3_BUCKET_NAME |
Bucket name | e.g., qmsentry-files |
|
S3_REGION |
AWS-compatible region | auto for Cloudflare R2 |
|
S3_ACCESS_KEY_ID |
S3 access key | R2 / S3 console | |
S3_SECRET_ACCESS_KEY |
S3 secret key | R2 / S3 console | |
DB_USER |
Least-privilege app DB role | qmsentry_app |
|
DB_PASSWORD |
Password for DB_USER |
Strong random string |
✅ = required at all times.
⚠️ = required in production; falls back to safe defaults in dev.
# Build and start all 13-layer production services
docker compose -f docker-compose.prod.yml up -d --build
# Run database migrations
docker compose -f docker-compose.prod.yml exec app npx prisma migrate deploy
# Seed initial data
docker compose -f docker-compose.prod.yml exec app npx prisma db seed
# Apply AuditLog INSERT-only constraint (run once, as DBA)
docker compose -f docker-compose.prod.yml exec postgres \
psql -U postgres -d qmsentry -f /docker-entrypoint-initdb.d/db-init.sql
# View application logs
docker compose -f docker-compose.prod.yml logs -f app-- Run as superuser after prisma migrate deploy
REVOKE UPDATE, DELETE ON "AuditLog" FROM qmsentry_app;
GRANT INSERT, SELECT ON "AuditLog" TO qmsentry_app;# Manual backup (automatic daily via crond container at 02:00 UTC)
docker compose -f docker-compose.prod.yml exec backup /bin/sh /backup.sh
# Restore from a specific backup file
./scripts/restore.sh qmsentry_2026-06-07.sql.gz| Phase | Database | Storage | Jobs | Notes |
|---|---|---|---|---|
| Dev | PostgreSQL (Docker) | Local uploads/ |
HTTP poll | Default Docker Compose setup |
| Production | PostgreSQL + PgBouncer | Cloudflare R2 or AWS S3 | Redis + BullMQ | Up to ~1,000 active users |
| Enterprise | PostgreSQL (AuditLog partitioned by year) | S3 | BullMQ + Meilisearch | 1M+ audit rows, full-text search |
At a 5-branch banking operation with ~200 quality users, expect ~500k audit entries/year and burst peaks of ~10 DB writes/second.
A full security audit was completed covering authentication, session management, input validation, file uploads, API security, and cryptographic integrity. 19 findings were identified and all fixed.
| Severity | Findings | Fixed |
|---|---|---|
| CRITICAL | 2 | ✅ 2 |
| HIGH | 7 | ✅ 7 |
| MEDIUM | 6 | ✅ 6 |
| LOW | 4 | ✅ 4 |
Key fixes:
/api/ai/chatwas completely unauthenticated — fixed with session guard + rate limit.env.examplecontained real cryptographic secrets — replaced withREPLACE_ME_*placeholders- Session cookies lacked
httpOnly,sameSite,secureflags — enforced - Session had no expiry (NextAuth default: 30 days) — set to 8h / 4h for privileged roles
changePasswordSchemaonly required 8 chars — raised to 12 with full complexity- Filenames stored without sanitization — path traversal sequences now stripped
X-Frame-OptionswasSAMEORIGIN— changed toDENY- No startup validation of secrets — app now refuses to boot with missing/short/placeholder values
__Secure-cookie prefix rejected over HTTP in dev — made environment-conditionalpino/thread-streambundled by webpack in Next.js 14 — moved toserverComponentsExternalPackages
- Zero raw SQL in
src/— all queries use fully typed Prisma calls. - Zero
anycasts — TypeScript strict mode enforced across the entiresrc/tree. npx tsc --noEmit— zero errors across the entire codebase.- Idempotent seed —
prisma/seed.tsusesupsertfor reference data; safe to re-run against a live database. - Structured logging — Pino with field redaction for
password,token,secret,cookie,authorization.
EPERM: operation not permitted during prisma generate
The dev server holds a lock on query_engine-windows.dll.node. Kill all Node processes first:
taskkill /F /IM node.exe /T
npx prisma generaterun_qms.bat does this automatically.
unable to get local issuer certificate on corporate networks
$env:NODE_TLS_REJECT_UNAUTHORIZED = "0"run_qms.bat applies this automatically for local dev.
App refuses to start with "FATAL" error
The startup validator (src/lib/core/startup.ts) rejected a required environment variable. Check that:
NEXTAUTH_SECRETis at least 32 characters and is not aREPLACE_ME_*placeholderDATABASE_URLis set and points to a running PostgreSQL instanceJOB_RUNNER_SECRETis set and not the placeholderCBS_ENCRYPTION_KEY, if set, decodes to exactly 32 bytes (openssl rand -base64 32)
Can't reach database server at localhost:5432
PostgreSQL is not running. Start the Docker Compose stack:
docker compose -f docker-compose.db.yml up -dLogin always fails / all pages show Unauthorized
Verify the __Secure- cookie issue is not present. In .env, confirm NODE_ENV is not set to production during local development. The session cookie name is environment-conditional: next-auth.session-token in dev, __Secure-next-auth.session-token in prod.
© 2026 QMSentry Platform — Engineered for Banking. Refined for Compliance. Built for Execution.