OpenHIM De-identification Mediator¶
OpenMed can run as an OpenHIM mediator that de-identifies FHIR JSON and plain-text transactions inside a health information exchange. The integration is feature-flagged and off by default. When enabled, the service registers with OpenHIM core at startup, sends an immediate heartbeat requesting current configuration, and continues heartbeats every 10 seconds.
Keep PHI inside the trust boundary
Deploy the mediator on infrastructure controlled by the national, regional, or facility HIE. Route identifiable transactions to OpenMed before any system outside that trust boundary. OpenMed does not send mediator payloads to OpenHIM's management API; registration and heartbeat calls contain only mediator metadata and uptime.
Registration payload¶
The example registration is in examples/openhim-mediator/mediator-config.json. It declares:
- URN
urn:openhim-mediator:openmed-deidentification - the default
/openmed/deidentifyOpenHIM channel - the mediator route
/openhim/deidentifyon port8080 - the endpoint displayed in the OpenHIM Console
OpenHIM core accepts mediator registration at POST /mediators and heartbeats at POST /mediators/:urn/heartbeat. The first heartbeat includes config: true; later heartbeats include uptime only. Registration is safe to call repeatedly across service restarts, while a running service instance suppresses duplicate registration attempts.
Configure the service¶
Set the following variables on the OpenMed service:
| Variable | Required | Default | Purpose |
|---|---|---|---|
OPENMED_OPENHIM_MEDIATOR_ENABLED | yes | false | Enables registration, heartbeat, and mediator routes. |
OPENMED_OPENHIM_CORE_URL | when enabled | none | OpenHIM management API URL, including its API port, such as https://openhim-core:8080. |
OPENMED_OPENHIM_USERNAME | when enabled | none | OpenHIM administrative API username. |
OPENMED_OPENHIM_PASSWORD | when enabled | none | OpenHIM administrative API password. Supply it through a secret store, not the JSON config. |
OPENMED_OPENHIM_AUTH_MODE | no | basic | Management API authentication: basic, or the deprecated OpenHIM token handshake for older deployments. |
OPENMED_OPENHIM_POLICY | no | hipaa_safe_harbor | Bundled policy applied to mediator payloads. Validated once at startup. |
OPENMED_OPENHIM_METHOD | no | replace | De-identification method applied to mediator payloads. Validated once at startup. |
OPENMED_OPENHIM_CONFIG_PATH | no | built-in config | Mounted mediator registration JSON. |
OPENMED_OPENHIM_VERIFY_TLS | no | true | Verifies the OpenHIM API certificate. Keep enabled outside isolated fixture tests. |
OPENMED_OPENHIM_ALLOW_INSECURE_HTTP | no | false | Allows a plaintext management URL only for the bundled synthetic fixture. Never enable it for a real HIE. |
OPENMED_OPENHIM_HEARTBEAT_INTERVAL_SECONDS | no | 10 | Heartbeat interval; accepted values are greater than zero and no more than 30 seconds. |
OPENMED_OPENHIM_REQUEST_TIMEOUT_SECONDS | no | 10 | Registration and heartbeat request timeout. |
The management credentials authorize mediator registration and heartbeat, so keep them out of mediator-config.json, source control, images, and logs. OpenHIM's management API uses HTTPS; OpenMed rejects plaintext management URLs unless the fixture-only override is explicitly enabled. Basic authentication is the default. Set OPENMED_OPENHIM_AUTH_MODE=token only when connecting to an older deployment configured for OpenHIM's deprecated salt-and-timestamp token headers.
The policy and method are deployment settings, not request parameters. Incoming query strings cannot weaken the configured de-identification posture. Change these settings only through the mediator deployment configuration and restart the service so startup validation runs again.
Run with an existing OpenHIM core¶
From the repository root, set the real management API and credentials, then start the example:
export OPENMED_OPENHIM_CORE_URL=https://openhim-core.internal:8080
export OPENMED_OPENHIM_USERNAME=mediator-admin@example.org
export OPENMED_OPENHIM_PASSWORD='use-your-secret-store'
docker compose -f examples/openhim-mediator/docker-compose.yml \
up --build openmed-mediator
The compose service mounts mediator-config.json read-only at /etc/openmed/mediator-config.json. If OpenHIM resolves the service under a different hostname, update host in both the channel route and endpoint before starting the mediator.
After registration, open the mediator in the OpenHIM Console and install the OpenMed De-identification default channel. The channel matches POST /openmed/deidentify on the OpenHIM router and forwards it to the mediator's /openhim/deidentify route.
Send a FHIR transaction¶
Send a Bundle through the OpenHIM router, not its management API:
export OPENHIM_CLIENT_USERNAME=openhim-client
curl --request POST https://openhim-core.internal:5000/openmed/deidentify \
--user "${OPENHIM_CLIENT_USERNAME}" \
--header 'Content-Type: application/fhir+json' \
--data @synthetic-bundle.json
With only the username supplied, curl prompts for the client password instead of placing it in the command or shell history.
For FHIR resources and Bundles, OpenMed de-identifies free-text and direct identifier values with openmed.interop.fhir_operations. Coded elements, systems, resource IDs, fullUrl values, request blocks, and references remain unchanged. Plain text/* bodies use the same OpenMed privacy pipeline. Other media types pass through byte-for-byte instead of being forced into a JSON envelope. Only response-safe representation and correlation headers are reflected; credentials, cookies, and unbounded custom clinical metadata are not copied into mediator response metadata.
Transformed responses use the required application/json+openhim media type. The envelope contains x-mediator-urn, the client response, and an orchestration step for the OpenHIM Console. The orchestration request intentionally omits the raw body, so identifiable input is not duplicated into transaction metadata. Errors return PHI-free messages.
Health and heartbeat¶
The existing /health, /livez, and /readyz routes retain their normal meanings. When mediator mode is enabled, GET /openhim/heartbeat reports local registration and heartbeat state without exposing credentials:
A healthy response has registered: true, a non-null last_heartbeat_at, and last_error: null.
Offline container smoke test¶
The compose file includes a small recorded handshake fixture, not OpenHIM core. It exists only to test registration and heartbeat without operating or bundling the real interoperability layer:
export OPENMED_OPENHIM_CORE_URL=http://openhim-fixture:8081
export OPENMED_OPENHIM_USERNAME=openhim@example.org
export OPENMED_OPENHIM_PASSWORD=synthetic-password
export OPENMED_OPENHIM_ALLOW_INSECURE_HTTP=true
docker compose -f examples/openhim-mediator/docker-compose.yml \
--profile fixture up -d openhim-fixture
docker compose -f examples/openhim-mediator/docker-compose.yml \
up -d --build openmed-mediator
curl http://127.0.0.1:8080/openhim/heartbeat
curl http://127.0.0.1:8081/health
docker compose -f examples/openhim-mediator/docker-compose.yml \
--profile fixture down --volumes --remove-orphans
The fixture uses synthetic credentials and payloads. Never use it as an HIE component. Unset those four variables before configuring a real OpenHIM deployment.