Skip to content

RTFM · Medical healthchecks

Medical checks: DICOM, HL7 and FHIR, explained

Hospitals run on three languages: DICOM moves the images, HL7 moves the messages, FHIR moves the records. Saphira's medical check package health-checks all three; with synthetic data only, and hard guard rails on anything that could change a real patient record.

Saphira Linux dragon mascot

Three languages, in plain words

When a radiographer presses the button on a CT scanner, the images travel to the hospital's archive (the PACS) speaking DICOM. When the booking system tells the radiology system that a patient has arrived, that message speaks HL7. When a modern app asks for a patient's allergies or medications, it speaks FHIR. A load balancer sitting in front of these systems must check them in their own languages; an HTTP port check would happily declare a PACS "healthy" while its DICOM door is jammed.

The trio at a glance
LanguageCarriesSaphira checks
DICOM (classic, TCP 104)Images and imaging work: echo, find, move, storedicom-echo · dicom-find · dicom-move · dicom-store
DICOMweb (HTTP)The same work over REST: query (QIDO), retrieve (WADO), store (STOW)dicom-qido · dicom-wado · dicom-stow
HL7 v2 over MLLPAdmission, discharge, orders; the message bus of the hospitalhl7-mllp
FHIR (HTTP)Structured clinical records for modern integrationsfhir

How a DICOM check talks to a scanner or PACS

Every DICOM conversation has two ends with names called AE titles: the SCU (service class user, the asker) and the SCP (service class provider, the answerer). The checks use the DCMTK toolkit's SCU tools (echoscu, findscu, movescu, storescu) to play the asking role, and every knob they need, which AE title to call as, which to call, which patient keys to query; is configuration in the same LB_SAPHIRA_* layer as the generic checks:

DICOM knobs (per-install, per-VIP overridable)
# $6/$7 convenience positionals or env or conf file:
LB_SAPHIRA_DICOM_AET=SAPHIRA-HC          # who we are (calling AE)
LB_SAPHIRA_DICOM_AEC=MODALITY-SCP        # who we call (called AE)
LB_SAPHIRA_DICOM_PATIENT_ID=SAPHIRA-SYNTHETIC-HEALTHCHECK
LB_SAPHIRA_DICOM_MODALITY=CT             # only sent when configured
LB_SAPHIRA_DICOM_QUERY_LEVEL=PATIENT     # PATIENT|STUDY

This matters for modality load balancing specifically: a CT suite from vendor A may require a specific called AE title and query level, and the per-service conf file (dicom.find/<service>.conf, service token = sanitized $1) lets each VIP carry its own. Neither HAProxy nor ldirectord needs a new interface for it; the configuration arrives through the same $1–$5 ABI and file layer as every other check.

Safety classes: cheap vs deep

Reading is cheap; writing changes the world. The medical checks are split by what they do to the systems they touch:

Safety classes
ClassBehaviourChecks
Cheap / continuous-safeRead-only synthetic transactions; safe at any check intervaldicom-echo · dicom-find · dicom-qido · dicom-wado · hl7-mllp · fhir
Deep / opt-in (guard-gated)State-changing: they store objects or trigger retrieves. Hard-refuse with exit 2 unless explicitly enableddicom-move · dicom-store · dicom-stow
The guard gate
# The guard: every state-changing check requires BOTH
LB_SAPHIRA_STATE_CHANGING=1
# AND its dedicated target (e.g. LB_SAPHIRA_DICOM_MOVE_DEST
# or LB_SAPHIRA_DICOM_STOW_URL). Missing either → exit 2.

Never enable the deep checks as high-frequency defaults. They exist to prove the full write path before a go-live or after maintenance; run them on demand or at long intervals against a dedicated health-check endpoint, never against a production archive at 5-second cadence.

The C-MOVE rule

A C-MOVE asks the PACS to send images somewhere else; the 'move destination' SCP. Running movescu alone proves nothing: the move instruction can succeed while the actual image transfer silently fails. The dicom-move check therefore requires a dedicated receiving SCP (for example a health-check storescp instance) configured as the move destination via LB_SAPHIRA_DICOM_MOVE_DEST, and fails closed without one. The move is only 'healthy' when the retrieve pipeline genuinely runs.

Synthetic data only: the privacy guarantee

No real patient ever appears in a Saphira health check. Query keys are synthetic (patient ID SAPHIRA-SYNTHETIC-HEALTHCHECK, a non-existent HL7 patient SAPHIRA-SYNTHETIC-PATIENT); stored objects are generated at check time by the packaged lb-saphira-dicom-synth.py; a minimal valid 2×2-pixel Secondary Capture whose PatientName is SAPHIRA^SYNTHETIC^HEALTHCHECK and whose UIDs are synthetic. No upstream sample files ship, so nothing resembling real patient data can leak into your estate through the health checks.

The HL7 MLLP check additionally asserts the ACK's MSA-1 field is AA or CA (accepted/committed-accept); a valid application-level acknowledgement, not merely a socket that answered. LB_SAPHIRA_HL7_MESSAGE can override the payload entirely, for sites whose HIS needs a specific query shape.

The nine checks

  1. 1. Continuous-safe first

    dicom-echo proves the association; dicom-find proves query; dicom-qido/wado prove the DICOMweb read paths; hl7-mllp and fhir prove the message and record planes.

  2. 2. Add the deep checks deliberately

    dicom-move, dicom-store, dicom-stow with the guard enabled, against dedicated targets, to certify the write path.

  3. 3. Balance on the cheap verdicts

    Let the balancers use continuous-safe checks for day-to-day routing decisions; schedule the deep ones for go-live proof.

Did we miss something?

If this page left something unanswered, found an error, or there is another subject you would like documented, tell us. Saphira’s documentation grows from real problems people need to solve.

Send feedback or request a new section →

Prefer not to do it yourself?

Everything needed to do the work yourself is documented here and remains free; we charge for human time, not for withholding knowledge. Sometimes the missing resource is simply time. The same people who build Saphira can provide paid professional help with implementation, migration, troubleshooting and administration.

Ask about professional support →