A demo reference for the three IG roles — data subject, data controller, data processor — who can see special-category (Article 9) data, and exactly where each control lives in the commvita platform, following a single record from access granted to access rescinded by policy — all on one immutable, intelligent audit.
UK GDPR / Data Protection Act 2018 define who does what. The subject holds the rights; the controller holds the accountability and the decisions; the processor only ever acts on the controller's instructions.
Not a party to the processing contract. The law gives them rights: to be informed, to access (SAR, Art.15), rectify, erase, restrict, port (Art.20) and object — plus the National Data Opt-Out for secondary use.
Decides why and how (Art.4(7)). Holds the lawful basis (Art.6 + Art.9(2)(h) for health), keeps the Art.30 record of processing, runs DPIAs, answers subject rights and reports breaches to the ICO within 72h.
Processes only on the controller's documented instructions, never for its own purposes (Art.4(8)). Bound by an Art.28 contract; sub-processors need authorisation. Decide your own purpose and you become a controller.
Under UK GDPR Article 9, health, sex life & orientation, ethnicity, genetics, biometrics, religion, trade-union membership and political opinion are special-category data — in a care record, almost everything clinical qualifies. It carries an extra layer of control on top of the access chain below.
Access needs an Art.9 condition — for direct care that is Art.9(2)(h) (provision of health/social care). The 18-role × 16-domain RBAC matrix decides which clinical domains a role reaches, and effective_role() takes the role only from an explicit assignment — never inferred.
Policy-as-code binds role → field, so even inside a record a professional sees only the special-category fields their purpose needs (Caldicott Principle 4 / Art.5(1)(c)). The executive oversight role is deliberately excluded from unrestricted scope — seniority is not blanket access.
Genomics requires an explicit genomics_access grant; sexual-health / GUM records are handled as Art.9 special category with their own confidentiality; sensitive mental-health notes can be break-glass-only. And every Art.9 action requires a fresh step-up re-authentication — a standing session is not enough.
Granted by an explicit role + Art.9 purpose + scope (plus the specific grant for genomics/GUM). Rescinded by the same policy levers as everything else — self-expiring delegations/groups filtered by effective_scope(), consent withdrawal or opt-out propagated via FHIR Consent R4, and token revocation. Secondary use of special-category data adds the Caldicott Guardian gate, honours the National Data Opt-Out, and for research relies on Art.9(2)(j) with Art.89 safeguards.
Access control starts at the very first keystroke. You cannot search your way to a record you have no basis to see — the smart patient search is constrained by scope, role and jurisdiction before a single name is returned. ● Live
The smart patient search (the PatientPicker used across the platform) filters by your organisation scope — the union of your own practice/trust ODS codes plus any team-lead delegations and user-group memberships (effective_scope()), exactly as the patient list does. A GP-practice receptionist scoped to one practice only matches that practice's patients; an empty scope returns nothing (never "everyone"); only an explicitly-assigned UNRESTRICTED role searches across organisations.
A receptionist (read_only) is blocked from special-category records — sexual-health and Mental Health Act data require a clinical role (require_min_role), returning 403 for reception/admin roles. Those sensitive records sit behind a clinical basis, never behind a demographic search: finding the person is not the same as opening their Art.9 data.
Search is also bounded by jurisdiction — an England user never matches a Crown Dependency, Ireland or any other jurisdiction's patients — and every match resolves through the same immutable audit as any other access.
Access is never a single switch — it is a chain, and every link must be satisfied before a professional sees a record. Each link is a real control in the platform.
The controller fixes why — a lawful basis (Art.6) plus a special-category condition (Art.9(2)(h), provision of care). This is encoded as policy-as-code: purpose → dataset, role → field.
Before any data is touched, an Art.28 Data Processing Agreement binds the processor to instructions-only use, security (Art.32) and deletion at end. The processor evidences DSPT / DTAC assurance first.
A role decides which of 16 data domains the person can reach (18-role hierarchy read_only→…→superadmin). Role comes only from an explicit assignment — effective_role() never infers it.
effective_scope() computes the ODS org codes a user may see — the union of their own org, any team-lead delegations (UserDelegation) and user-group memberships (UserGroup, scope-only, never a role). Stamped into the token at login as _org_codes. You see your population, not the database.
Role + scope let you reach a cohort; opening an individual record needs a care reason. Each access is justified and logged; out-of-area, bulk or cross-ICS access raises an anomaly alert.
Access rides a 60-minute JWT carrying a unique JTI. Special-category (Art.9) actions require a step-up re-authentication (/auth/reauth). Nothing is a standing key.
Secondary (non-direct-care) use adds a gate: the request routes to the Caldicott Guardian queue for approval, and the National Data Opt-Out is enforced — before any access is granted.
Granting is not the end — the access is continuously constrained, watched and evidenced.
Role × domain matrix caps what; org scope caps whose. Data minimisation is enforced by design (Caldicott Principle 4 / Art.5(1)(c)) — e.g. the executive oversight role is deliberately excluded from unrestricted scope.
Non-direct-care access is held for Caldicott Guardian decision (CaldicottDecision, principles applied recorded); the National Data Opt-Out blocks secondary use of an opted-out patient's confidential data.
Emergency override exists (e.g. break-glass-only mental-health notes) but every use is justified, logged, notifiable and challengeable by the patient or IG.
Access and account events are written to append-only SHA-256 hash-chained (WORM) logs that the server re-verifies on every read — a broken chain shows a tamper banner. There is no update or delete endpoint.
Non-controller access triggers controller notification and a patient "query this access" view (Caldicott Principle 8); the subject can pull an Art.15 SAR of who accessed their record.
The important part: access ends automatically when the policy that justified it lapses. No administrator has to remember to switch it off.
Delegations and group memberships carry an expires_at. effective_scope() filters expired grants at authentication time — the moment a grant lapses the associated org scope simply stops resolving, so the record falls out of view with no manual step.
A lapse is written once (lapse_recorded) to the hash-chained account log, and renewal requires an explicit re-attestation — access can't quietly persist or silently re-elevate. Role and group changes are logged the same way.
The subject withdrawing consent (Art.7) in the /consent-hub, or opting out in /shcr-opt-out, updates a FHIR Consent R4 record that propagates — removing sharing at source, including partial category-level opt-outs.
A live session is killed immediately: the JTI denylist revokes the token, /auth/logout ends it, and an admin can force-logout any session from the Security Console. The 60-minute TTL caps residual exposure.
A patient or IG reviewer can challenge a recorded access or a break-glass override; the workflow can restrict or revoke it, with the decision written to the immutable log.
Every event above — grant, access, special-category read, expiry, extension, revocation — lands in one place: an immutable, tamper-evident log that doesn't just record what happened, it watches for what shouldn't and drives a response.
Audit rows are append-only and SHA-256 hash-chained — each row's prev_hash → row_hash links to the one before it. The server re-verifies the whole chain on every read and shows a tamper-evidence banner if a link is broken. There is no update or delete endpoint — an entry cannot be quietly altered or removed, on any log (Login Audit, IG Spine, commvita Sign, Policy-to-Process).
Each event records the username, action and result, plus the source IP + X-Forwarded-For chain, the country logged in from, the device / user-agent and the jurisdiction — enough to reconstruct exactly how an access happened, and to spot when it looks wrong.
The audit is rule-driven: access outside expected hours or from an unusual location is flagged, and the Legitimate Relationship Log surfaces out-of-area, bulk and cross-ICS patterns with investigate / dismiss / DPO-flag workflows — so "why was this record opened at 03:00 from another region?" is a question the system asks, not one you have to go looking for.
When a patient's record is opened in another direct-care setting — a hospital, out-of-hours or NHS 111 clinician — a safety-netting alert is sent to the registered GP practice. It is gated on encounter / ADT corroboration (a real clinical contact, not a stray click), kept minimum-necessary, deduplicated and rate-limited, and the acknowledgement is itself audited. This is Caldicott Principle 8 in action — the duty to share for care, made visible to the patient's home team.
The Security Console adds clinical-workflow-aware anomaly detection and Article 9 special-category monitoring, with a GDPR Art.33 72-hour ICO breach countdown. And the subject can see who accessed their record and "query this access" — pulling an Art.15 SAR. Every alert is acknowledgeable, and the acknowledgement is written back to the immutable chain: the audit records the response, not just the event.
| IG concept | Module | Route | Model / API | Standard |
|---|---|---|---|---|
| Purpose & legal basis (policy-as-code) | Confidential-IG Spine | /ig-spine | purpose→dataset · role→field | UK GDPR Art.6/9 |
| Controller accountability | IG Hub · Patient Data Control | /ig-hub | DPIA · Art.30 flows · 72h breach | DSPT 2024/25 · Caldicott 8 |
| Processor contract & assurance | Supplier IG Assurance | /supplier-ig-assurance | DPA / contract register | Art.28 · DSPT · DTAC |
| Joint-controller sharing | DSPA Manager | /dspa-manager | DSPA lifecycle · signatories | Art.26 · NHS DSPA template |
| Role — what you can reach | RBAC · RBAC Matrix | /rbac | effective_role() · /rbac/matrix | 18 roles × 16 domains |
| Scope — whose records | RBAC · Groups & Delegations | /rbac/user-groups | effective_scope() · UserGroup · UserDelegation (expires_at) | ODS · Caldicott P4 / Art.5(1)(c) |
| Legitimate relationship | Legitimate Relationship Log | /legitimate-access-log | access log · anomaly alerts · Art.15 SAR | DSPT · GDPR Art.30/15 |
| Caldicott gate (secondary use) | Confidential-IG Spine | /ig-spine | CaldicottDecision | Caldicott 8 · National Data Opt-Out |
| Consent & withdrawal | Consent Hub · Shared Care Opt-Out | /consent-hub · /shcr-opt-out | FHIR Consent R4 · override log | GDPR Art.7 · FHIR R4 Consent |
| Session & token control | Data Security Architecture · Security Console | /security-architecture | JTI denylist · /auth/logout · /auth/reauth | GDPR Art.32 · NCSC |
| Special-category (Art.9) control | Confidential-IG Spine · RBAC | /ig-spine · /rbac | role→field policy-as-code · genomics_access · /auth/reauth | UK GDPR Art.9(2)(h)/(j) |
| Scoped patient search (org · role · jurisdiction) | Patient search · PatientPicker | GET /patients/search | effective_scope() org filter · Art.9 reads role-gated (/mha-records, /sexual-health) | UK GDPR Art.5(1)(c)/9 · Caldicott P4 |
| Immutable audit (grant/protect/rescind) | Login Audit · IG Spine WORM | /login-audit | SHA-256 hash chain · GET /auth/login-audit | GDPR Art.5(1)(f) · DSPT |
| Intelligent audit (OOH · anomaly · GP alert) | Legitimate Relationship Log · IG Spine · Security Console | /legitimate-access-log · /ig-spine | anomaly alerts · Clinical Trigger Alert (ADT/111) · Art.9 monitoring | Caldicott 8 · GDPR Art.33 |