From openmed-skills
Reversibly de-identifies clinical text with OpenMed and restores original PHI from a saved mapping. Use for pseudonymization workflows where re-linking is needed later under authorization.
How this skill is triggered — by the user, by Claude, or both
Slash command
/openmed-skills:reidentifying-textThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Some workflows need to remove PHI **for processing** but keep the ability to
Some workflows need to remove PHI for processing but keep the ability to
restore it later under authorization — adjudication, patient recontact, linking
results back to a record. That is pseudonymization (reversible), not
anonymization (irreversible). OpenMed supports it with
deidentify(..., keep_mapping=True) to capture a mapping, and reidentify to
restore. Everything runs on-device.
Do NOT use reversibility when:
method="remove" and keep
no mapping.pip install "openmed[hf]"
import openmed
note = "Patient John Doe (MRN 00481726) seen on 2024-03-02 by Dr. Alice Smith."
# 1) De-identify AND capture the reversal mapping
deid = openmed.deidentify(
note,
method="mask", # or "replace" for realistic surrogates
keep_mapping=True, # <-- required to enable reidentify()
policy="gdpr_pseudonymization",
)
safe_text = deid.deidentified_text # ship/process this
mapping = deid.mapping # SECRET: store separately, encrypted
# 2) Later, under authorization, restore the original
restored = openmed.reidentify(safe_text, mapping)
assert restored == note
reidentify(deidentified_text, mapping) performs the inverse substitution. The
mapping is a dict[str, str] of redacted → original text, produced only when
keep_mapping=True.
consistent surrogates for stable pseudonymsFor replacement that maps the same identifier to the same surrogate across a document (and reproducibly across runs with a seed):
import openmed
deid = openmed.deidentify(
"Mr. John Doe called. John Doe's MRN is 00481726.",
method="replace",
consistent=True, # same input value -> same surrogate within the run
seed=42, # reproducible across runs (implies consistent=True)
keep_mapping=True,
)
print(deid.deidentified_text)
restored = openmed.reidentify(deid.deidentified_text, deid.mapping)
consistent=True keeps surrogates stable so analytics on the pseudonymized text
stay coherent; seed makes them reproducible. Either way, reversal still requires
the saved mapping.
The mapping is the re-identification key. Treat it like a secret:
import json, os
import openmed
note = "Patient John Doe (MRN 00481726), DOB 1970-01-15."
deid = openmed.deidentify(note, method="mask", keep_mapping=True, seed=7)
doc_id = "doc-7f3a" # opaque id, no PHI
# De-identified text -> general processing store (safe to share downstream)
with open(f"deid/{doc_id}.txt", "w", encoding="utf-8") as fh:
fh.write(deid.deidentified_text)
# Mapping -> SEPARATE, access-controlled, encrypted vault (illustrative path)
os.makedirs("vault", exist_ok=True)
with open(f"vault/{doc_id}.map.json", "w", encoding="utf-8") as fh:
json.dump(deid.mapping, fh) # encrypt this store in production
To re-identify later, load only the mapping for the authorized doc_id:
import json, openmed
with open("vault/doc-7f3a.map.json", encoding="utf-8") as fh:
mapping = json.load(fh)
with open("deid/doc-7f3a.txt", encoding="utf-8") as fh:
safe_text = fh.read()
original = openmed.reidentify(safe_text, mapping)
| Goal | Call | Mapping |
|---|---|---|
| GDPR pseudonymization (reversible) | deidentify(..., keep_mapping=True, policy="gdpr_pseudonymization") | keep, encrypted, separate |
| HIPAA Safe Harbor anonymization | deidentify(..., method="remove", policy="hipaa_safe_harbor") | none |
| Irreversible token linking | deidentify(..., method="hash") | none (one-way) |
method="hash" yields consistent, one-way tokens — good for joining records
without ever restoring the original. That is not reversible and needs no mapping.
extracting-pii-entities: preview the spans first if you want to confirm
what will be masked before committing to a reversible run.deidentifying-clinical-text: that skill covers methods, policies, and
the safety sweep; this one adds the keep_mapping + reidentify round-trip.openmed.analyze_text on deid.deidentified_text;
re-identify only the final, authorized output — never intermediate logs.keep_mapping=True is mandatory for reidentify to work; without it
deid.mapping is None..deidentified_text (and .pii_entities, .mapping), not
.text/.entities.reidentify substitutes those keys
back into the text.method="mask", identical placeholders (e.g. two
[NAME]) cannot be distinguished on reversal. For lossless round-trips use
method="replace" with consistent=True/seed, which produces distinct,
reversible surrogates.gdpr_pseudonymization and
hipaa_safe_harbor (configuring-privacy-policies).npx claudepluginhub maziyarpanahi/openmed --plugin openmed-skillsRemove, mask, or replace PHI/PII in clinical free text on-device using OpenMed's deidentify(). Use when de-identifying medical notes, redacting PHI before sharing, or choosing a de-id method.
De-identifies PHI via HIPAA safe harbor (removes 18 identifiers) and expert determination methods. Assesses re-identification risks, limited datasets, and data agreements.
Guides medical researchers in de-identifying clinical data before LLM analysis using a local Python CLI with regex-based PHI detection. Supports 10 country locales (kr, us, jp, cn, de, uk, fr, ca, au, in) and CSV/TSV/Excel input.