Skip to content
BioDream-LLCPublic

About

Open-source healthcare integration engine: HL7 v2, FHIR, DICOM, X12 and CDA in one static binary, all four CMS-0057-F payer APIs, tested with Inferno. An Apache-2.0 alternative to Mirth Connect.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Perfuse

Open-source healthcare integration engine — HL7 v2, FHIR, DICOM, X12 and CDA in one static binary

A modern, Apache-2.0 licensed alternative to Mirth Connect. No JVM. No installer. No paid licence. Your existing Mirth JavaScript runs unchanged.

31 MB of memory at rest, against Mirth's 383 MB — measured on the same machine, both idle. One file to copy instead of a 254 MB install and a Java runtime, and it starts immediately rather than warming up.

License Go Release Platforms Tests

Try Perfuse now: the live test instance, no install

Sign in as demo with password try-perfuse-demo · test data only · look at every screen, change nothing

Download v0.1.16

Download Perfuse for Linux, x86-64   Download Perfuse for macOS, Apple Silicon   Download Perfuse for Windows, x64

One file, nothing to install. Intel Macs, ARM64 Linux, ARM64 Windows and checksums

Quick start · Why Perfuse · Mirth migration · Everything it does · Manual · Reference · FAQ

perfuse.health — the manual, the reference and this page, on the web


What is Perfuse?

Perfuse is an open-source healthcare integration engine — an interface engine — for moving clinical data between systems that were never designed to talk to each other. A lab sends results in HL7 v2, an imaging department speaks DICOM, a payer sends X12, and the receiving system wants FHIR. Perfuse sits in the middle, parses each of them properly, transforms what needs transforming, and delivers without losing anything.

What it reads and writes: HL7 v2 · FHIR R4, R4B and R5 · DICOM and DICOMweb · X12 837, 835, 270/271, 276/277, 834 and 278 · SMART Health Links and Cards · International Patient Summary · IHE XDS/XDM documents · S/MIME Direct messages · C-CDA and CDA · HL7 v3 · NCPDP Telecommunication, SCRIPT (with ePA) and RTPB · delimited and raw formats.

How it connects: MLLP, TCP, HTTP, SOAP, files, FTP, SFTP, SMB, WebDAV, databases, DICOM, DICOMweb, Kafka, message brokers, AMQP 1.0 (Azure Service Bus, RabbitMQ 4), MQTT, IBM MQ, Azure Blob Storage, Google Cloud (Pub/Sub and Cloud Storage), AWS — S3, SQS and SNS — IHE XDS.b and XCA, Direct secure messaging, immunization registries (CDC IIS) and DICOMweb: twenty-six source types and twenty-nine destination types, in both directions.

What it does beyond moving messages. A durable on-disk queue that keeps ordering and survives a restart. Shadow mode, so a channel can be changed against real traffic while delivering nothing. Feed contracts that tell you when a sender quietly changes something. TEFCA and UDAP for exchanging records with organisations you have no direct connection to. The four CMS-0057 payer APIs and a Plan-Net provider directory, with claims as CARIN Blue Button, prior authorisations as Da Vinci PDex and the yearly metrics page. eCR public health case reporting. SQL on FHIR v2 (FHIR as tables, to Parquet), digital quality measures to DEQM MeasureReports, and an MCP server so AI agents can query it under the caller's own role. SAML, OpenID Connect, LDAP, passkeys, SCIM and mutual TLS for sign-on. Metrics, alerting, tracing, an audit log, and a searchable message store.

And it is one file. Written in Go, licensed under Apache 2.0, running on Linux, macOS and Windows with nothing installed beside it — no Java runtime, no application server, no database to stand up first. The web console is compiled into the binary, and everything the configuration files can express can also be done from the browser, start-up options included: each perfuse serve flag that turns on a feature is also a setting in the console, applied at the next restart. That is a rule this project holds itself to, not an aspiration: a feature reachable only from YAML or the command line is treated as a bug.

Perfuse sits between clinical systems: a lab system over MLLP, radiology over DICOM and a payer over X12 arrive on one side; Perfuse parses, filters, transforms, queues and delivers; an EHR over FHIR, a downstream system over MLLP and an archive over SFTP receive on the other.

New in v0.1.16

  • Drug prior authorization (CMS-0062-P): a formulary, the Da Vinci US Drug Formulary API, NCPDP RTPB real-time benefit checks and NCPDP SCRIPT ePA, all through the same decision hook as medical prior authorization.
  • AI agents over MCP: a built-in Model Context Protocol server at /mcp. Agents search messages, the FHIR store, queues and channels under the token's own role, channel groups and tenant; read-only by default, every call audited.
  • IHE document sharing: an XDS.b registry and repository, an XCA gateway, MHD, PIXm and PDQm, and an XDS destination (Carequality and eHealth Exchange style), tested against the NIST XDS Toolkit.
  • Direct secure messaging: S/MIME signed and encrypted mail with XDM packages and MDN receipts, certificates from an address book or DNS, an SMTP source; checked against OpenSSL in both directions.
  • DICOMweb: an imaging archive over QIDO-RS, WADO-RS and STOW-RS, browsed by OHIF; tested against Orthanc both ways.
  • Immunization registries: the CDC IIS web service as source and destination, VXU with ACK handling, QBP/RSP queries.
  • International Patient Summary: Patient/$summary, also used by Health links to share a visit.
  • SQL on FHIR v2: its own FHIRPath engine, ViewDefinitions run from the console, $sql-run and perfuse sof, to CSV, NDJSON or Parquet for DuckDB, Athena or Spark. Passes all 144 official tests.
  • Digital quality measures: FHIRPath Measures, Measure/$evaluate-measure and perfuse measure, giving MeasureReports that pass the Da Vinci DEQM 5.0.0 profiles. (Published CQL eCQMs need a CQL engine, which is not built yet.)
  • CMS Aligned Network readiness: each of the 26 CMS Interoperability Framework criteria and where this server stands.
  • Prior authorization decision services: -pas-decider-url (questionnaires and CDex attachment requests on a pend, a private CA with -pas-decider-ca) and -crd-advisor-url, which adds a decision service's advice as one more CRD card. Vaglio (source-available, PolyForm Shield) is the recommended engine; any service speaking the hook plugs in, and Perfuse never requires one.

