Data Protection Impact Assessment (DPIA)
Required under GDPR Art. 35 when processing is likely to result in a high risk to the rights and freedoms of natural persons. Mandatory triggers in our case: (a) large-scale processing of special-category health data, (b) systematic monitoring of patient behaviour (adherence telemetry), (c) processing of national identification numbers (CNP, where applicable). One platform-wide DPIA, with targeted appendices for the highest-risk features (F9 telerehab, F10 telemetry, F5.5 video consultations). v1 drafted 2026-05-15. Revised 2026-09-06 against the September 2026 launch, at which the ~20,000-patient legacy population migrates onto this platform — every claim in this document was re-checked against the code rather than against the previous revision, and fourteen did not survive that.
Counsel-review status
Developer-drafted v1. Counsel reviews the necessity-and-proportionality assessment, validates the risk scoring against ANSPDCP guidance, and signs off the residual-risk conclusion. DPO countersigns under DPO designation §"DPO tasks". Where the residual risk remains "high", Art. 36 prior consultation with ANSPDCP applies.
§1 Description of the processing
1.1 The platform in scope
The RestartiX platform delivers telerehabilitation and clinic-operations services to patients on behalf of clinics. {{LEGAL_ENTITY}} operates both:
- The platform infrastructure (Processor role for third-party clinics; operator)
- RestartiX-the-clinic (Controller role for its own patients)
Subsequent third-party clinics onboard as additional Controllers; the platform's role remains Processor for those. No third-party clinic is onboarded yet, so every processing activity described here is currently single-tenant — which is what makes the open items in §3 remediable before they affect anyone but us.
The platform is a registered medical device. RestartiX MedCare v1.0 is CE-marked Class I under MDR (EU) 2017/745 via Rule 13, manufacturer RESTARTIX CENTER S.R.L., SRN RO-MF-000055558, Basic UDI-DI 5943172000RESTARTIXRY. This does not change the GDPR analysis — MDR and GDPR are parallel regimes — but it is context a supervisory authority will expect this document to state, and it constrains what the processing may claim to do: the declared intended purpose states the device has no measuring function and does not interpret physiological data. See medical-device.md.
Read this before the risk register
Two things a reviewer needs up front.
The processing assessed here is the September 2026 launch, at which the ~20,000-patient legacy population — 11,000+ treatment plans, 5,000+ subscriptions — migrates onto this platform. Migration is itself a processing operation and is assessed at R14. "Large scale" under Art. 35(3)(b) is satisfied on day one, not approached.
Every control named here describes the system as it runs, not as it is intended to run. Where a safeguard does not exist, this document says so in place and marks the entry ⚠️ rather than describing the design that was planned.
1.2 Nature of processing
Patient sign-up and identity — self-serve sign-up via the Portal, OAuth or password authentication via Clerk, patient profile (name, email, phone, DOB).
Consent capture — per-purpose consent recorded in a versioned
consentsledger, evidenced with the grant timestamp, the granting principal and the originating IP (granted_via_ip). Withdrawal is the only permitted update; there is no delete. No user-agent is recorded.Guided exercise program delivery — patients enrol in a clinician-prescribed programme of a length the clinician sets; follow video-guided exercises; self-report pain (VAS) and exertion (RPE) per session; record completion.
Adherence telemetry — video watch percentage, session-completion timestamps, exercise drop-off captured continuously during sessions.
Clinical correspondence — treatment-plan adjustments, specialist notes, prescriptions prepared and handed over to a patient.
Live video consultations (F5.5) — real-time audio/video between a patient and a clinician, relayed by Daily.co. Not recorded. This is the platform's only processing of Art. 9 data in the clear by a sub-processor — assessed at R11 and in §6.
Scheduling, forms and documents — appointments and calendars, patient-completed forms and consents, generated clinical documents (F3–F6).
National identification number (CNP) — collected from the patient only, stored AES-256-GCM-encrypted with a blind index for search, and read behind a dedicated permission that writes an audit row. See §2.2.
Audit logging — every state-changing operation, append-only, with actor, action, entity, IP, user-agent, status and field-level changes, and sensitive values masked by the central redaction list.
Reads are not logged.
audit.ActionReadexists but has exactly one call site — the CNP reveal — under an explicit admission rule ("would a supervisory authority ask who accessed this specific value?"). The consequence a reviewer should hold onto: a read-only break-glass session produces no audit rows at all unless it touched a CNP. The break-glass session itself is recorded, opened and closed; what was looked at during it is not. A general access log is designed and unbuilt.Notifications — transactional and operational email via AWS SES, plus in-app notifications. Operational messages honour a per-recipient opt-out. No marketing is sent — no marketing category is registered and the dispatcher refuses the classification outright (see §2.3).
1.3 Scope and context
- Volume: ~20,000 patients, 11,000+ treatment plans and 5,000+ active subscriptions at the September 2026 launch, migrating from the legacy system this platform replaces. This is not a growth projection — it is a known population with existing records, arriving together. "Large scale" under Art. 35(3)(b) is satisfied on day one rather than reached later, which is why this DPIA exists in the first place and why the migration gets its own risk at R14.
- Geography: Romania at launch. EU-wide architecturally; non-EU markets not in scope.
- Demographics: rehabilitation patients spanning the adult population, and minors. A minor is not blocked from the platform and there is no parental-consent flow at sign-up. What exists is an age-of-majority test (18) that decides whether a caregiver may accept the clinic's terms for someone else. GDPR Art. 8's age of sixteen, which governs consent-basis processing, has no gate. See R5 and counsel §3.2.
- Data sensitivity: Art. 9 special-category health data is the core data type, and since F5.5 it includes live consultation media. CNP is processed under Law 190/2018 Art. 4 — see §2.2, where the earlier "TBD pending counsel" is now resolved on the implementation side and still open on the legal side.
- Power asymmetry: patients are the data subjects; the clinic (controller) sets the purposes; the platform processes on instruction. Patients are in a position of dependency on the clinic for care; this informs the proportionality analysis and the strength of consent.
- Technology: standard SaaS — Next.js, Go, PostgreSQL with row-level security, Redis, AWS-hosted in
eu-central-1(single region; nothing is replicated elsewhere). No AI in clinical decision-making; no automated profiling producing legal effects on data subjects, so Art. 22 does not engage.
1.4 Purpose
- Provide guided telerehabilitation as a clinical service, in line with the clinic-as-controller model.
- Capture adherence data so the clinic can identify drop-off, intervene, and improve outcomes.
- Maintain the audit trail required by GDPR Art. 5(2) accountability and the medical-records overlay of Law 95/2006.
1.5 Data categories — see ROPA
The ROPA's Part A (RestartiX-as-controller) and Part B (RestartiX-as-processor) cover the data categories in operational detail. This DPIA treats them as incorporated by reference.
§2 Necessity and proportionality
2.1 Lawful basis (Art. 6 + Art. 9)
| Processing | Art. 6 basis | Art. 9 basis |
|---|---|---|
| Sign-up + account | (b) Contract performance | n/a (no special-category data at this step) |
| Consent capture | (c) Legal obligation (Art. 7 evidence) | n/a |
| Treatment plan delivery | (b) Contract performance | (h) Provision of health and social care + (a) explicit consent captured at sign-up |
| Adherence telemetry | (b) Contract performance + (f) legitimate interest (operational improvement) | (h) + (a) explicit consent |
| Communications (transactional) | (b) | n/a |
| Audit logging | (c) Legal obligation + (f) Legitimate interest | n/a |
| Customer support | (f) Legitimate interest (where requested) | (a) Explicit consent for any health-data subject matter that arises in support |
| CNP processing | (c) Legal obligation | n/a (CNP is not Art. 9 data — it's national-ID under Law 190/2018 Art. 4) |
| Live video consultation | (b) Contract performance | (h) Provision of health and social care + (a) explicit consent (telemedicine instrument, adopted before the consultation can start) |
Each Art. 9(2) basis is documented per consent purpose in the consent purpose catalog (1B.9).
2.2 Necessity assessment
The Art. 5(1)(c) data minimisation test applied to each category:
| Category | Necessary? | Why |
|---|---|---|
| Yes | Account communication channel; no realistic alternative | |
| Name | Yes | Personalisation; clinician's professional norm |
| Phone | Conditional | Required only if SMS becomes a fallback channel; optional today, and no SMS provider is engaged |
| DOB | Yes | Age-of-consent verification; clinical-relevance metadata; required for under-16 routing |
| Locale | Yes | UI language; communication language |
| VAS / RPE / adherence | Yes | The clinical product; the rehab program is meaningless without them |
| CNP | Yes, with restrictions | Necessary for the clinical record and for documents that must carry it under Romanian rules. The minimisation controls are structural, not procedural: it is written only through a patient-audience path — the patient's own profile page, or a national_id answer inside a form session — and stored AES-256-GCM-encrypted with a national_id_hmac blind index so a clinic can search without decryption, masked by default on the patient record, revealed only through a dedicated patients.view_national_id permission on a separate endpoint that writes an audit row on every read — the only read in the platform that is audited at all — and it is refused by database trigger from the two free-form stores (custom_field_values.value and forms.values) so it cannot leak sideways into a form answer. Counsel's binding answer on whether it may be requested at sign-up is still outstanding (§3.1); the implementation makes it optional and patient-controlled, which is the conservative reading |
| Consultation audio/video | Yes | The consultation is the media; there is no lesser form of it. Minimised by not recording — see R11 |
| Free-text patient comments | Yes | Patient-volunteered clinical context |
| IP / user-agent (sign-up, audit) | Yes | Security and accountability; minimised in logs (no IP in business analytics) |
| Video watch metadata | Yes | Adherence proxy; supports drop-off intervention |
⚠️ "Patient-entered" is a property of the API path, not a guarantee about who typed. A staff-audience write is refused outright (national_id_patient_only), and staff impersonating a patient are blocked from writing at all — so no staff-authenticated route can set a CNP. But a form session is public and token-authorised, carries no principal, and is always treated as a patient audience by construction. That is the clinic-tablet flow: staff issue the token and hand over the device. The platform therefore cannot distinguish a patient typing their own CNP from a receptionist typing it for them, and this document should not claim it can. What is guaranteed is narrower and still worth having: no staff member can set a CNP while authenticated as staff, or while impersonating the patient.
2.3 Proportionality assessment
Each category collected is in a one-to-one relationship with a purpose. No bulk collection, no profiling for purposes not disclosed, no cross-tenant analytics on identifiable data. The cross-tenant constraint is enforced architecturally (row-level security) and operationally (break-glass).
⚠️ One consent is collected and cannot be acted on. marketing_email and marketing_sms are seeded as consent-basis purposes, and the sign-up flow renders every consent-basis purpose as an optional toggle — so a patient is asked whether the clinic may send them marketing, and may say yes. No marketing message can be sent: no category is registered as marketing and the dispatcher refuses that classification outright. The permission is therefore inert.
This is not unlawful — no processing occurs on it — but it is a fairness and transparency point under Art. 5(1)(a): asking for a permission that is never exercised leaves the patient with a false picture of what they agreed to, and leaves a granted consent in the ledger that no processing corresponds to. The resolution is either to send the marketing the consent describes, or to stop offering the toggle until that is true.
The migration does not widen this. Bringing 20,000 existing patients across is a change of custody, not of purpose: the receiving clinic is the same controller, the treatment relationship is the same one, and the categories are those already listed. What the migration must not do is import more than the new schema has a disclosed purpose for — a legacy field with no home here is deleted at the boundary rather than parked in a column "in case". That is a design constraint on the migration tooling, and R14 is where it is assessed.
2.4 Data subject rights
| Right | Mechanism | Status |
|---|---|---|
| Information (Art. 13–14) | Privacy notice — clinic-published, platform-templated (1B.10) | Shipped (template machinery); content awaits counsel review |
| Access (Art. 15) | GET /v1/me/export — one JSON archive of the subject's records | Shipped. The subject is resolved from the calling login rather than taken from the request — see R13 |
| Rectification (Art. 16) | Patient profile editor in Portal; staff editing in Clinic app | Shipped |
| Erasure (Art. 17) | Anonymisation respecting Art. 17(3)(c) + Law 95/2006; soft delete throughout, never a hard delete of a patient record | Shipped for the live systems. Cannot reach the backup archive — see R12 |
| Restriction (Art. 18) | Per-purpose consent withdrawal, effective immediately; withdrawal closes any run the purpose was gating | Shipped |
| Portability (Art. 20) | The same JSON archive as Art. 15 | Shipped |
| Objection (Art. 21) | Consent withdrawal for consent-basis purposes; for the legitimate-interest telemetry, an operational routing through the controller | Partially shipped — the withdrawal path is built, the objection-routing process is documented but has no owner while the DPO seat is vacant |
| Automated decision-making (Art. 22) | Does not engage — no automated decision produces legal or similarly significant effects | N/A |
§3 Risk assessment
How to read this register
These are risks, not incidents. Each entry names something that could go wrong with this processing, states how bad it would be if it did, lists the controls that make it unlikely, and rates what is left over. That is what Art. 35(7)(c) asks a DPIA to do: think through the failure modes in advance and show the reasoning.
So an entry like R1 Cross-tenant data leak does not report that one clinic can see another's patients. It records that cross-tenant leakage is the failure that would matter most on a multi-tenant health platform, and sets out what stands in its way — row-level security in the database, the URL-scope guard on every per-org route, cache tags scoped to match data visibility. The residual rating is what remains once those are in place: never zero, because no control is perfect, and a register claiming otherwise would not be credible.
Two entries are different, and say so in their own text. R13 is a defect that was actually found in the running system, and is marked closed with what it leaves behind. Entries marked ⚠️ name a control that is missing rather than merely imperfect. Everywhere else, a control described here was checked and is in place.
Residual "Medium" is not an alarm. It is the normal resting state for a serious threat with working controls against it. What would warrant alarm is a High residual, or an inherent risk with nothing listed against it — neither of which this register currently contains.
For each identified risk: likelihood × severity = inherent risk, then residual risk after controls.
Scale: Low / Medium / High / Critical.
R1 — Cross-tenant data leak
| Aspect | Detail |
|---|---|
| Threat | A bug or operator action exposes one clinic's patient data to another clinic |
| Affected rights | Confidentiality, Art. 5(1)(f) integrity-and-confidentiality |
| Affected data subjects | Patients of the leaked-from clinic |
| Inherent likelihood / severity | Medium / High → High |
| Controls | Row-Level Security at DB level (RLS scoped to app.current_org_id); P47 URL-≡-scope guard on every per-org route; P42 cache-tag scope must match data visibility; integration tests cover RLS at the test level; no cross-tenant query path in code |
| Residual likelihood / severity | Low / High → Medium |
R2 — Platform-personnel access exceeding the processor boundary
| Aspect | Detail |
|---|---|
| Threat | Platform staff browse identifiable patient data without proper authorisation |
| Affected rights | Confidentiality, Art. 28 processor commitment |
| Affected data subjects | Any |
| Inherent likelihood / severity | Medium / High → High |
| Controls | Break-glass primitive — scoped, ≤ 4-hour, justified, audit-logged, real-time-notified to the affected clinic. No static role grants cross-tenant patient-data read. Console architecture forbids "background" reads (see decisions.md → break-glass). ⚠️ The quarterly break-glass log review is not yet running — it is a DPO task and no DPO is designated |
| Residual likelihood / severity | Low / Medium → Low |
R3 — Encryption-key compromise
| Aspect | Detail |
|---|---|
| Threat | KMS key material compromised, exposing column-encrypted CNP and credentials |
| Affected rights | Confidentiality |
| Affected data subjects | Any patient with CNP stored (subject to counsel finding §3.1) |
| Inherent likelihood / severity | Low / High → Medium |
| Controls | AWS KMS with key policies restricting administrative access; annual key rotation; planned customer-managed CMK migration as Phase 2; envelope encryption (column data encrypted by data key encrypted by KMS master key); separate KMS key per environment |
| Residual likelihood / severity | Low / High → Medium (key compromise remains catastrophic but the likelihood is materially reduced) |
R4 — Sub-processor government-access compulsion (US legal process)
| Aspect | Detail |
|---|---|
| Threat | US legal process compels Clerk, Cloudflare, or AWS to disclose data to authorities |
| Affected rights | Confidentiality, Schrems II safeguard set |
| Affected data subjects | Any |
| Inherent likelihood / severity | Low / High → Medium |
| Controls | See TIA for each transfer. Clerk: bounded data scope (no Art. 9 data); SCCs + EO 14086 + DPF (where applicable). Cloudflare: no authenticated bodies cached; SCCs. AWS: EU-only runtime; SCCs for parent-access surface; column-level encryption for the most sensitive columns. Sub-processor termination contingencies documented |
| Residual likelihood / severity | Low / Medium → Low |
R5 — Consent invalidity (under-16, withdrawn, or improperly captured)
| Aspect | Detail |
|---|---|
| Threat | The platform continues to process patient data based on consent that is invalid (under-16 self-serve, withdrawn but not honoured, captured under coercion or in unclear terms) |
| Affected rights | Lawfulness of processing (Art. 6/9) |
| Affected data subjects | Affected patients |
| Inherent likelihood / severity | Medium / High → High |
| Controls | Per-purpose consent ledger with immediate-effect withdrawal; explicit consent language in the consent purpose catalog; re-consent enforced on every version change by a gate that returns 412 until the current version is accepted. Age handling, stated precisely because two thresholds are involved: an age-of-majority test (18, Romanian contractual capacity) governs whether a caregiver may accept the clinic's terms on another person's behalf, and it is built and enforced — an unknown date of birth counts as an adult, so a blank field cannot be used to acquire authority over one. ⚠️ GDPR Art. 8's age of digital consent (16) is a different threshold for a different legal basis — consent-basis processing such as marketing and analytics — and no under-16 block or parental-consent flow exists at sign-up. Counsel §3.2 is still open |
| Residual likelihood / severity | Low / Medium → Low (subject to counsel's under-16 finding) |
R6 — Adherence telemetry over-collection / profiling drift
| Aspect | Detail |
|---|---|
| Threat | The platform begins to use adherence data for purposes beyond the disclosed clinical-follow-up (e.g. price discrimination, eligibility for future programs) |
| Affected rights | Purpose limitation (Art. 5(1)(b)), patient autonomy |
| Affected data subjects | Any patient generating adherence data |
| Inherent likelihood / severity | Low / Medium → Medium |
| Controls | Telemetry purposes enumerated in the consent purpose catalog; data-classification register restricts egress to disclosed targets, enforced by a CI gate. ⚠️ Not built: there is no algorithm-version column on the metric rows, so a change to how a metric is derived is not currently reconstructable from the data. The bi-annual DPO review of telemetry usage is pending the DPO designation |
| Residual likelihood / severity | Low / Low → Low |
R7 — Pose-tracking deferred-rollout activation without renewed consent
| Aspect | Detail |
|---|---|
| Threat | Activation of pose-landmark ingest without explicit renewed biometric consent and an updated DPIA would constitute Art. 9 biometric processing without basis. Partly overtaken by events, in the safe direction: the authoring stack shipped and a client-side skeleton preview is live in the Portal, gated on the seeded biometric_capture consent — but nothing leaves the device. There is no POST /v1/pose/frames, no codec, no replay blob, no metrics table, and no pose package in the telemetry service. The risk is therefore about the ingest half only |
| Affected rights | Lawfulness (Art. 9), purpose limitation |
| Affected data subjects | Patients in any pose-tracking-enabled program |
| Inherent likelihood / severity | Medium / High → High if activated without controls |
| Controls | Ingest is gate-locked behind: (a) renewed explicit consent per the biometric_capture purpose; (b) a DPIA appendix specific to pose ingest; (c) DPO sign-off; (d) an updated privacy notice version. It cannot be switched on by configuration, because it does not exist — the ingest endpoint, the codec and the metrics tables are unwritten, which is a stronger control than a feature flag. A second, independent gate sits alongside the GDPR one: a rep count or range-of-motion figure is a measurement, and the device's declared intended purpose states it performs none. Ingest ships when the intended purpose is updated to Class IIa, not before |
| Residual likelihood / severity | Low / High → Medium (the gate prevents accidental activation but high-severity remains as the upper bound) |
R8 — Loss of availability (RDS outage, backup unrecoverable)
| Aspect | Detail |
|---|---|
| Threat | A regional incident or data corruption destroys patient records faster than the recovery process can restore them |
| Affected rights | Availability (Art. 32(1)(b)) |
| Affected data subjects | All |
| Inherent likelihood / severity | Low / High → Medium |
| Controls | Multi-AZ database with automated failover; 35-day PITR window; a daily logical pg_dump of both the core and telemetry databases into an Object-Locked bucket, where the artefact is re-hashed after upload and only a byte-for-byte checksum match publishes the backup. An in-cloud restore drill exists and earned itself on first run — it proved production could not restore from its own dump, and the defect was fixed. ⚠️ Not in place: cross-region replication to a second EU region (not configured in any environment), and a per-release cadence for the drill. RTO 4h / RPO 1h are targets, not measured figures |
| Residual likelihood / severity | Low / Medium → Low |
R9 — Logging PII leak (Sentry, CloudWatch, application logs)
| Aspect | Detail |
|---|---|
| Threat | An exception or log line leaks identifiable patient data into operational telemetry consumed by a sub-processor (Sentry) or by platform staff outside elevation |
| Affected rights | Confidentiality, purpose limitation |
| Affected data subjects | Whoever's data is in the log line |
| Inherent likelihood / severity | Medium / Medium → Medium |
| Controls | A central redaction list masks the values of sensitive keys — password, secret, token, apikey, authorization, cookie, session, nationalid, cnp — with key names normalised so api_key, api-key and X-API-KEY all match. It is installed on the API's structured-log handler and on the audit-log JSONB writer, which matters because audit_log is plaintext, append-only and retained for years. Note it redacts by key name, not by value: email, phone and name are deliberately not on the list, so a PII value logged under an innocuous key is not caught. Error tracking runs sendDefaultPii: false plus a URL/breadcrumb scrub. ⚠️ Scope: the redaction list is installed in the API only, not in the telemetry or media services; error tracking is deployed on the Portal only |
| Residual likelihood / severity | Low / Low → Low |
R10 — Insufficient erasure on patient request
| Aspect | Detail |
|---|---|
| Threat | Patient requests erasure; the platform deletes the account but residual identifiable data persists in audit log, backups, or sub-processor stores |
| Affected rights | Erasure (Art. 17) |
| Affected data subjects | Patient who requested erasure |
| Inherent likelihood / severity | Medium / Medium → Medium |
| Controls | F11.1 erasure flow anonymises identifiable fields while preserving audit and Law 95/2006-retained clinical records (Art. 17(3)(c)); Clerk cascade deletion via management API; sub-processor erasure-handling documented in sub-processors; backups overwritten by lifecycle rather than selectively erased — patient informed of this in the privacy notice |
| Residual likelihood / severity | Low / Low → Low |
R11 — Video consultation media exposure at the sub-processor
| Aspect | Detail |
|---|---|
| Threat | Live audio and video of a rehabilitation consultation — Art. 9 health data in the clear — is relayed by a US sub-processor. Exposure could come from compelled disclosure, provider compromise, recording being enabled, or the media being relayed outside the EU |
| Affected rights | Confidentiality (Art. 32), lawfulness of transfer (Art. 44–46), purpose limitation |
| Affected data subjects | Every patient and clinician taking part in a video consultation |
| Inherent likelihood / severity | Medium / High → High |
| Controls | Nothing is recorded — rooms are created without recording, no code path enables it, and there is consequently no media at rest to compel, breach or mis-share; media servers are pinned to the EU (geo = eu-central-1) by a provider adapter that refuses to create a room when the region is unset rather than defaulting; participants are identified to the provider by an opaque reference, never by a patient identifier, so the provider cannot resolve a participant to a person; no clinical context travels in the room name, token or webhook; rooms are private and every join needs a per-participant token minted by us with a mandatory expiry, with eject_at_room_exp so expiry ends a call in progress; DTLS-SRTP encrypts every media leg in transit |
| Residual likelihood / severity | Low / High → Medium, on the technical analysis |
| ⚠️ Outstanding | The Art. 28 contract and the Art. 46 transfer tool are in force — the provider's terms incorporate its DPA and deem the SCCs signed on entering the agreement, with Module 3 selected by our processor-to-sub-processor position. What is not closed is that the provider account is shared with the legacy restartix-leo system, whose webhooks are account-wide — so our endpoint receives delivery for that system's live consultations, carrying real patient display names. Nothing is stored (the handler discards any room this platform did not derive before constructing the event), but receiving is processing, and a filter written for a hypothetical is now the only thing separating the two systems. Art. 32, and the contracting entity on the account is unconfirmed. See TIA T4 |
R12 — Erasure cannot reach the immutable backup archive
| Aspect | Detail |
|---|---|
| Threat | The Layer-2 pg_dump archive is under S3 Object Lock in COMPLIANCE mode for 7 years with no expiration rule. Neither an administrator nor the cloud provider can delete an object inside that window, so an Art. 17 erasure cannot be executed there. A privacy notice or DPA that promises deletion within 90 days is, on its face, contradicted |
| Affected rights | Erasure (Art. 17), storage limitation (Art. 5(1)(e)) |
| Affected data subjects | Any patient who requests erasure |
| Inherent likelihood / severity | High (it is certain, not probable) / Low → Medium |
| Controls | The EDPB position on backups is adopted explicitly rather than implied: erasure is applied to the live systems and to every sub-processor, the archive is used for disaster recovery only with no analytical, operational or support read path into it, and a restore is an incident-response event after which every erasure recorded since the dump was taken is re-applied before the restored system serves traffic. The commitment is written into the sub-processor list and must be stated in the privacy notice rather than left for a data subject to discover |
| Residual likelihood / severity | High / Low → Low, provided the re-application step is a real runbook and not an aspiration |
| ⚠️ Outstanding | The DPA template's Annex B retention table and §3.5 deletion clause were written before the Object Lock existed and are being corrected in the same revision as this DPIA. The restore-side erasure re-application runbook does not exist yet |
R13 — Export subject taken from an unauthorised header (closed)
| Aspect | Detail |
|---|---|
| Threat | GET /v1/me/export took its subject from the X-Patient-Profile-ID request header. The middleware parses that header for shape and deliberately does not authorise it, leaving that to the query — and this endpoint's repository reads on the admin pool, where there is no query to leave it to: RLS does not apply, and every statement in the archive was WHERE patient_profile_id = $1 with no caller and no organization predicate. Any authenticated patient could therefore obtain any other patient's complete archive by changing one header |
| Effect if exploited | Profile, appointments, forms, consents and clinical records for the named person, across every clinic they attend, in one JSON file. The subject query selects the encrypted national identifier and the service decrypts it — correct for a genuine data subject, and on this path a disclosure of the CNP that bypassed both the patients.view_national_id permission and the platform's only read-audit row |
| Affected rights | Confidentiality (Art. 32), and the integrity of the Art. 15 right itself |
| Affected data subjects | Potentially every patient on the platform |
| Inherent likelihood / severity | Exploitation required no privilege beyond a valid patient login, but did require knowing a profile identifier — a random UUID, neither guessable nor enumerable. Severity: High → High |
| Resolution | Closed. The subject is now resolved through patientscope.ResolveSubject, which answers only with profiles the calling login reaches — its own, plus any it holds a caregiver link for. A request naming a profile the login cannot reach is refused, rather than quietly answered with the caller's own archive, so nobody receives a file labelled with the wrong person. An integration test asserts all three properties: a stranger's profile is refused, the patient's own export still works, and a caregiver can still export a dependant |
| Residual likelihood / severity | Low / High → Low |
| ⚠️ Note for the record | The endpoint writes no audit row, by design: the accessor is normally the data subject, and logging someone reading their own record is surveillance of a right rather than a control over it. That reasoning is sound and it rested on the assumption this defect broke — so there is no record of whether the defect was exercised before it was closed. Any assessment of whether a personal-data breach occurred has to proceed from that absence rather than from logs |
R14 — The legacy migration itself
| Aspect | Detail |
|---|---|
| Threat | Moving ~20,000 patients, 11,000+ treatment plans and 5,000+ subscriptions from the legacy system into this platform is a large-scale processing operation in its own right. The failure modes are distinct from steady-state running: records attributed to the wrong patient; consent state lost or invented in translation; legacy fields imported with no disclosed purpose; a partially-completed run leaving two systems both believing they hold the record of truth |
| Affected rights | Accuracy (Art. 5(1)(d)), lawfulness (Art. 6), data minimisation (Art. 5(1)(c)), integrity (Art. 32) |
| Affected data subjects | The entire migrated population — the largest single group this document concerns |
| Inherent likelihood / severity | Medium / High → High |
| Controls | The platform-side design constraints that already bear on this: the legacy schema is explicitly not carried across, so every field arrives through an explicit mapping rather than a table copy — an unmapped field has nowhere to land, which is what enforces minimisation at the boundary. Consent is not inferred: a purpose with no legacy evidence arrives ungranted rather than assumed, and the re-consent gate then asks the patient. Soft delete throughout means a mis-attributed record is recoverable rather than destroyed. The migration runs against a complete target schema by deliberate sequencing — it is scheduled last precisely so it is not migrating into a moving target |
| Residual likelihood / severity | Medium / High → Medium, and only on the assumption below |
| ⚠️ Assumption | The migration tooling does not exist yet, so these controls describe a design intent rather than a verified implementation. This risk must be re-assessed against the actual tooling before it runs — that re-assessment is a precondition of the September launch, not a follow-up to it. Legacy consent-record fidelity in particular decides whether migrated patients arrive with a lawful basis on record or need to be re-asked |
Residual risk summary
Read the right-hand column. The left one is the risk if the platform had no defences at all — it is High for most entries because this is health data, and a register where it were not would mean the threats had been understated. The right-hand column is the assessment that describes the platform as it actually runs, with the controls in the entries above applied.
| Risk | If nothing were done | With the controls in place |
|---|---|---|
| R1 cross-tenant leak | High | Medium |
| R2 personnel access | High | Low |
| R3 key compromise | Medium | Medium |
| R4 government access | Medium | Low |
| R5 consent invalidity | High | Low (subject to counsel) |
| R6 telemetry drift | Medium | Low |
| R7 pose-tracking activation | High | Medium |
| R8 availability | Medium | Low |
| R9 logging leak | Medium | Low |
| R10 insufficient erasure | Medium | Low |
| R11 video consultation media | High | Medium |
| R12 immutable backup vs erasure | Medium | Low |
| R13 export subject authorisation | High | Low (closed) |
| R14 the legacy migration | High | Medium (on an unverified assumption) |
With the controls applied, nothing in this register sits at High. Five of the fourteen entries rest at Medium — R1, R3, R7, R11 and R14 — and the other nine at Low. Under EDPB and ANSPDCP practice that is the profile of processing which may proceed without Art. 36 prior consultation — subject to the sign-offs below, which have not been obtained.
R13 was the exception until recently: rated High and unmitigated, and since closed at source. It is kept in the register rather than deleted: a supervisory authority is entitled to see that the defect existed and what was done about it, and its audit note is a standing limitation rather than a resolved one.
Counsel sign-off and the DPO countersign remain unobtained independently of this.
The attention areas:
- R11 video consultations — the sensitive one, and the one whose principal safeguard is an engineering choice rather than a contract: nothing is recorded. If recording is ever enabled, R11 is reassessed from the start rather than amended. The open action is moving off the shared legacy provider account.
- R3 encryption-key compromise — mitigated further by the Phase 2 customer-managed KMS migration documented in aws-infrastructure.md.
- R7 pose ingest — gated twice over: renewed consent + DPIA appendix + DPO sign-off + privacy notice bump on the GDPR side, and the Class IIa intended-purpose update on the MDR side. The ingest half is unwritten, which is a stronger control than a flag.
- R12 erasure vs the backup archive — the position is defensible and now written down; the restore-side re-application runbook is owed.
§4 Appendix — F9 Telerehabilitation Program
Targeted DPIA appendix for the F9 patient slice. Covers the asynchronous guided exercise program; live consultations are assessed separately in §6.
F9.1 Description
Each patient is prescribed a programme whose length the clinician sets. Per session:
- Patient launches the session in the Portal
- Plays the video for each prescribed exercise. Playback is a native
<video>element streaming HLS from the video CDN, not an embedded third-party player in an iframe. The distinction matters to this assessment: no third-party frame executes in the patient's session, so the CDN sees a media request and not the page context around it - Self-reports VAS (pain) and RPE (exertion) at the end of the session
- Records session completion
- Optionally adds free-text comments
The patient may pause and resume; sessions are not time-locked.
F9.2 Specific risks
- R-F9-1: Self-report data quality. Risk: patients misunderstand VAS scale or RPE → low-quality clinical signal → potential downstream clinical decisions made on bad data. Control: in-app explanation of scales, multilingual; periodic review of scale distribution for systematic skew; clinician review of individual scores in context.
- R-F9-2: Drop-out without intervention. Risk: patient drops out, the clinic does not notice, the patient's clinical state worsens. Control: adherence and drop-off are surfaced to clinic staff in the Clinic app's analytics surfaces (the clinic's view of its own patients — a platform-side dashboard would be a cross-tenant read). Escalation is a clinical process the controller owns; the platform surfaces the signal.
- R-F9-3: Content drift under a patient mid-programme. Risk: a clinician edits shared content and the change reaches a patient already partway through, altering their prescription without a clinical decision. Control: content is copied on derive rather than referenced — the row a patient plays is their own, so editing a library template cannot reach them. Changing a live prescription requires unpublishing it first, which opens a recorded pause, and republishing is an explicit act.
F9.3 Necessity / proportionality
The video catalog, the per-session adherence record, the per-session clinical instrument — each is necessary for the clinical product. Less data would mean a clinically less useful program.
§5 Appendix — F10 Adherence Telemetry (media half)
Targeted DPIA appendix for the telemetry service that ingests video-watch and completion data. Pose-landmark ingest is not in scope and is not built — see R7.
F10.1 Description
A dedicated Go service (services/telemetry/) ingests:
- playback events: buffering, watch-percentage updates, errors
- an aggregated per-session metric: total watch %, completion, duration
- video-error events
Rows are keyed by patient_id within an organization_id — an opaque UUID, never a name, email or CNP; re-identification requires the separately-controlled primary database. Ingest is gated by signed-session-token audience + signature — not by a consent flag. Engagement / playback-QoS telemetry is processed under Art. 6(1)(f) legitimate interest, per architecture/decisions.md → Why engagement telemetry is legitimate interest, not consent. The Art. 21 right to object is honoured (consequence: the patient cannot run sessions, as the QoS data is necessary to operate playback; that is the balancing test playing out).
F10.2 Specific risks
- R-F10-1: Art. 21 right to object not honoured. Risk: a patient invokes their Art. 21 right to object to legitimate-interest processing of playback-QoS telemetry and the platform continues to ingest. Control: the clinic surfaces the privacy notice (controller-side) and routes the objection back to the platform; the platform's response is to suspend session ingest for that patient (the data is necessary to operate playback, so the consequence is no more sessions until the objection is resolved). DPO-tracked process; no per-run UI toggle exists by design (Art. 7(4) would invalidate it as coerced consent).
- R-F10-2: Telemetry over-collection. Risk: events accumulate beyond what the disclosed purpose needs (e.g. detailed UI interaction beyond watch %). Control: the ingest surface accepts a fixed set of event shapes and rejects anything else, so widening collection requires a schema and code change rather than a configuration one. ⚠️ The bound is the ingest schema itself — no CI guard independently asserts it, so nothing fails the build if the accepted set widens.
- R-F10-3: Telemetry blob retention. ⚠️ Not live, and deliberately left in the register. Telemetry aggregates into its own Postgres database; no object-storage bucket for telemetry exists in the infrastructure, so there are no blobs to retain and no lifecycle policy governing any. The risk becomes real the moment blob storage is introduced — most obviously by the deferred pose pipeline — and a genuine retention control must be built and recorded here at that point, not assumed.
- R-F10-4: Cross-service auth bypass. Risk: a misconfigured signed-session-token verifier allows ingest from a non-platform source. Control: HS256 signed tokens issued by the API at session start with a short TTL, verified by a dedicated verifier in the telemetry service against a fixed audience; tests cover token validation.
F10.3 Necessity / proportionality
Adherence is the primary outcome metric the clinic uses to evaluate program effectiveness and to identify drop-off. Without it the clinic cannot intervene. Less granular telemetry (e.g. only "session completed yes/no", no watch %) would render the drop-off detection materially weaker. The watch-% granularity is proportionate.
§6 Appendix — F5.5 Video Consultations
Targeted appendix for live video consultations. It exists because this is the platform's only processing that hands Art. 9 data in the clear to a third party.
F5.5.1 Description
A clinician and a patient join a private room for a scheduled consultation. The platform creates the room, mints one short-lived token per participant, and receives room-lifecycle webhooks. Media is relayed by Daily.co through a media server pinned to eu-central-1. No media is recorded, by us or by the provider.
The provider integration is deliberately narrow: all vendor-shaped behaviour is confined to one adapter file, and the underlying schema is provider-agnostic (it supports daily and whereby), so the sub-processor is replaceable without a data-model change.
F5.5.2 What the provider does and does not learn
| The provider receives | The provider does not receive |
|---|---|
| The consultation media itself (audio + video) | Any patient identifier, patient_profile_id, email, phone, DOB or CNP |
| An in-call display name | The clinical record, appointment content, form answers or adherence data |
| An opaque participant reference the platform derives | Any means of resolving that reference to a person |
| Room reference, participant cap, token expiry | The condition, the offering, or the clinician's specialty — nothing in the room name, token or webhook conveys clinical context |
| WebRTC connection metadata (IP, device, network quality) | Anything at rest: there is no recording to store |
The opaque reference is the design point worth stating plainly. It comes back on every webhook, so attribution works on our side, without the provider ever being told who the participant is.
F5.5.3 Specific risks
Assessed as R11. In short: the technical posture is the strongest of any transfer in the TIA, because "not recorded" removes the store that compulsion, breach and mis-sharing all depend on. The contractual posture is sound too: the provider's terms incorporate its DPA and deem the SCCs signed, with Module 3 — processor to sub-processor — selected by our actual role. The one open item is that the provider account is shared with the legacy system, which is an Art. 32 segregation point and leaves the contracting entity unconfirmed. It should close before a third-party clinic is onboarded.
F5.5.4 Necessity / proportionality
A remote consultation is the service the patient asked for; there is no less intrusive way to hold one. Proportionality rests on three choices, each of which forgoes data the platform could have collected:
- Not recording. A recording would be clinically useful and is what the
video_recordingconsent instrument was drafted for. It is not enabled, the instrument is not published, and no code path turns it on. Enabling recording is a new DPIA, a published instrument, and a re-consent — not a configuration change. - EU pinning that fails closed. The adapter errors when the region is unset rather than defaulting, so a misconfiguration cannot quietly relay EU health data through a US media server.
- Pseudonymity by construction. The provider is given the least it can function on.
F5.5.5 Consent
The telemedicine consent instrument must be adopted before a consultation can start; the gate is armed on adoption and withdrawal closes the run. video_recording is a separate instrument, unpublished, gating a feature that does not operate.
§7 Mitigation summary
The platform operates with:
| Mitigation | Where |
|---|---|
| RLS-based multi-tenant isolation | DB level |
| Break-glass for cross-tenant platform access | Middleware + UI |
| Per-purpose consent with immediate-effect withdrawal | Consent service (1B.9) |
| Audit log of every state change + every elevated access | Audit log (P41) |
| Encrypted at rest + in transit | KMS, TLS verify-full |
| EU data residency | eu-central-1 |
| SCCs + TIA for non-EU sub-processors | Sub-processor list + TIA |
| Backup + DR, with a restore drill run against the archive itself | Multi-AZ + PITR + Object-Locked pg_dump archive |
| DPO designated | DPO designation memo |
| Breach playbook + 48h Controller notice | Breach playbook |
| No recording of consultation media + EU media pinning that fails closed | Video provider adapter (F5.5) |
| CNP encrypted, writable only on a patient-audience path, masked, audited on read, trigger-refused from free-form stores | internal/core/crypto + DB triggers |
| F9 / F10 / F5.5 specific controls | This DPIA's appendices |
§8 Conclusion (subject to counsel + DPO)
The processing carries an acceptable residual-risk profile under EDPB / ANSPDCP standards on the technical analysis, and the technical controls have strengthened materially since v1 — CNP is encrypted and audited on read, the portable-profile disclosure now rests on registration rather than a defaulted-closed consent, and video consultations were built without recording and with EU media pinning that fails closed.
The procedural picture has not kept pace, and this revision states that plainly rather than carrying the v1 conditional language forward:
| Condition | Status |
|---|---|
| Counsel sign-off via F11.0.5 D7 | ⛔ Not obtained. The engagement described in the counsel brief has not run |
| DPO countersign — see DPO designation §"Tasks" | ⛔ Not obtained |
| Counsel brief §3.1 (CNP) | ⚠️ Built conservatively (patient-audience paths only, encrypted, masked, audited) ahead of the legal answer. The answer is still owed |
| Counsel brief §3.2 (under-16), §3.4 (retention table), §3.5 (biometric signature scope) | ⛔ Open |
| Art. 28 contract + Art. 46 transfer tool for the video sub-processor | ✅ In force by incorporation in the provider's terms. ⚠️ The provider account is shared with the legacy system, so the contracting entity is unconfirmed and the systems are un-segregated — see R11 |
| GDPR data-subject endpoints | ✅ Shipped; the export-path defect at R13 is closed |
| Production environment provisioned with the documented TOMs | ✅ Live since 2026-06-05 |
| R14 — migration tooling re-assessment | ⛔ The tooling does not exist yet; this DPIA's migration controls are design intent, and must be re-verified against the real implementation before it runs |
The honest statement of position, ahead of a launch that quadruples-and-then-some the affected population:
The processing design holds up. The isolation model, the consent ledger, the encryption posture for the identifiers that warrant it, the decision not to record consultations, the refusal to infer marketing consent — these were checked against the code in this revision and they are real.
What does not hold up is the assumption that everything this document asserted was implemented. Three named controls were found not to exist, and one endpoint was found to be materially broken. That is a documentation-discipline failure as much as an engineering one: a DPIA whose controls are aspirational is worse than useless to a supervisory authority, because it is the artefact they would rely on to conclude the risk had been thought about.
Two things must be true before the September launch, and neither is blocked by anything except deciding to do it:
- R14 is re-assessed against real tooling. Today its controls are intentions.
- The counsel engagement and the DPO designation happen. Every document in this directory is developer-drafted and unreviewed, and several controls here are DPO tasks with no DPO to perform them.
R13 was the third and is closed.
The remaining procedural items — the provider-account separation, the audit-archival mechanism, the gaps register on the legal index — are real but do not gate the launch in the way the three above do.
§9 Approvals
| Role | Name | Signature | Date |
|---|---|---|---|
| Drafted by (Platform) | {{SIGNATORY}} | __________________ | __________________ |
| Counsel sign-off | {{COUNSEL_FIRM}} | __________________ | __________________ |
| DPO countersign | {{DPO_NAME}} | __________________ | __________________ |
| Top management acknowledgement | {{TOP_MANAGEMENT}} | __________________ | __________________ |
Change log
| Date | Change |
|---|---|
| 2026-09-06 | R13 closed at source — the export now resolves its subject from the calling login instead of the request header, with an integration test covering the refusal, the patient's own export and the caregiver case. |
| 2026-09-06 | Re-verified claim by claim against the codebase, and re-framed from the retired June 2026 / 5,000-patient launch to the September 2026 launch at which ~20,000 legacy patients migrate. Fourteen statements were corrected. New: R13 (export endpoint does not authorise the subject — live, HIGH, unmitigated) and R14 (the migration as its own processing operation). Three named controls were found not to exist: cmd/check-telemetry-bounds, treatment_plans_versions.sessions_snapshot, and the telemetry S3 bucket with its lifecycle policy. Corrected: reads are not audited (one ActionRead call site; a read-only break-glass session logs nothing); consents record IP but no user-agent; marketing is refused outright, not consent-gated; there is no under-16 block or parental-consent flow; telemetry keys on patient_id, not principal_id; patient_exercise_logs does not exist; playback is a native <video>, not a Bunny iframe; adherence surfaces in the Clinic app, not a Console dashboard; the Art. 15/17/20 rights are shipped rather than pending. |
| 2026-09-04 | Revised against what was actually built. Added R11 (video consultation media) and R12 (immutable backup vs erasure), plus §6, the F5.5 appendix. Resolved the CNP necessity row from "TBD" to the built posture. Corrected R7 — pose preview is live on-device, ingest is unwritten and now carries an MDR gate as well as a GDPR one. Recorded the MDR Class I registration in §1.1. Replaced the launch-conditional conclusion with the current, less comfortable one. |
| 2026-05-15 | Initial v1 — platform DPIA + F9 + F10 (media half) appendices. Pose-tracking appendix deferred to activation gate |