er7-redact
documentation • source • crate • email
Remove patient detail from HL7 v2 messages in the ER7 pipe-hat encoding — as a Rust library and a command-line tool — without breaking the message.
PID|1||PATID1234^5^M11^ADT1^MR^MCM||JONES^WILLIAM^A^III||19610615|M||C|1200 N ELM STREET^^GREENSBORO^NC^27401-1020
becomes
PID|1||11a9d74f8a6a54a7^5^M11^ADT1^MR^MCM||REDACTED^REDACTED^REDACTED^REDACTED||1961|M||C|^^^^
Same segments, same fields, same components, same delimiters. Every path that resolved to a value still resolves to one, so the interface engine, the test harness, and the message viewer downstream all behave the way they did on the original.
Contents
- Install
- Command line
- Library
- What it does
- What it deliberately does not do
- Documentation
- Development
- License
Install
Or for the command-line tool:
Command line
# Redact with the built-in policy
MSH|^~\&|ADT1|MCM|LABADT|MCM|20260815140000||ADT^A08^ADT_A01|MSG00001|P|2.5
EVN|A08|20260815140000
PID|1||11a9d74f8a6a54a7^5^M11^ADT1^MR^MCM||REDACTED^REDACTED^REDACTED^REDACTED||1961|M||C|^^^^
NK1|1|REDACTED^REDACTED^REDACTED|SPO^Spouse^HL70063|^^^^|
PV1|1|I|2000^2012^01||||REDACTED^REDACTED^REDACTED^REDACTED|||SUR||||ADM|A0||||63d6f85fb1af0958
AL1|1|DA|1605^ACETAMINOPHEN^L|MO|HEADACHE
# Say what would change, and change nothing
PID[1]-3[1].1.1 pseudonym
PID[1]-5[1].1.1 replace REDACTED
PID[1]-5[1].2.1 replace REDACTED
PID[1]-7[1].1.1 first 4
PID[1]-11[1].1.1 clear
The report carries paths and actions and no values, so it can go straight into a ticket.
Library
use ;
let mut message = parse?;
let report = new.redact;
println!;
println!;
A policy is an ordered list of rules — an HL7 path and an action:
use ;
let policy = new
.with? // stable stand-in, so messages still join
.with? // REDACTED^REDACTED^REDACTED
.with? // 19610615 → 1961
.with? // ^^^^
.with?; // "" — tell the receiver to clear its copy
…or the same thing as a file, which is what a team reviews in a pull request:
PID-3.1 pseudonym
PID-5 replace REDACTED
PID-7 first 4 # the birth year is enough for most tests
PID-11 clear
PID-19 null
Invert it — redact everything, name what to keep — when the message is unfamiliar:
let policy = everything
.with?
.with?;
What it does
| Preserves the shape | Leaf text is rewritten; no segment, field, repetition, component, or subcomponent is added or removed. Null is the one documented exception. |
| Keeps absent, empty, and null apart | An empty field stays empty — writing REDACTED into it would invent a value. An explicit "" stays null — overwriting it would turn "clear this" into a value. |
| Never creates a position | A rule for a field the message does not carry does nothing, rather than padding the segment out to reach it. |
| Cannot corrupt the message | Replacement text goes in escaped, so a | in a placeholder can never split a field. |
| Eight actions | keep, clear, null, replace, mask, first, last, pseudonym. |
| Stable pseudonyms | The same identifier maps the same way in every message redacted with the same key, so a redacted export is still joinable. |
| Reports what it did | One row per position changed, fully qualified, with no values in it. |
| One dependency | er7, which has none of its own. |
What it deliberately does not do
This is a positional editor, not a compliance tool.
- It cannot tell you whether the result is de-identified. That is a judgement about a whole data set, its recipients, and what else they hold — made by a person who is accountable for it.
- It does not know which positions your senders use. Run
er7 message.er7and read what is actually in there. - It does not find an identifier written into free text. A name in an
NTE-3comment survives every positional policy; name that position, or use--all. pseudonymis not cryptographic. It is a keyed hash that preserves equality on purpose, and anyone with the key can invert it. Use it inside your own trust boundary; for data leaving it,clearorreplace.- There is no way back: no mapping table, no key escrow, no undo.
A message this crate has redacted is a message with less in it, which is progress, and is not the same thing as a safe one.
Documentation
| Where | What |
|---|---|
spec/ |
the normative specification — one file per section, rules D1–D18 |
docs/usage/ |
the walk-through |
docs/policies/ |
the policy format, the actions, the built-in tables |
docs/api/ |
every public item |
docs/faq/ |
the questions the rest raise |
examples/ |
runnable programs that assert their own results |
AGENTS.md |
how to change this code |
Development
All four are clean on main and must stay that way. Behavioural changes
start in spec/ — see
AGENTS/spec-driven-development.md.
Every message in this repository is synthetic, and must stay that way:
a repository about redaction is exactly where somebody would be tempted to
commit a real one. See AGENTS/safety.md.
See also
er7— parse, query, edit, and write ER7, with zero dependencies. The layer underneath this one.serde-er7— Serde support for the same value tree.
License
MIT OR Apache-2.0 OR BSD-3-Clause OR GPL-2.0-only OR GPL-3.0-only — see LICENSE.md.