Skip to content
FerroHEALTHPublic

About

A pure-Rust Master Patient Index for the openEHR platform: cross-identifier resolution and record location over IHE PIX and PDQ.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

335 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FerroPIX

The master patient index: who the patient is, under every identifier, and where their records are.

Documentation · Quick start · What it implements · Licensing

Project
Release CI License: BUSL-1.1 Docs GHCR Image pulls

Quality and security
OpenSSF Scorecard Quality gate Coverage CodeQL Signed releases SLSA Build L3

Conformance
PIXm 3.1.0 examples PDQm 3.2.0 examples PMIR 1.6.0 examples Audit record examples

What FerroPIX is

Every hospital, clinic and laboratory gives the same person its own patient number. When records have to come together, for a referral, a shared care plan or a regional record, someone has to know that these numbers belong to one person. Getting that wrong means a record in the wrong file, or a record that is never found.

FerroPIX is the Master Patient Index of the FerroHEALTH family, in pure Rust. It holds the cross-reference between the identifiers each organisation issues in PostgreSQL, finds a patient from their name and date of birth, sends the pairs its scorer is unsure of to a data steward, and answers for all of it over the IHE profiles hospital integrators already use. It also mints the pseudonyms a clinical store names a patient by, so identifying data stays outside the clinical record, which is where the openEHR platform model puts the patient index.

It is for hospitals and care networks that need one view of who a patient is across their own systems and their partners', for integrators and regional exchanges that link records between organisations, and for the data stewards who resolve possible duplicates in the operator console.

The FerroPIX operator console dashboard

What it implements

Each transaction follows the IHE ITI Technical Framework and its implementation guide, on HL7 FHIR R4, and the book cites the section behind every answer. A feed over FHIR and a feed over HL7 v2 of the same patient land in one person, and every query reads the same store.

Face Transactions What it does
PIXm Manager, PIXm 3.1.0 ITI-83, ITI-104 Answers which identifiers the other domains hold for a patient, by GET or by a posted Parameters body that keeps the identifier out of every URL, and takes each domain's feed, revisions, merges and removals
PDQm Supplier, PDQm 3.2.0 ITI-78, ITI-119 Finds patients by demographics and identifiers, with paging and the domain filter, and $match scores a described patient as certain, probable or possible
PMIR Registry, PMIR 1.6.0 ITI-93, ITI-94 Receives the master identity feed, takes subscriptions, and sends every committed change to each subscriber in order, from an outbox written in the change's own transaction
HL7 v2 PIX and PDQ, ITI TF-2 ITI-8, ITI-9, ITI-21, ITI-10 An MLLP listener for the identity feed, the PIX query and the demographics query, and update notifications to the consumers you name, on FerroBRIDGE's v2 parser
XCPD Responding Gateway ITI-55 Tells another community whether yours knows a patient, by a shared identifier or by demographics, on the same exchange or asynchronously

Beside the five faces:

  • Matching: an identifier in an authoritative domain links two records on its own, a probabilistic scorer grades the rest, and the pairs it is unsure of wait in a review queue.
  • Pseudonyms: one pseudonym per patient and pseudonymisation domain, read only by that domain's consumer.
  • The Dutch identifiers: the BSN checked by the eleven-proof, and every identifier of a sensitive domain sealed at rest and found by a keyed hash.
  • The operator console: search, the patient record, the review queue, link, merge and unmerge with a reason, and every reveal of an identifier recorded as a disclosure.

Quick start

The quickstart runs the published images beside PostgreSQL 18 and a development sign-in server, feeds one synthetic patient and asks for it. You need Docker Compose 2.23.1 or newer (or Podman with a compose provider), curl, jq and openssl, and no checkout. Every command below is a step of scripts/quickstart/tour.sh, which CI runs over the repository's compose.yaml, the file each release attaches, against the images each change builds. The Quickstart page explains each answer.

Every release attaches the quickstart as one self-contained compose.yaml. Download the latest release's into an empty directory:

curl -fsSLO https://github.com/FerroHEALTH/FerroPIX/releases/latest/download/compose.yaml

Write the secrets, start the stack and ask for readiness:

mkdir -p secrets && umask 077
openssl rand -base64 32 > secrets/ferropix-data-key
openssl rand -hex 16 > secrets/postgres-password
openssl rand -hex 16 > secrets/ferropix-db-password
printf 'postgresql://ferropix:%s@postgres:5432/ferropix' \
  "$(cat secrets/ferropix-db-password)" > secrets/ferropix-db-url
openssl rand -hex 16 > secrets/console-oidc-client-secret
docker compose up --wait
curl --fail-with-body -s http://localhost:8080/health/readiness

Get a client credentials token that may feed and query:

token="$(curl --fail-with-body -s http://localhost:8180/ferropix/token \
  -d grant_type=client_credentials -d client_id=quickstart-source \
  -d client_secret=any -d 'scope=system/Patient.cud system/Patient.rs' \
  | jq -er .access_token)"

Feed a patient as the hospital's Patient Identity Source (ITI-104):

