Skip to content
RadBase.

Written to be forwarded to your information-governance officer

This page is the security summary, not a brochure with a padlock on it. It says where patient data goes, what stops it going anywhere else, who has to sign, what is written down, and — at the end — what has not yet been demonstrated. Send it on as it is.

For a deeper review, including the deployment topology and the residual-risk register, write to syedmuhammedabbas@gmail.com.

01 · The boundary

Raw patient data never crosses it, and that is a credential, not a policy

Every upload and every PACS transfer lands in a quarantine store. One process can read that store: the de-identification worker. The application’s own storage credential has no read access to it at all — not restricted access, none. The credential the browser uses to upload can write and cannot read back.

This is enforced by issuing separate storage credentials with separate policies, so it survives a code review that misses it, a refactor, and a new engineer. There is an integration test whose entire purpose is to assert that the application’s credential is refused when it tries to read a quarantined object.

Nothing is written to the archive except the worker’s de-identified output. There is no code path from quarantine to the published archive, because there is no credential that could take one.

Lifetime
Seventy-two hours from arrival, whatever happens to the case. Published, rejected, or never reviewed at all — the raw object is purged. An unreviewed case eventually becomes unrecoverable, and that is intended.
On publish
Hard delete. No versioning, no lifecycle transition to cold storage, nothing to restore. The deletion is recorded in the audit trail with a timestamp.
Backups
The quarantine store is deliberately excluded from backups. Copying raw studies with a 72-hour life into a long-lived backup would create exactly the reservoir the 72-hour rule exists to prevent. The backup manifest records the exclusion explicitly.
Enforcement layers
The expiry is set when the upload ticket is created, so an abandoned upload still expires; a scheduled sweep is the primary mechanism; an object-store lifecycle rule is the backstop for when the sweep is down; and a database view surfaces any quarantine object still present when it should not be.
Scratch space
The worker’s temporary directory is an in-memory filesystem, so intermediate images never touch a disk.

02 · Tenant isolation

The database refuses the query, not just the code

One installation can host many hospitals. Every hospital is an organisation, every row of case data carries its organisation’s identifier, and PostgreSQL row-level security refuses any query that is not scoped to the caller’s organisation. The application connects as a role that cannot bypass those policies, and it refuses to start if it detects that it has been given one that can.

That is two independent layers: the application checks the role, and the database enforces the scope. A bug in the first does not leak data, because the second is not asking the first for permission.

The build fails if a table loses that protection. Continuous integration asserts against the live schema that every tenant table has row-level security enabled, that it is forced so the table owner does not bypass it, and that it carries a policy. The check is structural rather than a hard-coded list, so adding a table needs no edit — only deliberately exempting one does, and that exemption is visible in the assertion file.

Objects in storage are prefixed by organisation. The DICOM server is shared, and every image request is re-authorised against the owning case rather than trusted because it reached the server.

03 · The human gate

Nothing publishes without a named person recording it

  • A consultant must explicitly record that no identifiers remain. The record carries their name and the time. There is no automatic pass and no bulk approve.
  • The reviewer sees every key image at full resolution, every detection the pixel stage made with the redaction applied to it, and a diff of the report text before and after.
  • They can draw further redaction boxes or crop a region, and the pixels are rewritten before publication.
  • For ultrasound, screen captures, mammography, angiography, fluoroscopy and unspecified modalities, the person who uploaded a case may not be the person who passes it. The rule is applied at the review decision and again at publish, so it cannot be routed around by a different endpoint.
  • The reviewer’s free-text note is scanned for identifier shapes before it is saved, and refused if it contains one. That field is the natural place to type “MRN 998877 still visible top-left”, and it lands in an append-only log where it could never be corrected. A refusal names the kind of identifier and never echoes the value.

04 · The audit trail

Append-only, enforced by the database

Every state change is written to an audit log that database triggers make append-only: an update or a delete against those rows is rejected, not merely discouraged. That includes the deletion of the raw upload, which is what makes the 72-hour promise auditable rather than merely asserted.

Subscription and entitlement changes have their own append-only history, so a hospital arguing about when it was suspended and an auditor asking who changed a limit are reading a record nobody could have edited.

The audit log itself is complete today. The organisation-admin screen for reading it in the browser is in progress; until it lands, the log is queryable through the API by an organisation administrator.

05 · What is stored where

Three stores, and what is in each

Quarantine store
Raw uploads and raw PACS arrivals. Readable only by the de-identification worker. Deleted within 72 hours. Never backed up.
Archive store
De-identified DICOM, the rendered images used by the viewer, and exported teaching bundles. Keys are content-addressed and prefixed by organisation.
Library store
Reference material and the licence evidence for it.
PostgreSQL
Cases, taxonomy, tags, collections, quiz state, subscriptions and the audit trails — every tenant table under row-level security. The identity server and the DICOM server keep their own databases.
The DICOM server
De-identified instances only. Shared across organisations, with every image request re-authorised against the owning case.

06 · What is deliberately not stored

The absences are decisions

  • The name of the file you uploaded. Study exports are routinely called things like CT_HEAD_SMITH_JOHN_MRN123.zip, and that database row would outlive the 72-hour purge of the file itself. RadBase stores a synthetic name instead.
  • Search query strings in logs. The request logger strips everything after the question mark, because a query string can carry what someone was looking for.
  • Raw studies in any backup. The quarantine store is excluded by name, and the exclusion is stamped into the backup manifest.
  • Scratch files on disk. The worker’s temporary directory is in memory.
  • Worker credentials in the application’s process. The API deletes the worker-only storage credentials from its own environment at boot, so they are not there to be misused or dumped.
  • Anything about another hospital. There is no cross-hospital case index, no shared pool and no route by which a case leaves your organisation.

07 · Access

Sign-in, roles, and the shape of a mistake

Identity
An open-source identity server ships with the installation and runs inside it. Sign-in is by email and password; the browser never holds the access token. Passwords are at least twelve characters, and ten failed attempts lock the account for a growing interval.
Joining
An administrator invites someone by email address and hands them a join link that grants exactly the role on the invitation. Invitations expire and can be revoked before use. There is no self-service sign-up.
Roles
Six ranked roles. Nobody may grant a role above their own, change their own role, or alter someone above them. The last owner of an organisation can be neither demoted nor removed.
Hardening
Production mode, e-mail verification, one-time passcodes for new users and the disabling of the developer password grant are applied from configuration, and the installer refuses a production start that is missing any of them. E-mail verification and password reset need an outgoing mail server, which the site provides.
Transport
Strict transport security, content-type sniffing disabled, framing denied, and a content security policy carrying a per-request nonce on the application.

08 · Evidence

What has been demonstrated, and what has not

What is measured. 2,205 automated tests across 5 applications, and a 56-study synthetic corpus spanning eight modalities with identifiers deliberately planted in DICOM headers, in the pixels and in the report text. Both run on every commit. A ground-truth manifest records every planted identifier and its exact location, and the corpus scores against it: all planted header identifiers removed, all planted report identifiers replaced, and every instance carrying a planted pixel identifier either redacted or routed to a reviewer.

The corpus also plants tags that must survive — so a pipeline that passed by deleting everything would fail the test rather than pass it. And it is a pure function of its seed, which is itself tested, so a green run cannot be an artefact of a regenerated harness.

  • Open on the roadmap and stated in the product’s own risk register: per-tenant storage credentials in place of prefix isolation, encryption at rest, and access logging on the object store.
  • Directory binding for single sign-on is a configuration task in the bundled identity server rather than an application change, and is scoped per deployment.