π Download security one-pager (PDF) π Download HIPAA BAA template (PDF)
The BAA is the US HIPAA Business Associate Agreement and is separate from our GDPR / nFADP DPA. See the current vendor list at /legal/subprocessors.
1. Security at a glance
- Stateless mapping engine. The mapping cascade itself holds no state between requests. Account metadata, audit logs, and clamped upload samples are persisted in Supabase (EU region, Ireland) as described in the DPA; upload payloads carry a hard 24-hour TTL and are swept on expiry or on commit.
- HMAC-signed API keys. Keys are self-contained, scoped credentials signed with
MAPR_KEY_SECRET. There is no shared session store; revocation is enforced by a server-side deny-list checked on every request. - Schema-only by default. Only column headers and up to three sample row values per column (truncated to 80 characters) leave the customer. Full-data mode routes through phi-cloud under a separate BAA.
- 24-hour upload TTL. File caches expire automatically; there is no manual flush required and no opaque background retention.
- BYO-LLM-key support. Customers can supply their own provider keys, in which case the call is made under their provider account and the LLM cost is removed from the per-call price.
2. Encryption
- In transit: TLS 1.3 with modern ciphers (AEAD-only); legacy TLS 1.0/1.1 disabled at the edge; HSTS with preload directive.
- At rest: AES-256 (GCM) for any persisted state β account database, audit logs, encrypted BYO-LLM credentials.
- Key management: envelope encryption with KMS-managed root keys. BYO-LLM credentials are encrypted per-workspace with a unique data encryption key, wrapped by the KMS root.
- Secrets:
MAPR_KEY_SECRET,MAPR_LLM_API_KEY,CHAINLOG_API_KEYand related secrets are injected at deploy time from Cloudflare Workers encrypted secrets; they are never committed to source control and never logged.
3. Access control
- RBAC in the dashboard. Roles: Owner, Admin, Developer, Viewer. Role assignments are workspace-scoped and audit-logged.
- Scoped API keys. Each key encodes its allowed mode (schema-only vs full-data), allowed endpoint family, and optional rate-limit override.
- IP restrictions. Pro and Enterprise plans can bind keys to CIDR allow-lists enforced at the edge.
- SSO. Enterprise plans support SAML 2.0 and OIDC single sign-on against the customer's IdP, with SCIM provisioning on request.
- MFA. TOTP-based MFA is supported for all plans and enforced for Owner and Admin roles on Pro and Enterprise.
- Audit logging. Authentication events, authorisation decisions, configuration changes, and mapping commits are written to an append-only audit log retained for seven years (2,557 days) for live workspaces, comfortably above the HIPAA six-year minimum (45 CFR Β§ 164.530(j)(2)). Orphaned rows (workspace deleted) are retained for two years (730 days) for operator-side review and then purged by the daily retention sweep described in Β§8.4.
4. Network security
- Edge. Cloudflare DNS and proxy in front of every public hostname, with always-on DDoS protection and a managed ruleset for OWASP Top 10 categories.
- Application edge. Cloudflare Workers (via OpenNext) with EU PoPs preferred; static assets served from the Cloudflare CDN.
- Security headers. CSP with default-src
'none', HSTS with preload, COEP / COOP / CORP configured for the dashboard and Workbench, X-Content-Type-Optionsnosniff, Referrer-Policystrict-origin-when-cross-origin. - CORS. The REST API rejects browser-originated cross-origin requests; the embeddable Workbench whitelists customer-configured origins only.
5. Application security
- Input validation. Every API route validates input with strict schemas before any downstream call; field-level validators (regex, iban, gtin, loinc, icd10, npi) gate mapping outputs.
- Schema-only enforcement. Sample rows are clamped at the HTTP edge by
clampForSchemaOnly()(β€3 rows Γ β€80 chars/cell) and the same clamp is re-applied defensively at the LLM call boundary insidelib/matcher.ts. There is no code path that lets full-row content into a schema-only workspace; persisted upload metadata in Supabase storesparsed_sampleonly and keepsparsed_rowsnull for schema-only uploads, with a 15-minute sweep clearing any expired row payloads. - Rate limiting. Per-key sliding-window rate limiter enforced before any billable work begins; abusive patterns trigger automatic backoff and alerting.
- Dependency scanning. Daily automated scans of npm dependencies (Snyk and GitHub Dependabot); critical vulnerabilities patched within 48 hours, high within 7 days.
- Penetration testing. Annual third-party penetration test of the public API, dashboard, and Workbench. Executive summaries are available to Pro and Enterprise customers under NDA.
- SAST. Static analysis runs on every pull request; merges to
mainrequire a clean run plus a human reviewer.
6. Infrastructure
- Compute & edge. Cloudflare Workers (global deployment via OpenNext) with EU PoPs preferred. DNS, DDoS protection, and managed WAF rulesets are also Cloudflare.
- Storage. Cloudflare KV for rate-limit counters and statistics cache; Cloudflare R2 for asset storage.
- Account database. Supabase EU instance (Ireland) with point-in-time recovery enabled. Stores authentication, sessions, rate-limit ledger, audit log, and upload metadata (
parsed_sampleclamped to β€3 rows Γ β€80 chars/cell;parsed_rowsnull in schema-only mode; 15-min retention sweep). - Billing. Stripe Payments Europe Ltd. (Ireland).
- Email. Operator-configured SMTP relay (Cloudflare Workers Email by default; any nodemailer-compatible provider β Resend, Postmark, AWS SES β is supported). SPF, DKIM, and DMARC enforced on the sending domain.
All hosting providers above hold SOC 2 Type II reports; copies are available on request from each provider under NDA.
7. Compliance
- SOC 2 Type II β observation window in progress; first attestation report expected before the end of the current fiscal year.
- GDPR-aligned. Standard Contractual Clauses incorporated by reference in our DPA; subprocessor list maintained publicly.
- nFADP-aligned. The Swiss Federal Act on Data Protection 2020 is treated as equivalent to GDPR for the purposes of customer obligations.
- HIPAA-eligible via phi-cloud. Customers requiring HIPAA coverage route full-data calls through phi-cloud. AdaptivMapr offers its own BAA covering its role as a business associate; phi-cloud's BAA covers the downstream in-region LLM processing.
- CCPA / CPRA. We do not sell or share personal data within the meaning of California law.
8. PHI / HIPAA handling
AdaptivMapr is offered as a HIPAA-eligible Business Associate; a signed BAA is available on request from compliance@adaptivmapr.com. We do not claim SOC 2 Type II, HITRUST, ISO 27001, or any βHIPAA certifiedβ status β no such certification exists in U.S. law. The controls below describe how Protected Health Information (PHI) is handled in the cascade.
8.1 Schema-only mode is the safe path
In schema-only mode (the default), only column headers and up to three sample row values per column, each truncated to 80 characters, leave the customer's tenant. The clamp is enforced at the HTTP edge by clampForSchemaOnly() in lib/parser.ts and re-applied defensively at the LLM call boundary in lib/matcher.ts. No row-level PHI can leave the customer under schema-only mode. Covered entities that do not wish to transmit any PHI to a Business Associate should operate exclusively in schema-only mode.
8.2 Full-data mode routes through phi-cloud
When the customer's key is entitled to full-data mode and a request explicitly elects it, the layer-5 LLM call is delegated to phi-cloud (listed as a subprocessor at /legal/dpa#subprocessors). phi-cloud carries the BAA chain with the downstream model providers and pins the model to a PHI-eligible, in-region deployment via X-PHI: true and X-Region headers.
The AdaptivMapr Worker only opens a connection to phi-cloud when the destination host is listed in MAPR_PHI_ALLOWED_HOSTS. If the env var is unset in production, full-data requests fail-closed; if a non-BAA-covered host is configured, the gate rejects the request before any payload leaves the Worker. The behaviour is pinned by lib/__tests__/phi-host-allowlist.test.ts.
8.3 Do not put PHI in column names
AdaptivMapr's cross-workspace statistics rollup is opt-in per workspace. The flag opt_in_global_stats on public.tenants defaults to false and is toggled from /dashboard/settings via the Cross-workspace learning control. The opt-in gate is enforced at the database layer by Postgres Row-Level Security (migration 20260605120000_tenants_opt_in_global_stats.sql); row values are never shared with the rollup β only normalised header names contribute. Customers must therefore not place patient identifiers, names, or other PII tokens inside column header names. Use stable, schema-style identifiers such as patient_id, dob, or last_name.
8.4 Hash-chained audit log
Every /commit, every mapping confirmation, and every connector sync is written to the Chainlog audit ledger via lib/chainlog.ts. When Supabase persistence is enabled the row lands in public.audit_logs and the audit_logs_chain_assign trigger atomically assigns a per-tenant chain_seq, hash, and prev_hash. Writes to audit_logs are blocked at the database level (REVOKE update, delete), so even a compromised service-role key cannot tamper with history. Customers query their own trail via GET /v1/audit β keyset-paginated and filterable by kind, since, and until.
Retention. Live-workspace rows are kept for seven years (2,557 days) from creation, comfortably above the HIPAA minimum of six years (45 CFR Β§ 164.530(j)(2)). Orphaned rows β where the owning workspace has been deleted and tenant_id is therefore NULL per migration 20260605130000_audit_logs_tenant_set_null.sql β are retained for two years (730 days) for operator-side review, after which they are purged. A daily cron sweep at 04:00 UTC (app/api/cron/audit-cleanup/route.ts) executes both horizons with a 10,000-row-per-invocation cap to keep each run inside the Worker CPU envelope; if a horizon's backlog exceeds the cap, the following day's tick continues the sweep. Deletes are idempotent at the SQL level, so a duplicate fire is safe. Retention windows are configurable via MAPR_AUDIT_RETENTION_DAYS and MAPR_AUDIT_ORPHAN_RETENTION_DAYS; the live-row knob is hard-floored at the HIPAA six-year minimum unless MAPR_AUDIT_RETENTION_OVERRIDE_HIPAA_FLOOR=1 is set explicitly (intended for staging/dev only).
8.5 24-hour transient-data retention
Uploaded files and parsed rows live in Cloudflare KV with a hard expirationTtl derived from each entry's expires_at at create time (see storeWrite() in lib/uploads.ts). The Worker never extends an upload's TTL beyond its original 24-hour window: refresh writes recompute the TTL from the original expires_at, not from Date.now(). After 24 hours KV auto-purges the entry; the Supabase mirror is swept on the same horizon by the retention job described in DPA Article 13.
8.6 Right to erasure
Workspace deletion is initiated by the workspace owner from /dashboard/settings (Danger Zone β Delete workspace) and runs end-to-end via DELETE /api/v1/me/workspace. The endpoint is session- only β bearer API keys are deliberately rejected, so an MCP integration holding a long-lived token cannot trigger a cascade. A structured confirmation string of the form delete-my-workspace-<workspace_id> must be echoed in the request body; a misclick or stray payload from a connector cannot torch a workspace.
What is auto-deleted (hard-delete; not soft-delete):
- Every tenant-owned Supabase row that cascades from
public.tenants(id)βtenant_members,api_keys,mapping_statistics, and the row-levelaudit_logspayloads. - Every
uploadsrow matchingworkspace_id(the FK isON DELETE SET NULL, so the route handler issues an explicitDELETE). - Every
connectorsrow matchingworkspace_id(same rationale β explicitDELETE). - Every
rate_limit_countersrow whoseidentifierequals the workspace_id (best-effort).
What persists: the immutable hash-chained Chainlog ledger captures a workspace.deleted entry BEFORE the cascade fires, so erasure itself is auditable. The ledger holds mapping fingerprints only β header text β target field, never row values β so no PHI persists. The chain remains intact for audit integrity, which is the entire point of an append-only ledger.
KV cleanup is best-effort. Cloudflare Workers KV bindings do not expose a prefix-list or prefix-delete primitive. The route handler drives KV cleanup off the Supabase upload-id list (so every known upload key is purged at the moment of deletion) and explicitly clears the small set of keyed entries the codebase writes: workspace_admin_email:<workspace_id> and entitlements:<stripe_customer_id>. Any other workspace-keyed cache entry is auto-purged by its TTL within hours (rate-limit cache: minutes; entitlement cache: 24h; upload payloads: 24h).
Backups: hard-delete applies to live state. AdaptivMapr retains no soft-deleted-but-recoverable copy. The Supabase point-in-time recovery window is the platform's domain (see Β§6) and is governed by the Supabase data-protection addendum referenced from the DPA.
The cascade is pinned by a regression-prevention rail in lib/__tests__/workspace-deletion.test.ts: every workspace-scoped table named in a Supabase migration must either cascade from public.tenants(id) or be explicitly purged by the route handler. A future migration that adds a workspace-scoped table without coverage fails the build.
8.7 Subprocessors
The current subprocessor list β Cloudflare (Workers, KV, R2, edge), Supabase (Postgres, EU), phi-cloud (full-data LLM gateway), Stripe (billing), and the operator-configured SMTP relay β is maintained at /legal/dpa#subprocessors. Each subprocessor that may receive PHI is engaged under a BAA; the BAA chain itself is deploy-side and is confirmed in writing as part of the BAA review with each customer.
8.8 Encryption
TLS 1.3 in transit at the Cloudflare edge (see Β§2). AES-256 at rest for persisted state β Cloudflare KV and Supabase Postgres both apply AES-256 encryption to stored data by default.
8.9 Access controls
HMAC-signed self-contained API keys (signed with MAPR_KEY_SECRET) for machine access; JWT magic-link sessions for the dashboard. Postgres Row-Level Security is enforced on every tenant-scoped table; the RLS posture is pinned by lib/__tests__/rls-snapshot.test.ts, which fails the build if a policy regresses or a tenant table is left unprotected.
8.10 Email scoping
Transactional email β magic-link sign-ins, billing receipts, dunning notices, usage alerts β is addressed exclusively to workspace members (operator staff). Patients are not recipients of AdaptivMapr-originated email. Message bodies contain account and billing context only; patient-identifying content from uploads is not included in any transactional template.
8.11 PHI is not logged
Application logs carry request metadata (route, status, latency, key id, workspace id) and structured error context only. Sample rows and full-data payloads are excluded from log lines by construction. A static check enforcing this discipline in CI is on the roadmap and flagged as deploy-side until it lands as a dedicated test under lib/__tests__/.
8.12 HITL flag is exposed, not enforced
Medium- and high-risk templates β patient_demographics, lab_results, claims_line_items, drug_formulary β return requires_hitl: true and hitl_status: "pending_review" in the PATCH /v1/uploads/{id}/mappings response. The customer's commit workflow can gate on these flags. Native AgentGate HITL-queue integration is on the roadmap for Q3 2026 and is not wired in v1.
9. Vulnerability disclosure
We welcome reports from security researchers. Please email security@adaptivmapr.com with:
- A description of the issue and its potential impact;
- Reproduction steps or a minimal proof of concept;
- Your preferred attribution and contact for follow-up.
We commit to:
- Acknowledging receipt within 2 business days;
- Providing a triage status within 5 business days;
- Coordinating disclosure within a 90-day window under a responsible-disclosure policy;
- Not pursuing legal action against researchers acting in good faith and within the scope of this policy.
Out of scope: denial-of-service attacks, social engineering of AdaptivMapr personnel, physical attacks, and findings on third-party SaaS subprocessors (please disclose to the relevant vendor).
10. Incident response
- RTO target. 4 hours for the public API and dashboard.
- RPO target. 1 hour for the account database; near-zero for stateless API requests (idempotent retries supported).
- Customer notification. We notify affected customers of any confirmed Personal Data Breach within 72 hours, consistent with our DPA Article 11.
- Status page. Active incidents are surfaced at
status.adaptivmapr.com; subscribe to receive email or webhook notifications. - Post-incident reviews. Customer-affecting incidents receive a written post-incident review within 10 business days, shared with affected customers.
11. Subprocessors
Our current list of subprocessors is maintained at /legal/subprocessors (also cross-referenced from /legal/dpa#subprocessors). New subprocessors are announced at least 30 days in advance.
12. Contact
Security disclosures: security@adaptivmapr.com. Data protection requests: dpo@adaptivmapr.com. General contact: hello@adaptivmapr.com.