Projects / OpenEMR

OpenEMR + minidauth a medical record the database itself can't read

A medical record is the most regulated, most sensitive data most software ever holds. In this fork of OpenEMR, the widely used open source electronic health records system, a patient's name, Social Security number, address, phone and email are sealed before they reach the database, and open only for a clinician a quorum granted the reading role, even an administrator. It seals a large legacy PHP app with no ORM, showing the sidecar reaches any stack.

Run end to end

What gets sealed

OpenEMR is the open source electronic health records system. These fields are stored as ciphertext, and nothing on the server can decrypt them.

Patients
Name, Social Security number, street address, phone numbers and email. Dates and coded fields stay in the clear, so scheduling and reports keep working.

How it's wired

  1. Sealed on write

    A hook on the modern PatientService (the create and update choke point) and on the legacy create path seals the demographics before the row is written, so ciphertext is what reaches the database.

  2. Opened per user

    The common demographics readers open the fields for the signed-in user OpenEMR already tracks, so names shown in the chart, titles, tabs and the finder are plaintext for a verified reader.

  3. Gated by a quorum role

    A field opens only if minidauth's quorum grant says that user holds the reading role. Even an OpenEMR administrator who can open the chart sees ciphertext without it, and revoking it makes the same record go dark.

The key that seals these fields exists only as shares across the Tide network, and 14 of 20 nodes have to cooperate to use it. It is never on the OpenEMR server.

Details that matter

  • The sidecar holds no reading identity of its own. Opening is delegated per user and gated on a quorum-granted role.
  • With no granted reader in the request, a field stays sealed, so a read fails safe to ciphertext and never leaks plaintext by accident.
  • You cannot even search the database for a Social Security number, because the column holds only ciphertext.
  • The PHP side signs its short-lived reader token with a private Ed25519 key rather than a shared secret, so a copy of a config file is worthless.

Status and running it

A proof of concept, off unless MINIDAUTH_SEAL_URL is set, so an unconfigured checkout behaves exactly like upstream OpenEMR. The fork's README covers setup against a running minidauth.

Want to do the same for another app, or stuck running this one? Join the Discord and I'll help you out.

Other projects: Formbricks · Twenty · Cal.diy · Documenso · Medusa · Rocket.Chat · Chatwoot · Firefly III · Paperless-ngx · Integrations