QMS Nordic — Security Overview
A buyer-friendly summary of how QMS Nordic protects your data. Hand this to your InfoSec team during a vendor security assessment.
Draft — review before reliance. The architecture below describes the platform as built. The operational commitments — on-call rotation, breach notification windows, the certification roadmap and its dates — are as first drafted in April and have not been re-confirmed by the operator.
Document ID: QMSN-LEGAL-SEC-001 Version: 2.0-draft Last updated: 2026-09-30 Issued by: Aitech International ApS, Nydamsvej 43, 8362 Hørning, Denmark, CVR 46545354 Companion documents: DPA, BAA, Sub-processors
TL;DR
| Question | Answer |
|---|---|
| Where is my data? | Default: EU (Frankfurt, eu-central-1). Alternate regions on request. |
| Is data encrypted at rest? | Yes — AES-256 by Neon (database) and underlying object storage. |
| Is data encrypted in transit? | Yes — TLS 1.2+ enforced; HSTS; HTTPS-only. |
| Is data isolated between tenants? | Yes — Postgres Row-Level Security with FORCE ROW LEVEL SECURITY + per-transaction app.tenant_id GUC. Cross-tenant access is structurally impossible at the DB layer. |
| Is the audit log tamper-evident? | Yes — hash-chained per tenant. Database triggers refuse UPDATE and DELETE on the audit log for the application's database role. Altering or removing an entry breaks the chain, which the chain-verification script detects. |
| Are e-signatures Part 11 compliant? | Partly, and we grade it that way ourselves. Approving a controlled document requires a passkey (WebAuthn, with user verification) at the moment of signing, and records the signer, the date and time, the meaning of the signature (§11.50) and the version signed. Some other signed acts still record a typed signature reference instead of a passkey; the product says so on those screens. You validate the system for your intended use. |
| Do you have SOC 2 / ISO 27001? | SOC 2 Type I in scoping; Type II planned. For our sub-processors, the position is recorded per vendor in the sub-processor list rather than summarised here — it differs between them, and in some cases depends on the plan we hold. |
| Will you sign a DPA / BAA? | The DPA, yes — draft here. A BAA on request: HIPAA-eligible infrastructure is provisioned for the engagement before one is signed, so it is arranged rather than assumed. |
| Do you train AI on my data? | No — training-on-inputs is disabled in our enterprise agreements with Anthropic and OpenAI. |
| Do you allow penetration testing of my tenant? | Yes — coordinate with info@qmsnordic.com at least 14 days in advance. |
Data protection
Encryption at rest
All Customer Data is encrypted at rest with AES-256 by the underlying database (Neon Postgres) and object storage providers. Key management is delegated to the cloud provider; rotation is automatic per the provider's KMS schedule.
Encryption in transit
TLS 1.2 or higher is enforced for all connections. HSTS is set with a 1-year max-age and includeSubDomains. HTTP requests are 308-redirected to HTTPS.
Tenant isolation
Postgres Row-Level Security is enabled with FORCE ROW LEVEL SECURITY on every tenant-scoped table (40+ tables). Every database transaction sets a per-transaction GUC app.tenant_id that the RLS policies use to filter rows. The application database role is non-superuser and cannot bypass RLS. Without a tenant id set, the application sees zero rows.
Audit trail
Every state-changing action emits an audit log entry containing the tenant id, actor user id, entity type, entity id, action, before/after payloads, reason, ULID, and a SHA-256 chain hash computed from the prior chain hash + tenant id + payload hash + ULID. A per-tenant advisory lock is taken before each insert to ensure the chain is unbroken under concurrency.
The audit log is append-only:
- Database triggers refuse UPDATE and DELETE on the audit log for the application's database role. The role holds the table privileges; the triggers are what block the change. The database owner role, used for migrations and maintenance, is exempt from them by design
- No application code path issues UPDATE or DELETE against this table
- A chain-verifier utility re-computes the chain end-to-end and flags any inconsistency
Electronic signatures (21 CFR Part 11)
Approvals, document promotions, and other signed actions require an electronic signature. The signature workflow:
- The user is prompted with the entity hash and a free-text reason field
- For approving and withdrawing a document and releasing a batch, the signer confirms with their passkey (WebAuthn, with user verification), and the assertion is bound to the record being signed. Some other signed acts still take a typed confirmation instead of a passkey
- The signature manifestation captures: signer name, role, ISO timestamp, reason, entity id, entity version, and the SHA-256 hash of the entity payload
- The manifestation is rendered into the audit-ready PDF export
Signatures are non-transferable: the manifestation hash binds the signature to the specific entity id and version.
Identity and access
Sign-in
- Email and password, or single sign-on (OIDC) through Google Workspace, Microsoft Entra ID or Okta where an organisation has set it up
- Passwords are stored as bcrypt hashes with cost ≥ 10. Each account holder can change theirs under Account → Security, which asks for the current password and emails them that it changed
- Each account holder can add an authenticator app as a second step at sign-in: after the password, a six-digit code (RFC 6238) from the app. Its secret is stored encrypted under a key of its own, and ten one-time recovery codes, stored only as hashes, cover a lost phone. A passkey can be the second step instead, or as well: after the password, the device asks for the owner's fingerprint, face or PIN. An organisation's administrator can require a second step of its members: a member who signs in with a password and has none sets one up at their next sign-in, before reaching anything else. Members who sign in through the organisation's identity provider rely on its own
- Signing requires a passkey (WebAuthn). Adding a passkey asks for the account's password and emails the owner, and each account holder can see, rename and remove their passkeys under Account → Security
- Each account holder can see where they are signed in and sign any device out. A session ends after 12 hours without activity; changing the password signs out every other device, and resetting it signs out all of them
- Failed sign-in attempts are audit-logged, and password sign-in is limited per account and per network address. A password reset is never limited by it, and clears the account's count
- A password-less demo sign-in exists for development and is disabled whenever the application runs in production mode
Authorisation
- Role-based access control enforced server-side
- Roles: Platform administrator, QA administrator, Regulatory affairs (RA), R&D, Manufacturing, Clinical, Auditor
- Client-side gating is a UX courtesy; every action re-validates authorisation server-side
- Platform-operator access (
/backend) is a separate auth realm with no shared credentials
Public API
- Bearer-token authentication
- Tokens stored as bcrypt hashes; the plaintext token is shown to the user once at creation
- Tokens are tenant-scoped; they cannot read data from another tenant
- Read-only — no POST/PUT/PATCH/DELETE paths on
/api/v1 - Per-token rate limit (60 requests / minute by default)
- Revocation is immediate (within 60-second cache window)
Application security
Secure development lifecycle
- Code review required for every PR
- Type-checking, lint, unit tests, automated IQ + OQ subset on every PR
- Dependency vulnerability scanning (
npm audit, Dependabot) - Secret scanning enabled at the SCM layer
- Third-party penetration test: not yet performed. This page will say when one has been, and what it covered
HTTP hardening
- HSTS, CSP, X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy strict-origin-when-cross-origin
- Cookies: Secure + HttpOnly + SameSite=Lax
- Stripe webhooks: signature-verified before processing
Input handling
- ORM-mediated queries (Prisma) — no string-concatenated SQL
- Strict input validation on every server action via Zod schemas
- HTML sanitisation on rendered Customer Data via DOMPurify
AI safeguards
- Provenance captured on every AI generation (model, prompt hash, retrieved chunk ids, output hash, latency, cost)
- AI output is labelled as a draft and cannot promote itself to effective
- The helpdesk AI drafts replies but never sends one: in every mode, a reply reaches a customer only when a person sends it. "Autopilot" means drafting eagerly; a draft above the tenant's confidence threshold is held for approval, and the hold is recorded in the audit trail
- Training-on-inputs is disabled with Anthropic and OpenAI
Operations
Hosting
- Application runtime: Vercel. The application's server functions run in one region, Frankfurt (
fra1); static assets are served from Vercel's global edge network. There is no second region to fail over to - Database: Neon Postgres on AWS eu-central-1 (Frankfurt), confirmed in the provider console on 2026-09-09 — not a default we assume, the region the production project actually runs in
- Provider certifications: recorded per vendor in the sub-processor list, and only where they have been confirmed against the provider's own register entry or attestation. They are not uniform across our vendors, and they can depend on the plan held with each one, so this page does not state a single position for all of them
Backups and recovery
- Continuous backup with point-in-time recovery. Within the retention window below, the recovery point is under a minute
- Retention window: 7 days, read from the provider console on 2026-09-09. Stated as the figure it is rather than as a minimum: beyond seven days there is no restore point, so a problem introduced more than a week before anyone notices it cannot be rewound
- Cross-AZ redundancy at the database layer
- Annual disaster-recovery exercise (restore from PITR into a recovery instance, verify hash-chain integrity, signed by SRE Lead + QA Lead)
- Recovery Time Objective: ≤ 4 hours for primary-region failure
Monitoring
- Application errors: Sentry (with PII scrubbing)
- Uptime: external synthetic monitor + status page at qmsnordic.com/status
- Anomaly alerts to the on-call engineer
Incident response
- 24×7 on-call rotation for P0 / P1 incidents
- Documented runbook in
docs/runbooks/ - Personal Data Breach: customer notification within 24 hours of awareness per the DPA
- HIPAA Breach: customer notification within 60 days per the BAA
Compliance posture
| Framework / Regulation | Status |
|---|---|
| EU GDPR | DPA available; EU SCCs + UK Addendum incorporated |
| HIPAA | BAA available; safeguards per 45 CFR §§164.308, .310, .312 |
| 21 CFR Part 11 | Partial — hash-chained audit trail and passkey signatures on document approval; some signed acts still use a typed reference (see above) |
| EU MDR / IVDR | The platform is not a regulated device; the platform supports Customer's MDR/IVDR compliance |
| ISO 13485 | Aitech runs its own quality system in QMS Nordic; not yet ISO 13485 certified |
| ISO 27001 | Pursuing in 2026 |
| SOC 2 Type I | Scoping in progress |
| SOC 2 Type II | Planned for 2026 |
| EU AI Act | Provenance + human-oversight architecture aligned with Articles 12–14, 16 |
| Cyber Essentials Plus (UK) | Considering, dependent on UK customer pull |
Data residency
Every customer's data is stored in the EU, in Frankfurt: the database on Neon, AWS eu-central-1, and the application's server functions in Vercel's Frankfurt region. It is the same for every customer. There is no US region and no multi-region option; until 2026-09-26 this page listed both, and the admin settings offered a choice between them that changed nothing.
Some features send data to service providers in the United States: the AI features and the assistant (Anthropic), document search and voice conversation (OpenAI), spoken replies (ElevenLabs) and email (Resend). Each is listed, with what it receives and the transfer safeguard relied on, in the sub-processor list. The helpdesk's AI can be switched off per organisation; the other AI features cannot yet be switched off per organisation.
Vulnerability disclosure
If you believe you have found a vulnerability, email info@qmsnordic.com with details. We acknowledge within 24 hours, triage within 72 hours, and provide a remediation timeline within 7 days. We do not currently operate a paid bug-bounty program but offer responsible-disclosure recognition on https://qmsnordic.com/legal/security.
Customer-side responsibilities
This security model assumes Customer:
- Provisions Authorised Users with appropriate roles
- Disables accounts for terminated employees promptly
- Configures the identity provider with MFA and conditional access per Customer's own policy
- Reviews the audit log periodically
- Performs the Performance Qualification (PQ) on its tenant per the Validation Pack
- Notifies Aitech of any suspected security event affecting Customer's tenant
Document version
| Version | Date | Change |
|---|---|---|
| 1.0-draft | 2026-04-30 | Initial overview, under the platform's former name |
| 2.0-draft | 2026-09-05 | Re-issued under QMS Nordic / Aitech International ApS; status-page and disclosure URLs corrected; "Enterprise election" replaced (no such tier). Architecture and commitments otherwise unchanged and still unreviewed. |
| 2.0-draft | 2026-09-30 | Four statements corrected to match the product: Part 11 graded partial, not compliant; no penetration test performed yet; audit-log protection is by trigger, not by missing privileges; the helpdesk AI never sends a reply on its own. |