Everything it does

Sixteen areas. Nothing here is aspirational: where something is partial it says so, and the verification record says what each claim was checked against. The manual covers every one in full.

HL7 v2
Parse, route, transform, generate, acknowledge
  • A parser that indexes rather than decodes: reading one field does not rebuild the message
  • Segments, fields, repetitions, components, subcomponents, Z-segments, non-standard separators and every escape sequence
  • MLLP both ways with TLS, plus TCP, HTTP and files
  • A field dictionary: "the patient's family name", not PID-5.1
  • AA / AE / AR and enhanced-mode acknowledgements, sent after delivery by default
  • perfuse generate for synthetic traffic, perfuse deident for a shareable corpus, send and listen for testing
FHIR
R4, R4B and R5: mapper, server, validator
  • HL7 v2 to FHIR: ADT, ORU, SIU, MDM and VXU to Patient, Encounter, Observation, DiagnosticReport, Appointment, DocumentReference and Immunization
  • US Core 9.0.0 (USCDI v6) claimed only after the resource is checked against the profile; no errors from the HL7 validator
  • A FHIR REST server: search, chains, _include, _has, transactions, history, Bulk Data $export
  • Subscriptions (R5 Backport for R4), recorded in the same transaction as the change
  • Write limits for a public server: writes a minute per client, and a resource cap
  • fhir as a destination; a browser FHIR lab; perfuse fhir to convert, validate and serve
  • SMART Health Links and Cards (Kill the Clipboard): read a patient's QR code, verify the cards, share the visit back
  • International Patient Summary: Patient/$summary builds an IPS 2.0.0 document from the store; empty sections say so rather than reading as none
  • SQL on FHIR v2: ViewDefinitions on its own FHIRPath engine, $sql-run and perfuse sof, to CSV, NDJSON or Parquet; passes all 144 official tests
  • Digital quality measures: FHIRPath Measures, Measure/$evaluate-measure and perfuse measure, to DEQM MeasureReports (summary, individual, subject-list; strata); no CQL engine
DICOM
The DIMSE services and DICOMweb, both directions
  • C-STORE, C-FIND, C-ECHO, C-MOVE and C-GET, sending and receiving
  • A DICOM query source: a scheduled C-FIND whose results become messages
  • DICOMweb: an archive served over QIDO-RS, WADO-RS, WADO-URI and STOW-RS for viewers such as OHIF; STOW-RS in and out of channels
  • De-identification as a pipeline step: patient removed, private tags stripped, AE title set
  • Metadata into tags a filter can read; studies onward as HL7 v2 or FHIR
  • Checked against Orthanc, by Orthanc's own catalogue
X12
Claims, payers and pharmacy, parsed as structures
  • 837 claims, 835 remittance, 270/271 eligibility, 276/277 claim status, 834 enrolment, 278 prior authorization
  • Interchange, group, transaction, loops and qualifiers understood; X12 to XML and back
  • 275 claims attachments in the 006020 form CMS-0053-F adopts, built and read
  • Da Vinci PAS to and from the X12 278, and 278 decisions to PAS ClaimResponses that pass the HL7 validator
  • 997 and 999 are separate, and Perfuse says so rather than pretending acknowledgement is synchronous
  • Pharmacy too: NCPDP Telecommunication claims and NCPDP SCRIPT prescriptions, with a missing substitution flag refused
Documents
C-CDA and CDA, including the ones inside v2
  • Read, validate and convert C-CDA and CDA, including documents base64-encoded inside an MDM^T02
  • The narrative is compared with the coded entries, and a document whose two halves disagree is flagged
  • Templates reported by name; repair of malformed documents; PDF rendering; merging several into one
  • HL7 v3, delimited and raw formats, read by element and attribute
  • Signed claims attachments (HL7 DSDR): XAdES-X-L with OCSP and RFC 3161 time-stamps, signed and verified; checked with xmlsec1 and OpenSSL
  • CDA and document destinations, and a browser lab
  • IHE XDS.b registry and repository, XCA gateway and MHD, PIXm, PDQm; an xds destination publishing CDA; checked with the NIST XDS Toolkit
  • Direct secure messaging through your HISP: S/MIME signed and encrypted, IHE XDM packages, MDNs, certificates from an address book or DNS CERT; checked with OpenSSL
