Owner: Matthew (SIRO) · Maintained by: Hermes with OpenCode · This review: 18 October 2026 · Next review: 18 October 2027 (annual, as 1.1.2 requires)
Evidence for: item 1.1.2 — "There is a record of all information assets that contain personal information (an IAR or ROPA), and it is reviewed at least 12 months." This register sits under 1a. Personal Confidential Data and is the data-side companion to the IT Asset Register, which covers hardware and software (Standard 8) and explicitly excludes data assets.
The controlling rule: no patient data on VPS1, VPS2 or VPS3. Patient data is held only on client VPS instances. This register exists to show where personal data actually sits, and the largest finding of this review is that the estate holds workforce personal data, not patient data — which is a deliberate architectural position, not an accident. Where a store looked like a patient store, it was measured rather than assumed (§4).
Method. Measured live on 18 October 2026: row counts from
pg_stat_user_tables, database sizes frompg_database_size(), volume sizes from the filesystem. No table contents were read — only table names and row counts, which is enough to classify a store without looking at a single record.
These are the assets that genuinely answer 1.1.2. All of it is information about staff and signatories, not patients.
| ID | Asset | What it holds | Where | Owner | Retention |
|---|---|---|---|---|---|
| IA-01 | keycloak — identity provider |
Staff usernames, email addresses, hashed passwords, MFA secrets, role mappings. 17 MB | Central PostgreSQL, Primary | SIRO | Life of account + audit window |
| IA-02 | aes_portal — e-signature / PKI portal |
Signer identities and signing metadata. 7.5 MB | Central PostgreSQL, Primary | SIRO | Life of the signature record |
| IA-03 | wikijs — knowledge centre |
Internal documentation, policy copies, page history. Staff names appear in authorship and audit; user-uploaded media volume is 4 KB (effectively empty). 125 MB | Central PostgreSQL, Primary | SIRO | 12 months of page history |
| IA-04 | Nightly database dumps | Compressed dumps of every database in §1–§3, so they inherit whatever those hold | Primary | SIRO | 7 days — enforced by the backup scripts (-mtime +7) |
| IA-05 | Staff endpoint records | Do not exist. No register of laptops, phones or removable media | — | SIRO | — |
IA-05 is a gap, not a store. It is listed because 1.1.2 asks for a record of all assets, and the device half of DSPT 9.5.2 and 6.2.1 is unanswered for the same reason.
Measured, because "it's only reference data" is exactly the claim that needs evidence.
| ID | Asset | What it holds | Where | Notes |
|---|---|---|---|---|
| IA-06 | veripath_platform — platform database |
38 reference tables / 14.2 M rows (SNOMED CT UK, dm+d pricing) and 121 application tables / 82 K rows of platform configuration and demo fixtures. 2.98 GB | Central PostgreSQL, Primary | No patient data. The only patient-shaped table holding records is an invoice-line table with 2 rows. Renamed from dsp_clinic on 2026-10-18 (§4) |
| IA-07 | veripath_dental — dental demo |
4 appointments, 1 practice, 1 provider. 11 MB | Central PostgreSQL, Primary | Demo fixtures only |
| IA-08 | Orthanc DICOM store | Dental imaging, test fixtures. 12 MB | App engine volume | No live patient imaging on our estate |
| IA-09 | ERPNext client site data | Client attachments and generated files. 8.1 MB | Auxiliary volume | Test client content |
| IA-10 | Forgejo repositories | Source code, infrastructure config, policy sources. Confidential, no personal data | Forgejo, Primary | |
| IA-11 | DefectDojo findings | Vulnerability scan results. 6.5 GB + 4.4 GB (live + staged) | Primary volumes | Confidential, no personal data |
| IA-12 | SeaweedFS object store | S3-compatible filer storage. 18 MB used | Primary | Contents unverified (§5, D3) |
| IA-17 | clinic_vault — backup archive |
Borg repository, encrypted and append-only, 57 GB. Fed nightly by pulling the Primary's /var/backups/daily dump sets (41 files) |
Auxiliary host, root-only | Holds our own database dumps — DefectDojo dominates at 1.15 GB per day. No patient data. Encrypted at rest, which is the only encrypted store on the estate and worth citing under 9.5.2 (§5, D2) |
The client_vps_postgres container is not a client patient store. It was created on 11 September 2026 by the gp_booking_app compose stack itself (postgres:15-alpine), as scaffolding for the tenancy model, and it holds development and demo databases:
| ID | Asset | What it holds | Where | Status |
|---|---|---|---|---|
| IA-13 | dsp_clinic_dev |
Development copy of the platform schema: 154 tables, 90 rows across patient-shaped tables | Client DB container, Primary | Demo fixtures, but must be proven so or purged (§5, D1) |
| IA-14 | erpnext_test_client |
Test client ERPNext site: 701 tables, 0 rows in patient-shaped tables. 44 MB | Client DB container, Primary | Test client |
| IA-15 | veripath_pharmacy |
Pharmacy client database: 36 tables, 0 rows in patient-shaped tables. 10 MB | Client DB container, Primary | Test client |
| IA-16 | fh_app_user |
The image's default database, created empty on first start | Client DB container, Primary | Empty scaffolding — candidate for removal |
This register originally classified dsp_clinic as a likely special-category patient store, on the strength of its name. That was wrong, and the measurement that disproved it is in §2. Three controls now stop the mistake recurring:
veripath_platform and veripath_dental carry a catalogue comment, readable from \l+ or any inspection, stating that patient data is stored only on client VPS instances and never on VPS1/2/3.dsp_clinic → veripath_platform, because the name was the whole reason it was misread. The rename covered every live consumer: the three app .env files (DB_NAME, GP_DB_NAME), the celery start scripts, the environment template, the pgbouncer [databases] entries (regenerated from the tenant register), and the security check-in sync job's config and fallback.The client DB container is the one place to be explicit about. It runs on the Primary, so a reader could reasonably ask how "patient data lives on client VPSs" squares with a postgres container called client_vps_postgres sitting on VPS1. The answer, measured: it is scaffolding created by the app's own compose file, and it holds development and test databases (§3) — not live client patient data. This wording needs the SIRO's confirmation, because the hosting arrangement is his to describe.
| # | Item | Why it matters |
|---|---|---|
| D1 | dsp_clinic_dev — DECIDED 18 Oct 2026: left as it is |
Holds 90 rows across patient-shaped tables, all demo fixtures, and it is the GP stack's CLIENT_DB_NAME in three places, so it is in use rather than obsolete. The SIRO chose to leave it untouched. Recorded here so those rows are a known, accepted position rather than a surprise on the next review |
| D2 | clinic_vault — identified 18 Oct, two things still needed |
It is not client data: it is our own dumps, encrypted. What is still missing is a retention rule (41 daily dump sets, mostly DefectDojo, held indefinitely in an append-only repo) and a recorded restore test. Neither is urgent; both are askable in an audit |
| D3 | SeaweedFS contents unverified (IA-12) | 18 MB, small, but unclassified |
| D4 | No ROPA exists. The CMS page data-processing-activities is a pasted AI conversation containing an unfilled template ([Your Organisation Name]), published since May with an effective date |
Replace it with a real record of processing or unpublish it. This register answers the IAR half of 1.1.2; the ROPA half is still open |
| D5 | Incident forensic material — 880 MB at /root/forensics, held since 14 Aug with no disposal rule |
Contains an attacker account list and a database dump. Decide what is retained as the incident record and dispose of the raw copies with a date recorded |
| D6 | Retention periods for IA-01 to IA-04 | Proposed in the table above; the SIRO should confirm rather than inherit a proposal |
| D7 | A guard, so this cannot silently change | Proposed: a daily check across VPS1–3 that flags any patient-shaped table gaining rows, wired into the existing security status feed. Without it, the "no patient data" position depends on nobody ever seeding a demo again |
| D8 | Shared database superuser — DECIDED 18 Oct 2026: rotation declined, risk accepted | Every service connects as the PostgreSQL superuser, and that one password sits in 12 live configuration files (27 counting backups and archives) — infra .env, wagtail, cold-standby, the ERPNext site config, celery defaults, the three app env files, one markdown document, and our script config. keycloak and wikijs already do this correctly, each with its own non-superuser role, which is the pattern to extend. Worse than a shared database credential: the same value is also the Redis password in the GP, dental and ERPNext stacks, so one string protects the database and the caches. On 18 Oct the value was removed from the August incident report that was enumerating leaked secrets — that document should not have restated a live credential. Rotating it is a coordinated change across several stacks, not a credential swap. The exposure that prompted the question was contained — the transcripts that displayed it live inside /root (0700). Accepted, and recorded as a risk rather than a clean position: shared superuser credentials with plaintext copies are the kind of access-control weakness Standard 4 asks about, and the durable fix is a dedicated database user per service so the superuser stops being load-bearing |