An HL7 v2.x message viewer for Notepad++. PipeHat turns Notepad++ into a fluent HL7 workbench: syntax highlighting, hover tooltips with field definitions, a dockable message-tree panel, and a fail-closed PHI scrubber. Windows-only, x64, Unicode.
Named after the |^~\& "pipe-and-hat" encoding characters every HL7 interface engineer
knows by heart.
- Syntax highlighting -- segments, fields, components, and delimiters are colorized as you read, driven by a delimiter-aware HL7 tokenizer (reads the real separators from MSH).
- Field tooltips -- hover any field to see its segment/field name, data type, and
required flag. Coded values are decoded: hovering
MSH-9orEVN-1shows what the trigger event means (e.g.ADT^A01-> Admit / Visit Notification,SIU^S12-> New Appointment Booking). - Dockable message tree -- a segment -> field outline of the message with the same trigger-event decoding inline; click a node to jump the editor to it. The panel follows the active message: it never auto-opens on startup and closes when you close the HL7 file.
- PHI scrubber -- de-identify a message for sharing/testing, with HIPAA Safe Harbor field coverage (names, IDs, dates, provider/scheduling segments, and more). Runs fail-closed: any field the parser can't account for is reported, and a residual scan (SSN, email, IP, long digit runs) warns you before you ever treat output as de-identified.
- Conformance checking -- validate a message against per-interface rules (max field length, allowed value sets, required fields) defined in an editable profile. Violations are squiggle-underlined and listed -- a pre-flight "will the receiver accept this?" check.
- Structural validation -- advisory malform detection: missing MSH, invalid segment IDs, empty required MSH fields, unterminated escape sequences. Never blocking.
- Compare views -- put one message in each of Notepad++'s two split views and run
Compare Views (
Ctrl+Alt+Shift+D); every differing field is highlighted in place in both panes, segment- and field-aware (ignores volatile MSH-7 datetime / MSH-10 control ID). - Pretty-print -- put every segment on its own line (fixes single-line CR-delimited messages), and folding to collapse detail-segment groups (OBX/NTE under their parent).
- Version & escape decoding -- MSH-12 shows the HL7 version + era; escape sequences
(
\F\ \S\ \T\ \Xhh\…) are decoded on hover. - External transform providers -- run the active message through any transformation
engine and see the result in the other view, diffed field by field. PipeHat knows one
contract (stdin in, stdout out, exit code) and nothing about any vendor, so an
InterSystems IRIS wrapper, a Mirth JavaScript runner, and an XSLT processor are all
just lines in
PipeHat.providers.
The plugin activates automatically on HL7 content (first non-blank segment is MSH,
FHS, or BHS) or a .hl7 file. You can also force it on any buffer with
Enable HL7 Highlighting (Ctrl+Alt+Shift+E).
| Shortcut | Action | Shortcut | Action |
|---|---|---|---|
Ctrl+Alt+Shift+T |
Toggle message tree | Ctrl+Alt+Shift+V |
Validate message |
Ctrl+Alt+Shift+H |
Scrub PHI | Ctrl+Alt+Shift+D |
Compare the two views |
Ctrl+Alt+Shift+C |
Check conformance | Ctrl+Alt+Shift+R |
Pretty-print / reformat |
Ctrl+Alt+Shift+G |
Toggle folding | Ctrl+Alt+Shift+E |
Enable HL7 highlighting |
Ctrl+Alt+Shift+<- / -> |
Previous / next field | Ctrl+Alt+Shift+P |
Settings |
Ctrl+Alt+Shift+M |
Send message (MLLP) | Ctrl+Alt+Shift+L |
Toggle MLLP listener |
Ctrl+Alt+Shift+Y |
Replay all messages (MLLP) | Ctrl+Alt+Shift+PgDn / PgUp |
Next / previous message |
Ctrl+Alt+Shift+K |
Copy field path | Ctrl+Alt+Shift+W |
Copy as rich text |
Ctrl+Alt+Shift+X |
Transform with... (pick provider) | Ctrl+Alt+Shift+A |
Transform again (last provider) |
All combos include Shift deliberately -- plain Ctrl+Alt+letter collides with
Notepad++ defaults and gets grabbed by other software (graphics drivers, AltGr
layouts) on some machines.
Any conflicts can be remapped in Settings -> Shortcut Mapper -> Plugin commands.
- Windows x64
- Notepad++ (x64)
- To build from source: CMake 3.15+ and Visual Studio 2019/2022 (MSVC, C++17)
- Locate your Notepad++
pluginsdirectory (typically%ProgramFiles%\Notepad++\pluginsor, for a portable install,plugins\next tonotepad++.exe). - Create a folder named
PipeHatinside it. - Copy
PipeHat.dllinto that folder, so the path is…\Notepad++\plugins\PipeHat\PipeHat.dll. - Restart Notepad++.
The folder name and the DLL name must both be
PipeHat-- the dockable panel registers under that module name and won't appear if they differ.
Verify the install: open an HL7 message (a file whose first line begins with MSH). You
should see colorized segments and a PipeHat entry under Plugins in the menu bar.
Out-of-source build with CMake + MSVC:
cmake -S . -B build -A x64
cmake --build build --config Release
# output: build/Release/PipeHat.dllThen follow the install steps above with the freshly built DLL.
Verification is mostly manual -- open an HL7 sample in a debug Notepad++ build and exercise
the menu commands. The parser and PHI-coverage regressions that matter are pinned by a
standalone harness that links only HL7Lexer.cpp + PHIScrubber.cpp (no Windows deps) and
exits non-zero on failure:
# from a Visual Studio developer prompt, at the repo root
cl /EHsc /std:c++17 /I src tests\SegmentIDTest.cpp src\HL7Lexer.cpp src\PHIScrubber.cpp /Fe:build\SegmentIDTest.exe
build\SegmentIDTest.exe- Open any HL7 v2.x file (or paste a message and ensure the first line starts with
MSH) -- highlighting and tooltips activate automatically. - Plugins -> PipeHat menu exposes the commands (message tree, PHI scrub, about).
- Hover a field for its definition; click a tree node to navigate to it.
The scrubber is a best-effort de-identification aid, not a compliance guarantee.
⚠️ Fixed in v2.0.0 -- re-scrub anything scrubbed by an earlier build. Segment IDs containing a digit (PV1,NK1,GT1,IN1,IN2,PD1,DG1,PR1,PV2) were not recognized, so those segments were skipped entirely -- next-of-kin details, guarantor SSN/DOB, insurance IDs and doctor names survived the scrub, and the scrub still reported clean because the coverage check shared the same blind spot.PIDwas unaffected, which is why the leak was not visible. If you shared output from an earlier build, treat it as not de-identified.
- It runs fail-closed: if any PHI-mapped field can't be processed, or a residual scan finds an identifier, the completion dialog switches to a warning -- do not treat the output as de-identified until you've reviewed it. The anonymize-mode coverage check derives segments independently of the parser, so a parser gap surfaces as a warning rather than a silent skip.
- Scrubbing empties the undo buffer on purpose, so originals are not recoverable via Ctrl+Z. Keep your own backup of the source message.
- The on-disk original and Notepad++'s
backup\snapshots may still contain pre-scrub PHI.
Always review scrubbed output before sharing it outside a trusted boundary.
Check Conformance (Ctrl+Alt+Shift+C) validates the active message against rules you define in
PipeHat.profile, created on first run in the Notepad++ plugin config folder
(%AppData%\Notepad++\plugins\config). Rules are per-interface -- the same field can carry
different limits at different endpoints. Format:
PID-8.values=M,F,O,U,A,N # first component must be one of these
PID-5.max=48 # field must be <= 48 characters
MSH-9.required=true # field must be present
Violating fields are squiggle-underlined in the editor and listed in a summary dialog.
You don't have to hand-edit the file: Settings (Ctrl+Alt+Shift+P) opens a rule editor --
a segment / field / max / allowed values / required grid with Add / Edit / Remove -- that
reads and writes PipeHat.profile and reloads it immediately so the next Check Conformance
uses your changes. The file format is unchanged, so hand-editing and the GUI interoperate.
⚠️ MLLP is cleartext. Messages cross the network unencrypted -- PHI included. Use it only over loopback or a trusted network. There is no TLS (MLLP/S) yet.Need TLS today? Front it with stunnel. This is the standard way to add TLS to a protocol that lacks it, and many sites already do it. Point stunnel at your TLS endpoint, have it listen on
127.0.0.1:2575, and point PipeHat's send host/port at that local listener -- the plaintext hop never leaves the machine. Native TLS is on the roadmap (docs/06-ROADMAP.md), deliberately unrushed: a build that says "TLS" while skipping certificate validation would be worse than the honest cleartext warning above.
PipeHat can send the active message to an HL7 endpoint and receive inbound messages over MLLP (HL7's framing for TCP). It ships disabled and opens no sockets until you turn it on in Settings -> MLLP.
- Send Message (MLLP) (
Ctrl+Alt+Shift+M) -- frames the active message, sends it to the configured host/port on a background thread, and shows the returned ACK/NAK (MSA-1+ control id). - Toggle MLLP Listener (
Ctrl+Alt+Shift+L) -- starts/stops an MLLP server. Each inbound message is auto-acknowledged (AA) and opened in a tab (colored like any HL7 buffer). The menu item shows a checkmark while listening. - Replay All Messages (MLLP) (
Ctrl+Alt+Shift+Y) -- sends every message in the buffer, one MLLP frame per message, and reports accepted / rejected / no-ACK / failed counts. This is the difference between an interface test and an echo: Send Message frames the whole buffer as a single message, so a 5-message log arrives as one blob with one ACK, while a real receiver frames and ACKs per message. Point it at a Mirth channel and it is a regression harness.- Replay offers to refresh MSH-10 (control id) and MSH-7 (datetime) on each message, and you should almost always say yes: receivers deduplicate on control id, so replaying captured messages with their original ids gets them accepted once and silently discarded on every later run -- a test that reports success while delivering nothing.
A buffer may hold many messages (a log or batch file), and each message's own MSH delimiters
are used to parse it -- a !-separated message sitting after a |-separated one is read
correctly. The tree groups by message (12/480 ADT^A01 [MSG012]), envelope segments
(FHS/BHS/BTS/FTS) sit outside any message, and Ctrl+Alt+Shift+PgDn / PgUp step
between messages reporting "Message 12 of 480".
Saving received messages is OFF by default. Inbound messages open as in-memory tabs only --
no PHI touches disk. If you enable Save received messages to disk in Settings -> MLLP, each
message is written to %LOCALAPPDATA%\PipeHat\received\ as <type>_<controlId>_<time>.hl7
(deliberately under LOCALAPPDATA, not the roaming plugin-config folder, so cleartext PHI can't
be carried off the machine by roaming profiles or backup agents). backup\ folder.
The listener binds to loopback (127.0.0.1) only unless you both tick Allow binding a
non-loopback interface and supply a bind address -- and even then a confirmation warns you that
you're exposing an HL7 receiver on your network. The first send or listen each session prompts
a cleartext-PHI confirmation. Network settings persist to PipeHat.ini.
CLAUDE.md-- architecture orientation and the invariants that matter when touching the parser or scrubber.AGENTS.md-- the regression-critical invariant subset for AI coding agents.docs/05-CODE-REVIEW.md-- defect inventory and fix status.docs/06-ROADMAP.md-- what's shipped and what's planned (trigger-event decoding, HL7 version awareness, message compare/diff, conformance profiles, and more).
Note:
docs/00–04are the original design brief and are aspirational -- they describe classes and layers that don't exist in the shipped code. Trust the source anddocs/05/06for current reality.
v2.1.0. Adds multi-message file support (each message parsed with its own MSH
delimiters; tree grouping and Next/Previous Message navigation) and Replay All Messages
(one MLLP frame per message, with MSH-10/MSH-7 refresh). v2.0.0 added MLLP send/receive
(HL7 over TCP) behind an off-by-default toggle -- the plugin's first network feature -- and
fixed a silent PHI leak where segments whose IDs contain a digit (PV1, NK1, GT1,
IN1...) were skipped by the scrubber while it still reported clean (see the scrubber warning
above and docs/05-CODE-REVIEW.md C6; pinned by tests/SegmentIDTest.cpp). Crash-class defects
and the fail-open PHI leak from the initial review are fixed and build-verified. v1.1 added trigger-event decoding, HIPAA Safe Harbor scrubber coverage,
conformance checking, hotkeys, and smarter panel behavior; v1.2 adds structural validation,
message compare/diff, escape + HL7-version decoding, pretty-print, folding, and broader
activation; v1.3 adds the Settings GUI for editing conformance rules without hand-editing
the profile file. Each feature is verified by a standalone test. MLLP send/receive (network)
is integrated behind an off-by-default toggle and pending live-endpoint verification before a
v2.0 release. See the roadmap for what's next.
MIT © 2026 Shawn Iachetta.