Connectors
26 source types, 29 destination types
  • Sources: MLLP · TCP · HTTP · SOAP · file · FTP · SFTP · SMB · WebDAV · database · DICOM · DICOM query (C-FIND) · DICOMweb (STOW-RS) · Kafka · message broker (STOMP) · Amazon SQS · Amazon S3 · AMQP 1.0 (Azure Service Bus, RabbitMQ 4, ActiveMQ) · Azure Blob · MQTT · IBM MQ · Google Cloud Pub/Sub · Google Cloud Storage · Direct (from a HISP) · JavaScript Reader · serial
  • Destinations: MLLP · TCP · HTTP · SOAP · SMTP · file · FTP · SFTP · S3 (any storage class, Glacier included) · Amazon SQS · Amazon SNS · AMQP 1.0 · Azure Blob (Hot, Cool, Cold, Archive) · MQTT · IBM MQ · Google Cloud Pub/Sub · Google Cloud Storage · database · DICOM · DICOMweb · FHIR · CDA · IHE XDS.b · Direct secure messaging · document · JavaScript · Kafka · message broker (STOMP) · another channel
  • Databases: PostgreSQL, MySQL, SQL Server, Oracle, SQLite, the dialect checked when the channel is saved
  • Kafka keyed per patient, offsets committed after the batch; an SQS message deleted only once handled
  • AWS, Azure and Google Cloud without their SDKs; S3 archives as date-partitioned JSON for Athena
  • Files named from the message, written under a temporary name, appended or pruned after a set time
Transform
Declarative steps, or JavaScript or Lua
  • Eleven steps with no code: set, copy, remove, clear, default, case, date, pad, trim, replace, map, each with an optional when
  • Filters with &&, || and !, in the same language the message search uses
  • JavaScript, including the Mirth dialect with E4X, or sandboxed Lua; shared script libraries
  • Shared mapping tables with who decided each mapping and why; code sets and value-set lookups
  • AI Mapper: mapping suggestions with a confidence each, from rules on this server, with no model and no network call
  • Attachments moved out of a message and put back; an unmapped code is kept, a malformed date stops the message
Delivery
A durable queue that does not lose messages
  • An on-disk queue per destination: one slow receiver holds up nobody else, and a restart loses nothing
  • Order kept: message 2 waits until message 1 succeeds or is given up on
  • Exponential backoff, and dead letters that keep the message
  • Inspect, retry, skip and drain from the console; replay and reprocess from the stored original
  • Killed mid-batch five times: 255 acknowledged, 255 delivered, none lost, none duplicated
Security
Sign-on, roles and channel groups
  • SAML 2.0 (checked with Keycloak and Microsoft Entra), OpenID Connect, LDAP / Active Directory, passkeys, SCIM 2.0, mutual TLS
  • Six roles: monitor, viewer, operator, editor, admin, platform; API tokens for machines; an audit log
  • Channel groups for a hospital group on one server, with a different role in each group if needed
  • Directory groups map to any of the six roles and to channel groups at every sign-in
  • A SMART on FHIR authorization server: Inferno's SMART App Launch STU2.2 suite 80 of 80
  • An MCP server for AI agents: status, messages, queue, alerts and FHIR under a token's own role and groups, read-only unless granted, every call audited
  • No patient data over plain HTTP off loopback; cloud metadata addresses refused at connect time; PHI access watched for volume, after-hours and bulk reads
TEFCA
UDAP, Facilitated FHIR and its auditing
  • UDAP: discovery, dynamic client registration and signed metadata verification
  • Facilitated FHIR exchange between TEFCA participants, following the SOP
  • Every exchange audited and written to disk, with purpose-of-use rules
  • Partner discovery with certificate chain verification, checked against a real trust community
Prior auth
CMS-0057, and drugs for CMS-0062-P
  • Patient Access, Provider Access, Payer-to-Payer and Prior Authorization, with a screen saying which are ready
  • Da Vinci CRD 2.2.1 over CDS Hooks, DTR 2.2.0 questionnaire packages, and a PAS 2.2.1 server: $submit, $inquire, $decide
  • A reviewer queue for pended requests, with the documents sent for each
  • An optional decision service asked about what the rules leave to a reviewer (we recommend Vaglio, source-available; any engine plugs in); any failure pends, never approves
  • Claims to CARIN Blue Button 2.2.0, decisions to PDex 2.2.0, $member-match, $davinci-data-export, a Plan-Net directory
  • The yearly prior authorization metrics page in CMS's layout
  • Drugs: NCPDP SCRIPT ePA and Real-Time Prescription Benefit from one formulary, published over the Da Vinci US Drug Formulary 2.1.0 API; decided through PAS
  • Inferno: CRD passes, DTR 43/45, PAS 82/84, PDex payer-to-payer 19/19, Bulk Data 48/48
  • CMS Aligned Network readiness: all 26 Interoperability Framework criteria, each read from what is switched on, with what is yours to do
Public health
eCR, ELR and immunization registries
  • eCR 2.1.2: an eICR for each message with a reportable-condition trigger code, from the RCTC you load
  • Reportability Responses received at $process-message, and a test agency to try the round trip
  • ELR: a lab's ORU^R01 reshaped into HL7 2.5.1 ELR with only the reportable orders; ONC's tool reports all 28 of its examples valid
  • Immunization registries on the CDC IIS web service: VXU and QBP submitted, the registry's ACK judged, and a registry endpoint to test against
Live changes
Shadow mode, feed contracts, drift
  • Shadow mode: a candidate channel runs beside the live one on real traffic, delivering nothing, and every difference is shown field by field
  • Feed contracts: what a feed should contain, and an alert when the sender drifts
  • perfuse profile says what is actually in a feed; perfuse compare proves two engines agree
  • perfuse test runs a channel's tests in CI; channel history read from git
Operations
Dashboards, alerts, search, fleet, tenants
  • A live dashboard, a flow map with a time scrubber, and team dashboards (operations, connections, prior auth, imaging, revenue cycle, lab, privacy), exportable to Grafana
  • Eleven alert kinds, including below rhythm: a feed quieter than its own history
  • Prometheus metrics, OTLP tracing, and VPN tunnel state from AWS, Azure or strongSwan
  • Find a patient's messages by MRN, name, birth date, accession or claim number across HL7 v2, FHIR, DICOM and X12, every search audited
  • A fleet view of several servers, and multi-tenancy with limits per tenant
  • Everything in the browser: 28 screens, three themes, a ⌘K palette, settings applied with a restart button
  • One static binary as a systemd, launchd or Windows service; perfuse init writes it
Mirth
Both directions: Mirth 4.5.2, OIE, BridgeLink
  • Existing Mirth JavaScript runs unchanged: E4X, channelMap, $(), DateUtil, SerializerFactory
  • perfuse explain says by name what converts, what converts with caveats and what does not
  • perfuse translate converts channels, groups, code template libraries or a whole server backup
  • Export back: Mirth, OIE 4.5.2 and 4.6.0, and BridgeLink 26.9.0 all accept the channel files Perfuse writes
  • A lock-in audit of the Java your channels depend on
Developers
An API for everything, and Go libraries
  • Every screen is the REST API underneath, with API tokens for machines
  • github.com/biodream-llc/perfuse/hl7: the HL7 v2 parser as a standalone library
  • …/perfuse/fhir (the FHIR model) and …/perfuse/pas (the PAS request reader), standard library only
  • perfuse sbom lists everything linked in; every release ships an SBOM and checksums

How small it is

Measured on one machine, both products idle, then Perfuse again after real traffic. Not a throughput benchmark — just what each one costs you to have running.

Perfuse Mirth Connect 4.5.2
Memory at rest 31 MB 383 MB
Memory after 5,000 messages 46 MB —
CPU when idle 0.0% 0.1–0.3%
What you install one file a 254 MB application plus a JVM
Runtime to install first none OpenJDK 17
Processes 1 JVM + application server

About twelve times less memory at rest, and nothing to install underneath it. Mirth's figure is the JVM's resident size and is configurable; the point is not that the number is fixed but that there is a JVM there at all.

Download size: ~20 MB per platform. Binary: 61 MB, of which 26 MB is the in-browser playground — so the engine, every connector, the parser and the whole web console together are about 35 MB.

Startup is immediate rather than a warm-up: no JVM to boot, no JIT to reach steady state, no application server to deploy into. Restarting a channel is not an event you schedule.

Who it is for

  • Integration teams running HL7 v2 interfaces who want the engine's source and no licence renewal
  • Sites on Mirth Connect 4.5.2 or earlier, now unable to take upstream fixes
  • Anyone building FHIR APIs on top of existing v2 feeds
  • Labs, imaging centres, payers and health systems who need X12, DICOM or CDA alongside v2

Try it without installing anything

test.perfuse.health is a public Perfuse with test data only. Sign in to the console with username demo and password try-perfuse-demo: it can look at every screen and change nothing. Its FHIR, CRD, DTR, PAS, Bulk Data and SMART endpoints are the ones Inferno's hosted test kits run against; see deploy/test-instance.

Download

Version 0.1.0 — single binary, nothing to install. Each archive contains the executable, the licence, the notice and the full PDF manual.

Platform Architecture Download
Windows x64 (Intel/AMD) perfuse-v0.1.16-windows-amd64.zip
Windows ARM64 perfuse-v0.1.16-windows-arm64.zip
macOS Apple Silicon (M1–M4) perfuse-v0.1.16-darwin-arm64.tar.gz
macOS Intel perfuse-v0.1.16-darwin-amd64.tar.gz
Linux x86-64 perfuse-v0.1.16-linux-amd64.tar.gz
Linux ARM64 / aarch64 perfuse-v0.1.16-linux-arm64.tar.gz

On Windows, unzip and double-click perfuse.exe. It starts the server, keeps its database and channels in %LOCALAPPDATA%\Perfuse, and opens your browser at the sign-in page. The window it opens shows the administrator's password on the first run; keep it open while you use Perfuse, because closing it stops the server. Double-clicking again while it runs just opens the browser. From a command prompt, perfuse with no arguments still prints the usage.

Verify what you downloaded against SHA256SUMS:

shasum -a 256 -c SHA256SUMS --ignore-missing     # macOS / Linux

A CycloneDX software bill of materials ships with every release as perfuse.cdx.json, and any build can produce its own with perfuse sbom -json.

Other ways to install
# Go 1.23 or newer
go install github.com/biodream-llc/perfuse/cmd/perfuse@latest

# Container — the Dockerfile is scratch-based, nothing inside but the binary
docker build -t perfuse . && docker run -p 8080:8080 perfuse

# From source
git clone https://github.com/biodream-llc/perfuse && cd perfuse && make build

The Linux binaries are statically linked and verified to run on Alpine, so they need no glibc and work in scratch and distroless images.


What it looks like

Everything below is the interface that ships inside the binary. There is no separate application to deploy.

Dashboard — live channel state and the last 24 hours, with the share of traffic that went wrong readable at the same time as the total.

The Perfuse dashboard showing 5,000 messages in 24 hours, no failures, channels running, a throughput graph and a breakdown by HL7 message type.

Messages — every message that arrived, what happened to it, and the bytes as they were sent. The filter box takes the same expression language channels use, so anything that works here can be pasted straight into a channel.

The message browser, with search by channel, outcome, type and control ID, a filter expression box, and a table of delivered messages with their timings.

Flow map — every feed and destination on one page, with a scrubber. Drag it back to a moment and see which strands were dark.

The flow map showing four channels, each with its destinations and a sparkline of traffic, and a scrubber for moving back through the last hour.

Channels — each channel is a file on disk. Anything built in the browser can be edited by hand, and the other way round. To Mirth writes a Mirth channel file that a real Mirth server accepts.

The channel list, showing each channel with what it listens on, when it acknowledges, where it sends, and the file it lives in.

Metrics — volumes, timings and error rates over time, with percentiles, exposed to Prometheus as well.

The metrics view showing message volumes, delivery timings and error rates over time.

FHIR lab — paste HL7 v2, see the FHIR it becomes, and validate it. The same conversion the fhir destination type uses.

The FHIR lab, converting an HL7 v2 message into FHIR resources and validating the result.

Quick start

# 1. Start the console. The web interface is inside the binary.
perfuse serve

# 2. Open http://127.0.0.1:8080 and build a channel in the browser.

That is the whole installation. There is no separate web application to deploy and no assets directory to point at.

perfuse init will lay out the directories, write a working example channel and generate a service definition for your operating system — a systemd unit, a launchd plist or a Windows service.

To run a channel from the command line instead:

# Listen for HL7 v2 over MLLP and write each message to a file
cat > labs.yaml <<'YAML'
name: labs
description: Accepts HL7 v2 over MLLP and files each message

source:
  type: mllp
  # Localhost only. MLLP has no authentication or encryption of its own.
  listen: 127.0.0.1:2575

destinations:
  - name: archive
    type: file
    # One file per day per message type, appended. So five ADT messages of two
    # kinds produce two files, not five.
    dir: ./received
YAML

# Check it before running it
perfuse check labs.yaml

# Run it
perfuse run labs.yaml

# In another terminal, send it synthetic traffic
perfuse generate -n 5 -send 127.0.0.1:2575

Everything in the configuration files can also be done from the web interface. That is a rule this project holds itself to, not an aspiration — a feature reachable only from YAML is treated as a bug.


Why Perfuse exists

In March 2025, NextGen Healthcare closed Mirth Connect's source. From version 4.6 a paid licence is required. Version 4.5.2 remains available under the Mozilla Public License, which means every site still running it is on a release that will never receive another upstream fix — including security fixes.

That leaves a real problem. Mirth Connect runs a very large share of the HL7 interfaces in production today, those interfaces work, and the people who maintain them did not choose this. Rewriting them is not a weekend's work, and paying to keep receiving patches for software you already deployed is a different proposition from the one you originally accepted.

Perfuse is a way out that does not require a rewrite:

  • Your Mirth JavaScript runs unchanged, E4X and all — msg['PID']['PID.5']['PID.5.1'], for each, XML literals, channelMap, $(), DateUtil, SerializerFactory
  • perfuse translate converts Mirth channel exports into Perfuse channels, not just a report saying what it found — a whole server backup at once, code template libraries and channel groups included
  • The same for the forks. Sites that moved to the Open Integration Engine or BridgeLink rather than pay can come across the same way; both are tested
  • perfuse explain reads a Mirth export and says plainly what would block a migration, before you start
  • Shadow mode runs the converted channel beside the original on real traffic and shows exactly where the two disagree — while only the original delivers anything
  • You can export back to Mirth. A channel built in Perfuse can be written as a Mirth channel file, and a real Mirth server accepts it. Leaving is as supported as arriving.

The last point is deliberate. An engine you cannot leave is the problem, not the solution.


Verified against real software, not mocks

Most integration tooling is tested against its own idea of what the other side does. That is how a product ends up with a hundred passing tests and a feature that has never once worked against the real thing.

Perfuse is verified against actual running software. What was checked, against which versions, and what it found is recorded in docs/verification.md:

Verified against What it proved
Mirth Connect 4.5.2 (real server) Channels Perfuse exports are accepted and deploy. Found four faults the exporter's own nine passing tests could not see — Mirth stores an invalid channel and returns success
Open Integration Engine 4.5.2 and 4.6.0, BridgeLink 26.9.0 (real servers) Each wrote its own channels, code template library, group and server backup; every one translates, loads and runs. Found four importer defects, among them a Mapper variable that never reached the script reading it
Keycloak 26 SAML sign-in end to end. Found a signature-canonicalisation defect a thousand self-written tests had missed
Microsoft Entra ID (real tenant) SAML sign-in with a real Microsoft account, including an interactive browser sign-in. Found five behaviours no reading of the specification predicts
HAPI FHIR FHIR resources Perfuse produces validate in an independent server
Orthanc DICOM C-STORE and C-FIND, and DICOMweb both ways (STOW-RS into Orthanc; Orthanc's DICOMweb client searching and retrieving from Perfuse)
OHIF Viewer listing and displaying studies from Perfuse's DICOMweb archive
NIST XDS Toolkit XDS.b ITI-41/18/43 against its Registry/Repository, and its XCA Initiating Gateway against Perfuse's Responding Gateway
PostgreSQL, SFTP, ActiveMQ Database, file transfer and broker destinations against real services
The FHIR R4 specification's own examples All 2,912 example files the standard's authors published. 13,723 resources validated, none reported wrongly. Found a bundle whose invalid content was being reported as a missing feature, exiting zero under -strict
The HAPI HL7 v2 test corpus 59 of the reference Java implementation's own awkward messages — uuencoded payloads, escaped delimiters, repeating groups — all parsed
SIGKILL, mid-batch 255 messages acknowledged across five hard kills, 255 present downstream, none lost and none duplicated. The numbers for the weaker acknowledgement mode are published too
Inferno, ONC's test kits (hosted, against test.perfuse.health) SMART App Launch STU2.2 80/80 · Bulk Data 2.0 48/48 · PDex 2.0 payer-to-payer 19/19, coverage 312 + 1 skip · Da Vinci CRD Server 2.2.1 passes · PAS Server 2.2.1 82/84 · DTR Payer Server 2.2.0 43/45 · Plan-Net 1.1.0 199 pass, 10 fail · US Core 6.1.0 FHIR API 323 passing (locally). Every failure left is explained in the record
The HL7 validator, on other people's v2 Microsoft FHIR-Converter's 139 samples, 81 French national and NHS Wales samples, the NHS nw-gmsa set and messages posted on chat.fhir.org, converted and validated as whole bundles. What is left are codes the senders sent

Interoperability tests run against containers, not stubs. Where a real product's behaviour contradicted the specification, the real behaviour won and a fixture recording it was committed.



Migrating from Mirth Connect

# 1. What would stop you, before you commit to anything
perfuse explain server-backup.xml          # Mirth, OIE or BridgeLink; a channel, group or whole backup

# 2. Convert it, code template libraries included
perfuse translate server-backup.xml -o channels/

# 3. Test it against sample messages
perfuse test channels/labs.yaml

# 4. Run it beside Mirth on your real traffic, delivering nothing to anyone
perfuse run channels/labs.yaml

# 5. Prove the two engines agree, on your own messages
perfuse compare -left-name Mirth -right-name Perfuse mirth-out/ perfuse-out/

perfuse explain is the step worth running first. It reads the export and tells you what converts cleanly, what converts with caveats, and what does not convert at all — by name, not as a count. A migration that surprises you in week three is worse than one that refuses on day one.

What does not convert is reported, never silently approximated. Perfuse will refuse a destination type it cannot honour rather than write something close and let you discover the difference in production.

Running it beside Mirth without touching production

Step 4 needs no change to what Mirth delivers. Add one destination to the existing Mirth channel that forwards a copy to Perfuse, and give the Perfuse channel a directory as its only destination:

name: parallel-run
description: Receives a copy of live traffic from Mirth and files the result. Sends nothing onward.

source:
  type: mllp
  listen: 127.0.0.1:2576

destinations:
  # The only destination is a directory, so this channel cannot reach a downstream
  # system even if it is misconfigured. There is nothing else for it to send to.
  - name: what-perfuse-produced
    type: file
    dir: ./perfuse-out

Then perfuse compare pairs the two directories by MSH-10 and reports where they disagree, grouped by cause — so three thousand messages differing for one reason are one finding, not three thousand. Message content is withheld by default, because a report is a thing people paste into tickets. -strict exits non-zero unless the two are identical, which is what makes it usable in a pipeline.

This is the evidence a migration actually turns on, and it costs you nothing but a directory. Nobody has to approve a parallel run that delivers to no one.

Shadow mode is a different tool, for later. It compares a candidate Perfuse channel against a live Perfuse channel, which is how you change a channel safely once you have cut over. It is not how you compare Perfuse against Mirth — perfuse compare is.

Full detail: Migrating from Mirth · Mirth scripts


Perfuse vs Mirth Connect

Perfuse Mirth Connect 4.6+
Licence Apache 2.0, open source Commercial, paid
Source available Yes No (closed since 4.6)
Runtime Single static binary JVM + application server
Install Copy one file Installer, Java, database setup
Memory at rest 31 MB (measured) 383 MB (measured)
Existing Mirth JavaScript Runs unchanged, E4X included Native
FHIR R4/R4B/R5, mapper + REST server built in Add-on / manual
DICOM Built in, both directions Limited
X12 837, 835, 270/271 parsed structurally Text handling
CDA narrative vs coded comparison Yes No
Shadow mode on live traffic Yes No
Feed contracts / drift detection Yes No
Export back to the other engine Yes — writes Mirth channel files a real Mirth accepts N/A
Kafka Source and destination, keyed for per-patient ordering No connector
IHE XDS.b / XCA, Direct, DICOMweb Built in Not provided
SQL on FHIR, quality measures ViewDefinitions to Parquet; FHIRPath Measures to DEQM MeasureReports Not provided
AI agents (MCP) Built-in MCP server under the caller's role Not provided
CMS-0057 and CMS-0062 payer APIs Patient Access, Provider Access, Payer-to-Payer, Prior Authorization, drug prior authorization (RTPB, SCRIPT ePA, Drug Formulary); CARIN BB and PDex output validated by the HL7 validator Not provided
Configuration from the web UI Everything Most things
Multi-server view Fleet view, included A paid feature
Published crash-consistency results Killed mid-batch and counted: 255 acknowledged, 255 delivered None published

Perfuse is younger and has a smaller connector catalogue. Where a capability is missing it says so rather than approximating it.


Measured against an independent gap analysis

Someone designing a Mirth replacement published a competitive capability analysis: 68 candidate features surveyed against 25 engines — Rhapsody, Cloverleaf, Corepoint, InterSystems IRIS for Health, Smile CDR, HAPI FHIR, MuleSoft, Boomi, Kafka, Apache Camel, NiFi, Temporal, Redox and others — each verified rather than assumed, producing 35 confirmed gaps ranked by priority.

It is not about Perfuse. That is what makes it useful: it is an outside view of what a modern interface engine ought to have, written without reference to this one. Perfuse checked against its top ten:

Their recommendation Perfuse
#1 Silent-interface / SLA / heartbeat alerting — "deadliest failure mode, cheapest win" Has it — eleven rule kinds, evaluated on a 30s schedule over a 5m window
#2 HA / clustering / failover Fleet view across instances
#3 Keyed partitioning / per-patient ordering Stricter than proposed — once anything queues for a destination, everything for it goes behind, and it is not configurable
#4 End-to-end lineage + tracing Flow map with a scrubber, and OTLP export
#5 Interface test framework in CI perfuse test
#6 Config-as-code / GitOps Channels are YAML files in a directory — native, no plugin
#7 FHIR validation + bulk $export Both built in
#8 AI-assisted mapping A mapping engine with confidence scores — rules-based, no model, no network call
#9 Aggregator, wire-tap, DLQ Shadow mode, queue retry, reprocess, replay
#10 OAuth2 credential vault Partial — UDAP client-credentials for TEFCA

Two of those deserve a caveat rather than a tick. The OAuth2 row is genuinely partial: there is no general credential vault, only the TEFCA path. And #3 is a different trade rather than a better one — the strict rule costs throughput while a queue drains, which was accepted because the faster alternative is silently wrong.

The one clear gap was Kafka, and this release closes it. The report's own advice for the whole streaming category was to ship a Kafka connector rather than build streaming internals, so that an engine becomes the healthcare-aware edge of an event platform instead of competing with one. That is exactly what the connector is: no windowing, no joins, no schema registry, no stream processing. Those stay in Kafka's ecosystem.


Documentation

Reference Every command, connector, transformation step and configuration key
Manual The full book, with worked examples. Also docs/manual/perfuse-manual.html in a checkout, in every release archive, and served at /manual by a running instance
Manual (PDF) The same, for printing and for aeroplanes
Verification record What was tested against which real software, what it found, and what remains unverified
Security TLS, authentication, audit and what is deliberately refused
HL7 Go API Using the parser as a library in your own Go programs

The manual is also served by the running instance, so an operator never has to find this page.


Using Perfuse as a Go library

The HL7 v2 parser is a standalone package with no dependency on the engine:

import "github.com/biodream-llc/perfuse/hl7"

msg, err := hl7.Parse(raw)
if err != nil {
    return err
}

// Path lookup returns an error for a path that is not valid, rather than an
// empty string that could equally mean "absent" or "you typed it wrong".
family, err := msg.Get("PID-5.1")     // patient's family name
if err != nil {
    return err
}

for _, obx := range msg.Segments("OBX") {
    fmt.Println(obx.Field(3).Component(2).String(), obx.Field(5).String())
}

It indexes rather than decoding, so reading one field does not rebuild the message. API documentation.

Two more packages are published on the same terms (standard library only, no engine), so that software working beside Perfuse reads the same data the same way:

  • github.com/biodream-llc/perfuse/fhir: the FHIR resource model, R4 and R5 serialisation, and validation.
  • github.com/biodream-llc/perfuse/pas: the Da Vinci PAS request reader Perfuse's own PAS server uses: the Claim, its services and their codes, and reference resolution inside the Bundle. A decision service behind -pas-decider-url reads requests with it.

FAQ

Is Perfuse really free? What is the licence?

Yes. Apache License 2.0 — commercial use, modification, distribution and private use are all permitted, with no fee and no per-channel or per-interface pricing. See LICENSE and NOTICE.

Will my existing Mirth channels work?

Mirth JavaScript transformers and filters run unchanged, E4X included. Channel exports are converted with perfuse translate. Run perfuse explain on your export first — it tells you by name what will and will not convert. Anything that cannot convert is reported rather than approximated.

Can I go back to Mirth if I change my mind?

Yes. A channel built in Perfuse can be exported as a Mirth channel file, and this is verified against a real Mirth server rather than against our own parser. An engine you cannot leave is the problem, not the solution.

Do I need Java, a database or an application server?

No. One static binary. It uses an embedded database by default and PostgreSQL if you point it at one. The web console is compiled into the executable.

Is it HIPAA compliant?

Compliance is a property of a deployment, not of software, so no product can honestly claim it on your behalf. What Perfuse provides: TLS with mutual authentication, an audit log of who changed what, role-based access control, message-store retention limits, de-identification steps, and a refusal to serve patient data over plain HTTP on a public interface unless you explicitly override it. The security chapter is written for the person who has to sign off on it.

Is this production ready?

The engine, queue, transports and web interface are complete and tested, and the parts that talk to other people's software are verified against that software rather than against mocks. Use shadow mode. Run it beside what you have, on real messages, delivering nothing, and read the differences for yourself. That feature exists precisely because you should not take this paragraph's word for it.

What is missing?

A smaller connector catalogue than Mirth's, no clustering yet, and no plugin ecosystem. Filters, transformations, scripts, contracts, shadow comparisons and mapping tables do not survive a round trip out to Mirth and back — each is reported when it cannot be converted. Missing capabilities are refused by name rather than approximated.

How do I get help or report a bug?

Open a GitHub issue. Include the output of perfuse version and, if a message is involved, a de-identified sample. There is also a friction report built into the interface that records where this installation refused you and how often — that is often the fastest way to describe a problem.


Contributions are not accepted

This is deliberate, and it is not a judgement about anybody's code.

Perfuse is developed and maintained by BioDream LLC, and it stays that way. Pull requests will not be reviewed or merged. There is no contributor agreement to sign because there is no route for a contribution to arrive.

The reason is the subject matter. This software moves clinical data, and the discipline that makes it worth trusting is that every behaviour is verified against real software rather than against its own assumptions, and that anything which cannot be honoured is refused by name instead of approximated. That standard is easy to state and slow to enforce across contributors, and a half-enforced standard in a healthcare integration engine is worse than no standard at all.

Bug reports are wanted. Open an issue with the output of perfuse version and, if a message is involved, a de-identified sample. A clear report of something that does not work is more valuable here than a patch.

What the licence permits regardless

Apache 2.0 gives you the right to use, modify and redistribute this software, and closing this repository to contributions does not take that away. You may take a copy, change it to suit you, and run or publish the result, commercially or otherwise. What is closed is the path back into this repository — not your ability to do what you like with your own copy. That is the arrangement, and it is the point of the licence.

Building it yourself

make check runs formatting, vet, the race detector, cross-compilation, type checking and the unit suites. make e2e runs the browser tests. docs/releasing.md covers cutting a release and lists what the current one could not verify.


Licence

Apache License 2.0. Copyright 2026 BioDream LLC. See NOTICE.

Perfuse is not affiliated with, endorsed by, or derived from Mirth Connect or NextGen Healthcare. HL7 and FHIR are registered trademarks of Health Level Seven International; DICOM is a registered trademark of NEMA. Those marks are used only to describe what this software reads and writes.

Keywords

Category: healthcare integration engine · interface engine · integration engine · healthcare middleware · clinical data integration · health data integration · healthcare interoperability · medical data integration · healthcare ESB · clinical integration platform · health information exchange · HIE · HIE software · healthcare data pipeline · clinical message router · medical interface engine · EHR integration · EHR integration engine · hospital integration engine · healthcare API gateway

Mirth: Mirth Connect alternative · Mirth Connect replacement · Mirth alternative · Mirth replacement · open source Mirth · Mirth Connect open source · Mirth Connect fork · Mirth migration · migrate from Mirth · Mirth Connect 4.5.2 · NextGen Connect alternative · NextGen Connect replacement · Mirth channel converter · Mirth JavaScript · Mirth E4X · Mirth channel import · Mirth channel export · Mirth Connect licence · Mirth Connect pricing · free Mirth alternative

HL7 v2: HL7 · HL7 v2 · HL7 version 2 · HL7 v2.x · HL7 parser · HL7 v2 parser · Go HL7 library · HL7 interface engine · HL7 router · HL7 transformer · HL7 ADT · HL7 ORU · HL7 ORM · HL7 MDM · HL7 SIU · HL7 DFT · HL7 ACK · HL7 Z-segment · HL7 escape sequences · MLLP · MLLP server · MLLP client · MLLP over TLS · HL7 2.3 · HL7 2.5.1 · HL7 2.7 · HL7 2.8 · HL7 message store · HL7 replay · HL7 de-identification

FHIR: FHIR · FHIR server · FHIR R4 · FHIR R4B · FHIR R5 · FHIR REST API · HL7 to FHIR · HL7 v2 to FHIR · v2 to FHIR converter · FHIR mapper · FHIR validator · FHIR bulk export · FHIR search · FHIR transaction bundle · FHIR facade · SMART on FHIR · US Core · FHIR Patient · FHIR Observation · FHIR DiagnosticReport

DICOM: DICOM · DICOM router · DICOM gateway · C-STORE · C-FIND · C-ECHO · C-MOVE · C-GET · DICOM SCP · DICOM SCU · PACS integration · DICOM de-identification · DICOM anonymisation · radiology integration · imaging integration · DICOM to HL7 · DICOM to FHIR · DICOM query retrieve · modality worklist · DICOMweb · QIDO-RS · WADO-RS · STOW-RS · OHIF

X12 and revenue: X12 · X12 837 · X12 835 · X12 270 · X12 271 · X12 278 · X12 parser · EDI healthcare · healthcare EDI · claims integration · remittance advice · eligibility verification · prior authorisation · prior authorization · CMS-0057 · Da Vinci PAS · payer integration · revenue cycle integration · claims 837 · ERA 835

Documents: C-CDA · CDA · CCDA validator · CDA parser · clinical document architecture · CCD · discharge summary · MDM T02 · document integration · CDA to FHIR · clinical document exchange · CDA narrative validation · IHE · XDS

Exchange: TEFCA · TEFCA participant · UDAP · UDAP dynamic client registration · Facilitated FHIR · QHIN · Carequality · Commonwell · national health information exchange · trusted exchange framework · record locator · patient discovery · cross-organisational exchange · IHE XDS · XDS.b · XCA · MHD · PIXm · PDQm · document registry · document repository · ITI-41 · ITI-18 · ITI-43 · ITI-38

Public health: eCR · electronic case reporting · ELR · electronic lab reporting · public health reporting · reportable conditions · syndromic surveillance · immunisation registry · notifiable disease reporting

Security: SAML · SAML 2.0 · SAML single sign-on · OIDC · OpenID Connect · LDAP · Active Directory · passkeys · WebAuthn · FIDO2 · SCIM · SCIM 2.0 · mutual TLS · mTLS · client certificate authentication · role-based access control · RBAC · audit log · PHI audit · HIPAA audit trail · HIPAA integration engine · healthcare single sign-on · Keycloak SAML · Microsoft Entra SAML · Okta SAML · ADFS SAML · API tokens

Operations: durable queue · message queue · guaranteed delivery · dead letter queue · message retry · shadow mode · canary deployment · feed contracts · schema drift detection · interface monitoring · HL7 monitoring · Prometheus metrics · OpenTelemetry · OTLP tracing · distributed tracing · alerting · message search · message replay · multi-tenant · fleet management · flow map · interface dashboard

Transports: SFTP · FTP · SMB · WebDAV · S3 · SMTP · SOAP · STOMP · ActiveMQ · message broker · PostgreSQL · MySQL · SQL Server · Oracle · SQLite · database integration · file drop · serial port

Deployment: single binary · static binary · no JVM · no Java · Go · Golang · Apache 2.0 · open source · self-hosted · on-premise · air-gapped · Docker · container · Kubernetes · systemd · launchd · Windows service · Linux · macOS · Windows · ARM64 · cross-platform · lightweight · low memory footprint · fast startup · free healthcare software · no licence fee

About

Open-source healthcare integration engine: HL7 v2, FHIR, DICOM, X12 and CDA in one static binary, all four CMS-0057-F payer APIs, tested with Inferno. An Apache-2.0 alternative to Mirth Connect.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages