Perfuse is an open-source healthcare integration engine under Apache 2.0. One static binary, no Java runtime, and 31 MB of memory at rest against Mirth Connect's 383 MB.
Mirth Connect works, and it has run interfaces at thousands of sites for the better part of twenty years. The reasons teams start looking elsewhere are usually not about whether it functions:
| Perfuse | Mirth Connect | |
|---|---|---|
| Memory at rest | 31 MB | 383 MB |
| Idle CPU | 0.0% | 0.13–0.27% |
| Runtime required | None. One static binary | OpenJDK 17 |
| Install | Copy one file | 254 MB installer plus the JRE |
| Licence | Apache 2.0, no tiers | Commercial only since 4.6 |
| Existing Mirth JavaScript | Runs unchanged, including E4X | Native |
| Configuration in the browser | Everything, as a rule the project holds itself to | Most things |
| HL7 v2, FHIR, DICOM, X12, CDA | All, in the one binary | HL7 v2 and FHIR core; others via extensions |
| Kafka | Source and destination, keyed for per-patient ordering | No connector |
| Find a patient across formats | One box — MRN, name, date of birth, accession — across HL7 v2, FHIR, DICOM and X12 | Search by metadata, or by hand per format |
| Production track record | None yet. v0.1.3 | Twenty years, thousands of sites |
That last row is the one that should decide your evaluation, not the first. Perfuse v0.1.3 has never processed production clinical traffic. The Windows and ARM64 Linux binaries have never been executed at all. Everything that was verified is written down, and so is everything that was not — the record is here. Mirth has two decades of operational history and Perfuse has none. Weigh that honestly.
Mirth transformers are JavaScript, and a great many of them use E4X — the XML-as-a-language-feature
syntax that lets a script write msg['PID']['PID.5']['PID.5.1']. E4X was removed from modern
JavaScript engines years ago. Any migration that involves "just move the JavaScript somewhere else"
discovers this on the first transformer.
Perfuse runs E4X. That is the single reason a Mirth migration is tractable here rather than a rewrite of every transformer you own.
# 1. What would stop you, before you commit to anything
perfuse explain mirth-channel-export.xml
# 2. Convert it
perfuse translate mirth-channel-export.xml -o channels/
# 3. Test it against sample messages
perfuse test channels/labs.yaml
# 4. Run it beside Mirth on 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 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 cannot be honoured is refused rather than approximated. Perfuse will decline a destination type it cannot implement properly instead of writing something close and letting you find the difference in production.
Add one destination to your 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.
- name: what-perfuse-produced
type: file
dir: ./perfuse-out
Nothing is delivered, so no patient is affected and no change-control board needs to meet.
perfuse compare pairs the two directories by message control id and reports where the engines
disagree, grouped by cause — three thousand messages differing for one reason are one finding, not
three thousand. A filter disagreement is counted separately from a transformation difference, because one
decides whether the receiving system hears about that patient at all and the other changes a value.
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 makes it usable in a pipeline.
No, and nothing honestly is. It converts your channel exports, runs your JavaScript including E4X, and tells you by name what it cannot carry across. That is as close as an honest answer gets.
Nothing. Apache 2.0, no commercial tier, no seat count, no feature held back for a paid edition. If you later want support, that is a separate conversation and not a condition of use.
Yes. A channel whose only destination is a directory delivers to no one, so it can sit beside the incumbent for as long as you want evidence.
The honest list is kept in the repository rather than summarised here, because a summary of gaps is the first thing to go stale. Read what was verified and what was not.
BioDream LLC. Bug reports are welcome as GitHub issues. Code contributions are not accepted — a single maintainer by design, which is worth knowing before you build on it.
Download for Linux, macOS or Windows — one file, nothing to install. Or read the reference manual, or the source on GitHub.