compliance-ready-backend-kit
A NestJS and Postgres backend where every control maps to a named HIPAA, PCI-DSS, or SOC 2 clause.
This is a NestJS and PostgreSQL backend baseline built so that a security reviewer can trace each capability back to a named compliance control instead of taking your word for it. It is a reference implementation: database-per-tenant isolation, role-based access control, nested-JWT access tokens, and a control-plane split, all wired together and all documented against specific clauses of HIPAA, PCI-DSS v4.0.1, and SOC 2.
It is for teams who have to answer an auditor, a customer security questionnaire, or a due-diligence checklist and want to start from a defensible position rather than retrofitting controls onto a demo. One thing to be clear about up front, and the repo says this louder than I will: the kit provides technical scaffolding that supports controls. It does not make you compliant or certified. Policies, workforce training, risk assessments, and third-party validation are still yours to do.
The problem it solves
Most starter backends optimize for the happy path and treat security as a later sprint. That works until someone asks you to prove it. Then you discover that “we have auth” is not an answer to “which control satisfies 164.312(a)(1),” and that a single shared database with a WHERE tenant_id = ? predicate is one forgotten clause away from a cross-tenant data leak.
This kit takes the opposite stance. Isolation is enforced by the database engine, not by a query you have to remember to write: each tenant gets its own Postgres database. A forgotten predicate returns less data instead of a breach. Access tokens are signed and then encrypted, so a token holder cannot read claims straight out of base64url. And every one of these choices is mapped, in COMPLIANCE.md, to a specific HIPAA, PCI-DSS, and SOC 2 reference, with an honest status next to it.
What you get
The compliance map covers 16 controls: 8 implemented, 3 partial, 3 not implemented, and 2 that live outside application code (TLS termination and network-layer DDoS). The repo states the gaps as plainly as the wins, which is the point.
- Database-per-tenant isolation (HIPAA 164.312(a)(1), PCI Req 7, SOC 2 CC6.1): a separate Postgres database per tenant, so the isolation boundary is the engine itself.
- Nested JWT access tokens: signed with ES256, then encrypted (A256KW + A256GCM), so claims are not readable by whoever holds the token.
- RBAC with a permission catalogue declared once in code and seeded identically into every tenant database.
- Password storage with Argon2id (HIPAA 164.312(d), PCI 8.3.2).
- Rate limiting and login throttling: a Redis-backed sliding window implemented in a Lua script, with stricter budgets on
/api/auth/login,/api/auth/register, andPOST /api/tenants. - Control-plane authorization: tenant creation is gated behind a required bearer API key that has no default.
- Request-level DoS limits and input validation.
- Key rotation via
kid-indexed envelope encryption, where retiring keys still verify for the token TTL.
Stated gaps, so you can plan around them: no append-only audit logging yet, no MFA or passkeys, no encryption at rest, and the key-encrypting key still lives in configuration rather than a KMS or HSM.
Quick look
Bringing it up is a short sequence. You need Node 24, pnpm 9, and Docker.
cp .env.example .env
pnpm install
pnpm infra:up # Postgres + Redis
pnpm db:migrate
pnpm build
pnpm start:auth # listens on :3011
There is no “first to register wins admin” race. The first administrator is provisioned explicitly through a seed command:
SEED_ADMIN_PASSWORD='...' pnpm db:seed:admin --tenant acme --email admin@acme.example
How it works
Every authenticated request runs through a three-guard chain. TenantGuard resolves which database the request touches. AccessTokenGuard decrypts the token and verifies the tenant ID inside it matches. PermissionsGuard then checks the caller’s role-based permissions. Tenant provisioning is a separate, privileged path: POST /api/tenants requires the control-plane API key and is not something a normal user token can reach.
There are two Prisma schemas by design. The master schema is migrated normally. The tenant schema is rendered to SQL and applied at provisioning time, and that tenant DDL is committed to the repo on purpose, so a reviewer can read exactly what every new tenant database contains. The same intentional legibility shows up in the permission model: a permission checked but never seeded becomes a permanent 403, and one seeded but never checked is enforced by nothing, which is precisely the kind of thing an access review is supposed to surface.
The compliance mapping is candid about the tradeoffs too. Token encryption hides the permission set from a token holder, but it also removes visibility for your own operators and assessors, a real cost to debugging and evidence gathering. And PCI 12.3.3 is called out as an obligation the crypto choices create, not one they satisfy. That kind of note is the difference between scaffolding you can defend and a feature list you cannot.
For the full control-by-control table, the guard internals, and the complete list of known gaps, read the repo and its COMPLIANCE.md.