curl --fail-with-body -s -X PUT \
  'http://localhost:8080/fhir/Patient?identifier=urn:oid:2.999.1.1|H-100001' \
  -H "Authorization: Bearer $token" \
  -H 'Content-Type: application/fhir+json' \
  --data-binary @- <<'EOF' | jq -e '.issue[0].severity == "information"'
{
  "resourceType": "Patient",
  "identifier": [
    { "system": "urn:oid:2.999.1.1", "value": "H-100001" },
    { "system": "http://fhir.nl/fhir/NamingSystem/bsn", "value": "999990007" }
  ],
  "name": [{ "family": "Proefsma", "given": ["Ada"] }],
  "gender": "female",
  "birthDate": "1961-04-12"
}
EOF

Ask for the patient's BSN (ITI-83), with the identifier in a posted body:

curl --fail-with-body -s -X POST 'http://localhost:8080/fhir/Patient/$ihe-pix' \
  -H "Authorization: Bearer $token" \
  -H 'Content-Type: application/fhir+json' \
  --data-binary @- <<'EOF' | jq -e '.parameter[] | select(.name == "targetIdentifier") | .valueIdentifier'
{
  "resourceType": "Parameters",
  "parameter": [
    { "name": "sourceIdentifier", "valueString": "urn:oid:2.999.1.1|H-100001" },
    { "name": "targetSystem", "valueString": "http://fhir.nl/fhir/NamingSystem/bsn" }
  ]
}
EOF

Find the patient by demographics (ITI-78):

curl --fail-with-body -s -X POST 'http://localhost:8080/fhir/Patient/_search' \
  -H "Authorization: Bearer $token" \
  -H 'Accept: application/fhir+json' \
  -d family=proefsma -d birthdate=1961-04-12 \
  | jq -e '.total == 1 and .entry[0].resource.name[0].family == "Proefsma"'

Open the operator console on http://localhost:3000/ and sign in with any user name:

docker compose --profile console up --wait

The development issuer signs a token for any client that asks. It is for synthetic data on your own machine; docker compose --profile console down --volumes removes everything the tour started.

Deploy it

Every release publishes, from one tag:

  • the server image ghcr.io/ferrohealth/ferropix and the console image ghcr.io/ferrohealth/ferropix-console, for linux/amd64 and linux/arm64, on a distroless base;
  • the Helm chart oci://ghcr.io/ferrohealth/charts/ferropix, which runs both under the Restricted Pod Security Standard with the images pinned by the digests the release scanned;
  • static ferropix and ferropix-console binaries for x86_64 and aarch64 Linux on the GitHub release.

The server runs beside PostgreSQL 18, the authorization server whose tokens it reads, an ATNA audit repository and, for the HL7 v2 face, a FHIR R4 terminology server such as FerroTERM. Deployment covers the image, the chart and production settings, and Configuration every key.

Security and conformance

  • Who may call it: SMART access tokens from the issuers you trust, with the scope each transaction needs (Security and audit).
  • What it records: an audit record of every IHE transaction in the IHE BALP format, sent to your ATNA repository.
  • What you install: every artifact carries SLSA Build Level 3 provenance and an SBOM, and every finding of the release's vulnerability scan carries an OpenVEX judgement (Verifying an image).
  • Conformance: each conformance badge above links to its pass list: every example the IHE PIXm, PDQm and PMIR guides publish, with the reason for any that fails (Conformance and specification pins).

The source is public and every feature is in this repository; there is no paid edition with extra functions.

Crates

FerroPIX publishes the identity profiles it implements as Rust crates. Each IHE profile is a feature of ihe-iti and each Dutch Generic Function a feature of nl-generic-functions, none on by default:

cargo add ihe-iti --no-default-features -F pixm
Crate What it does Version Docs Downloads
ihe-iti The IHE identity profiles PIXm, PDQm, mCSD, PMIR and XCPD, one feature per profile, with their audit records crates.io docs.rs downloads
nl-generic-functions The Dutch Generic Functions: localization (NVI), consent (Mitz), addressing (LRZa) and authentication (Nuts) crates.io docs.rs downloads

Part of FerroHEALTH

FerroPIX is one of five FerroHEALTH servers, and the one that owns patient identity. Each server does one job and reaches the others only over published standards: FerroEHR stores the record and finds a patient's EHR through FerroPIX, FerroBRIDGE translates the record to FHIR and the OMOP research model, FerroFED connects records across organisations and resolves the patients of a cross-site query here, and FerroTERM serves the code systems. Any system that speaks PIXm, PDQm, PMIR, XCPD or HL7 v2 can use FerroPIX.

Documentation

Support and contributing

Licence

Business Source License 1.1 (LICENSE): free outside production and for non-commercial production use, a commercial licence from Cadasto B.V. for other production use, and Apache License 2.0 four years after each version. The licensing page says which applies to you.

About

A pure-Rust Master Patient Index for the openEHR platform: cross-identifier resolution and record location over IHE PIX and PDQ.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages