Legal
Security Overview
How we build, run, and harden Drug Catalog. Real architecture, real controls, real incident response — written for engineers reviewing us, not for marketing.
Effective date: 3 June 2026
1. Architecture at a glance
Drug Catalog is a small, focused stack with a deliberately narrow trust boundary:
client
│ HTTPS (TLS 1.2+, HSTS preload)
▼
┌──────────────────────────────┐
│ Cloudflare Worker (edge) │ auth, quota, rate-limit, logging
│ stateless · global PoPs │
└──────────────┬───────────────┘
│ mTLS to control plane
▼
┌──────────────────────────────┐
│ Supabase Postgres │ drugs · interactions · changes
│ Switzerland — eu-central-2 │ tenants · api_keys · api_usage
│ Zurich │ webhook_subscriptions
└──────────────┬───────────────┘ phi_audit_log · session_tokens
│
▼
┌──────────────────────────────┐
│ Watchtower workers │ ingest from 44 public sources
│ staging-first → quarantine │ raw artefacts in object storage
│ → promote → changes feed │ 12-month retention
└──────────────────────────────┘
PHI Gateway (sibling product, separate DPA)
▲
│ bucketed context only — never names / DOB / MRN
│
/v1/prescriptions/validateAll customer-facing data lives in a single Postgres instance in Switzerland (eu-central-2 Zurich). Cloudflare Workers terminate TLS at the edge, validate the API key, and forward authenticated requests to the database. Worker instances are stateless and carry no customer data between requests.
2. Transport security
- TLS 1.2 minimum, TLS 1.3 preferred, with modern cipher suites; legacy ciphers and SSLv3 are disabled at the edge.
- HSTS with
max-age=31536000; includeSubDomains; preload, submitted to the Chromium HSTS preload list. - Certificate management via Cloudflare for the public surface; automatic renewal; CAA records pinned.
- Inter-service traffic from the Worker to Supabase is encrypted in transit and additionally mutually authenticated via control-plane credentials.
3. API key handling
API keys are tenant-scoped. When you create one, the plaintext value is shown to you exactly once and is never persisted by us; we store only a SHA-256 hash in the api_keys table. On each request, the Worker hashes the incoming bearer token and looks up the hash; the plaintext is never written to logs, never sent to subprocessors, never echoed back in API responses.
The Worker keeps a small in-memory cache of validated key hashes with a thirty-second TTL to amortise database round-trips. Revocations propagate within one cache cycle. A revoked key cannot be reactivated; rotating issues a new plaintext value.
4. BYOL credentials for commercial upstreams
Where a customer holds a licence with a commercial upstream (HCI Compendium / Documedis / hospINDEX, Vidal, Rote Liste, BNF, USP-NF, KNMP G-Standaard, Austria-Codex, or the IFA PZN registry) and wants tenant-scoped passthrough, those credentials are supplied via the dashboard and stored encrypted with pgsodium authenticated encryption with associated data (AEAD) against a per-provider key, as defined in supabase/migrations/20260602000034_byol_credentials.sql. Plaintext credentials exist only for the lifetime of the upstream request inside the Worker. Per-provider keys are rotated on a documented schedule and on compromise indicators.
5. Session security for the dashboard
- Authentication is by email magic-link. Magic links are single-use and expire after fifteen minutes.
- The resulting session cookie is HMAC-signed using a server-side secret, identified by row in
session_tokens, and carries no payload other than an opaque token id. - The cookie is set
HttpOnly,Secure, andSameSite=Lax, with a maximum age of thirty days. - Active sessions are listed in the dashboard and can be revoked individually or in bulk.
6. PHI handling and the validation endpoint
The /v1/prescriptions/validate endpoint is the only surface that accepts clinical context, and even then in a deliberately non-identifying shape:
- Bucketed only. Age bands (for example,
65-74), eGFR bands, a pregnancy flag, and condition codes (ICD-10 / SNOMED CT). No names, no dates of birth, no medical record numbers, no addresses, no free-text notes. - Forwarded, not retained. Bucketed context is forwarded to the PHI Gateway sibling product, which holds its own DPA. Drug Catalog retains only counters and elapsed-time values in the
phi_audit_logtable — never the request body, never the response body, never the warning text generated. - Rejected if mishandled. The endpoint refuses payloads whose shape suggests direct identifiers (extra fields, free-text strings exceeding a bounded length, regular-expression matches against personal data patterns).
- Default-off. The endpoint is opt-in per tenant and disabled on the Developer (free) tier by default.
7. The watchtower — secure ingestion
The ingestion pipeline (the “watchtower”) is staging-first and audit-first. For each of the 44 public sources it operates:
- Raw artefact archive in object storage at
sources/raw/{source}/{run_id}.{format}, retained for twelve months; - Typed staging tables that mirror the production schema with an added
run_idcolumn; - Per-source schema validators — XSD for AIPS XML, JSON Schema for EPha and openFDA, column-name and regex checks for BAG-SL, oddb, WHO ATC, dm+d, BDPM, and CIMA — with quarantine tables for failing rows;
- Promotion guard: a quarantine rate above one percent blocks promotion and trips a
schema_driftalert to PagerDuty and Slack; production keeps the last-good snapshot and every API response touching the affected source carries_meta.schema_state = “stale”; - Per-run audit in
ingestion_runs(kept indefinitely), with item counts, quarantine counts, schema state, and any promotion block reason; - Cross-source diff feed in the
changestable, exposed publicly viaGET /v1/changes?since=…and via webhook; - Best-effort 24-hour SLA to adapt to upstream schema drift.
8. Audit logging
- Every API call is recorded with key identifier, endpoint, HTTP status, and response time in milliseconds. No catalogue values are logged.
- Administrative actions on tenants and keys (create, revoke, rotate, plan change, owner-email change) are recorded in a tamper-evident administrative log kept indefinitely.
- Logs are accessible to a tightly-scoped operations role; access is itself audited.
9. Vulnerability management
- Dependency scanning runs on every push and on a scheduled weekly job. We track CVE advisories for our direct and transitive dependencies.
- CVE response window: seven (7) days to patch criticals, thirty (30) days for high-severity, ninety (90) days otherwise.
- Static analysis runs in CI; secret-scanning rejects any push that contains credentials.
- Production deploys go through code review and automated integration tests.
10. Incident response
The incident-response runbook covers detection, containment, eradication, recovery, and post-incident review. Reports of suspected incidents can be sent at any time to incidents@drug-database.com.
- On-call engineers are paged via PagerDuty for severity-1 and severity-2 incidents.
- Customer notification of a personal-data breach occurs within seventy-two (72) hours of confirmed awareness, in line with the DPA and Article 33 GDPR.
- Post-incident reviews are written up and shared with affected customers in summary form.
11. Compliance roadmap
- GDPR (EU 2016/679) and Swiss nFADP — in scope today; see the Privacy Policy and DPA.
- SOC 2 Type 1 — Q1 of the current fiscal year, audit in progress with a Big Four-equivalent firm.
- SOC 2 Type 2 — observation period running into Q4 of the current fiscal year, report expected shortly thereafter.
- ISO 27001 — under evaluation for the year after SOC 2 Type 2, primarily for European enterprise customers.
- The Service is not, and is not represented to be, a medical device under Swiss Medical Devices Ordinance, EU MDR, or US FDA classifications. See the Terms of Service Section 9.
12. Penetration testing
We commission an independent penetration test annually, covering the API surface, the dashboard, the MCP server, and the watchtower control plane. Executive summaries are available to enterprise customers under NDA. Findings are triaged using the CVE response window in Section 9.
13. Responsible disclosure and bug bounty
We welcome responsible-disclosure reports from security researchers. Send the technical detail of any suspected vulnerability to security@drug-database.com. We will acknowledge receipt within two (2) business days and provide a triage update within five (5).
- Test only against accounts you own; do not access other tenants’ data.
- Do not run denial-of-service tests, social-engineering, or physical attacks.
- Give us a reasonable window to remediate before any public disclosure.
A monetary bug bounty programme is in private invitation and will be opened up to the public after the SOC 2 Type 2 report is issued.
14. Contact
Security disclosures: security@drug-database.com.
Incidents: incidents@drug-database.com.
Privacy: privacy@drug-database.com.