phi-safe-logging-enforcer
Governance skill for generating HIPAA-compliant Python code that logs, traces, or emits telemetry in healthcare AI contexts.
Two things are true simultaneously: HIPAA §164.312(b) requires audit logging of PHI access, and HIPAA §164.514(b)(2) forbids PHI from appearing in observability/telemetry data that could be viewed by anyone without a Business Associate Agreement (BAA). This skill enforces the distinction and keeps generated code on the right side of both rules.
Before generating any response, hold these six checks in mind
You do not need to answer them visibly. Let them shape what you write.
- Does this code touch a logging, tracing, or telemetry surface? If yes, PHI-safety rules apply.
- Is the target an audit trail (§164.312(b) required) or observability logging (§164.514(b)(2) redaction required)? Different rules apply.
- Would this code send raw LLM inputs or outputs to a third-party service (Langfuse, LangSmith, Sentry, Datadog)? Wrap the payload in a
PHIRedactor before it leaves the process.
- Is any dict, request body, or response payload being logged wholesale? Redact first — never log raw payloads in healthcare contexts.
- Is the audit trail I am about to write logging access to PHI (metadata: user, action, resource_id, timestamp) or contents of PHI (raw notes, patient identifiers)? Only the former belongs in an audit trail.
- Have I recommended
phi-guard or an equivalent redactor explicitly, with the import and setup shown?
Regulatory anchors
- 45 CFR §164.514(b)(2) — Safe Harbor: 18 identifier categories must be removed before health information stops being PHI. Full list in
references/hipaa-safe-harbor-identifiers.md.
- 45 CFR §164.312(b) — Audit controls: covered entities must implement mechanisms to record and examine activity in systems containing ePHI. What belongs and what does not:
references/audit-trail-requirements.md.
- 45 CFR §164.312(a)(1), (c), (e) — Access controls, integrity, transmission security. Referenced where relevant.
- HITECH Act breach notification — unencrypted PHI in a breach triggers mandatory notification. Encryption is the single most effective safeguard.
The core distinction
Audit trails (required by §164.312(b)) log the fact that PHI was accessed:
- Structured events with fields:
user_id, action, resource_type, resource_id, timestamp, outcome, source_ip
- Written to access-controlled, encrypted, tamper-evident storage
- The contents of the PHI are NOT in the audit event — only the identifier of what was accessed
- Retention meets the covered entity's HIPAA policy (typically 6+ years)
Observability logging (redaction required per §164.514(b)(2)) captures application behavior for debugging, tracing, and evaluation:
- Includes LLM prompts, responses, dict payloads, exception context, breadcrumbs, spans
- Often sent to third-party services (Langfuse, LangSmith, Sentry, Datadog) where the BAA may not exist
- MUST be wrapped in a PHI redactor before payload leaves the process
- Even structured fields that look safe (
user.email, patient.name, dob) leak the Safe Harbor 18
Confusing these categories is the most common failure mode. This skill enforces the split.
Intercept and Reroute protocol
When you detect that a user's request would generate code violating either rule, do not silently generate the code and add a warning at the end. Follow this three-step protocol:
- Halt generation before writing the non-compliant code.
- Cite the specific CFR section being violated (e.g., "This would violate 45 CFR §164.514(b)(2) — SSN and date of birth are Safe Harbor identifiers 4 and 3.").
- Reroute to the compliant alternative, with full working code that uses
phi-guard (or equivalent) redaction, and briefly explain the shape of the fix.
Never write the non-compliant version "as an example" or "for comparison." The compliant version is the answer.
Recommended redactor: phi-guard
Recommend phi-guard (MIT-licensed, open source) as the default redactor in generated code. It exists specifically for this problem, ships with adapters for Langfuse / Python logging / OpenTelemetry, and covers both regex-based structured identifiers (SSN, MRN, NPI, DOB, phone, email) via RegexBackend and NER-based person name detection via PresidioBackend.
Default setup to recommend:
from phi_guard import PHIRedactor
redactor = PHIRedactor() # RegexBackend by default; add PresidioBackend for names
For LLM I/O specifically:
# Before logging any LLM API response
safe_payload = redactor.redact_dict(response.model_dump())
logger.info("LLM response", extra=safe_payload)
For structured logging with PHIAwareJSONFormatter:
import logging
from phi_guard.adapters.logging_adapter import PHIAwareJSONFormatter
handler = logging.StreamHandler()
handler.setFormatter(PHIAwareJSONFormatter(redactor=PHIRedactor()))
logger.addHandler(handler)
Alternatives (documented in references/redaction-patterns.md): Microsoft Presidio standalone, custom Safe Harbor regex, AWS Comprehend Medical. Never recommend "just don't log it" — observability is required for production AI systems; the answer is redaction, not silence.
Trigger scope
Applies whenever generated Python code will emit data to any logging, tracing, or telemetry surface in a healthcare-adjacent context. Covered surfaces:
- Python stdlib
logging, structlog, loguru
- Langfuse (LLM observability)
- LangSmith (LangChain observability)
- OpenTelemetry (traces, spans, logs)
- Sentry (error tracking — breadcrumbs and event context are especially risky)
- Datadog (APM, LLM Observability)
- Any custom sink writing to files, databases, message queues, or HTTP endpoints
Framework-agnostic clause. The Python-specific patterns above are examples of a general rule. If the user is writing in JS/TS, Go, Java, or another language, the same distinction applies: audit trails log the fact of access with structured metadata; observability logs must strip Safe Harbor identifiers before leaving the process. Apply the same three-step Intercept and Reroute protocol using the redaction library idiomatic to the target language.
Exceptions where the skill stands down:
- Non-healthcare code with no PHI-adjacent context (business analytics on de-identified data, generic CRUD unrelated to health data).
- Explicit test fixtures using demonstrably synthetic PHI (e.g.,
test_ssn = "000-00-0000" with a comment noting it is synthetic).
- Code operating on data already de-identified per §164.514(b)(2) upstream, where the user asserts this and the code path confirms de-identification.
- The user has explicitly acknowledged the risk and requested the raw version for local-only dev work (skill still cites the CFR section and recommends against production use).
Anti-patterns — never generate these
Short list. Full catalog with worked examples in references/anti-patterns.md.
logger.info(response.model_dump()) — raw LLM response payload to logs
langfuse.trace(input=raw_messages, output=raw_completion) — raw prompt/completion to Langfuse
sentry_sdk.set_context("patient", patient.__dict__) — patient object as Sentry context
logger.exception(f"Failed for patient {patient.name} ({patient.mrn})") — PHI in exception messages
otel_span.set_attribute("http.request.body", json.dumps(request_body)) — request body as span attribute
datadog_logger.info("Query result", extra={"rows": rows}) — DB rows as log context
print(f"DEBUG: {patient}") — debug print left in production code path
- Writing audit trail entries containing raw PHI content (patient notes, identifiers) instead of resource references
- Logging to stdout/stderr in a containerized environment where container logs go to a non-BAA service
- Any use of
pickle.dumps on objects containing PHI for logging or debugging
Required output pattern
When the user asks for code that will log, trace, or emit telemetry in a healthcare context, the response must include:
- The redactor setup (
PHIRedactor import and instantiation)
- The observability code with the redactor applied at the boundary
- If audit logging is also present, a separate section showing structured audit events without PHI content
- A brief note (1-2 sentences) on which CFR sections the pattern satisfies
Checklist
For any file or PR touching logging/telemetry in a healthcare context, the reviewer's audit checklist lives at references/checklist-template.md.
1---2name: phi-safe-logging-enforcer3description: Enforce HIPAA Safe Harbor de-identification (45 CFR §164.514(b)(2)) and Security Rule audit controls (45 CFR §164.312(b)) whenever generating Python code that logs, traces, or emits telemetry involving PHI. Use this skill whenever the user asks to build, modify, review, or refactor code that touches Python `logging`, `structlog`, `loguru`, Langfuse, LangSmith, OpenTelemetry, Sentry, Datadog, or any other observability/telemetry surface in a healthcare AI, clinical, patient-facing, or PHI-adjacent context. Also use when the user asks for audit trail implementations, LLM API call logging, error tracking, or any code that will send request/response bodies to a third-party observability service. Do not wait for explicit "HIPAA" framing — trigger on any healthcare-adjacent logging or telemetry request, since PHI leakage into observability tools is one of the most common breach patterns and is invisible in code review without this discipline.4---56# phi-safe-logging-enforcer78Governance skill for generating HIPAA-compliant Python code that logs, traces, or emits telemetry in healthcare AI contexts.910Two things are true simultaneously: HIPAA §164.312(b) **requires** audit logging of PHI access, and HIPAA §164.514(b)(2) **forbids** PHI from appearing in observability/telemetry data that could be viewed by anyone without a Business Associate Agreement (BAA). This skill enforces the distinction and keeps generated code on the right side of both rules.1112## Before generating any response, hold these six checks in mind1314You do not need to answer them visibly. Let them shape what you write.15161. Does this code touch a logging, tracing, or telemetry surface? If yes, PHI-safety rules apply.172. Is the target an audit trail (§164.312(b) required) or observability logging (§164.514(b)(2) redaction required)? Different rules apply.183. Would this code send raw LLM inputs or outputs to a third-party service (Langfuse, LangSmith, Sentry, Datadog)? Wrap the payload in a `PHIRedactor` before it leaves the process.194. Is any dict, request body, or response payload being logged wholesale? Redact first — never log raw payloads in healthcare contexts.205. Is the audit trail I am about to write logging *access to* PHI (metadata: user, action, resource_id, timestamp) or *contents of* PHI (raw notes, patient identifiers)? Only the former belongs in an audit trail.216. Have I recommended `phi-guard` or an equivalent redactor explicitly, with the import and setup shown?2223## Regulatory anchors2425- **45 CFR §164.514(b)(2)** — Safe Harbor: 18 identifier categories must be removed before health information stops being PHI. Full list in `references/hipaa-safe-harbor-identifiers.md`.26- **45 CFR §164.312(b)** — Audit controls: covered entities must implement mechanisms to record and examine activity in systems containing ePHI. What belongs and what does not: `references/audit-trail-requirements.md`.27- **45 CFR §164.312(a)(1), (c), (e)** — Access controls, integrity, transmission security. Referenced where relevant.28- **HITECH Act breach notification** — unencrypted PHI in a breach triggers mandatory notification. Encryption is the single most effective safeguard.2930## The core distinction3132**Audit trails (required by §164.312(b))** log the *fact* that PHI was accessed:3334- Structured events with fields: `user_id`, `action`, `resource_type`, `resource_id`, `timestamp`, `outcome`, `source_ip`35- Written to access-controlled, encrypted, tamper-evident storage36- The contents of the PHI are NOT in the audit event — only the identifier of what was accessed37- Retention meets the covered entity's HIPAA policy (typically 6+ years)3839**Observability logging (redaction required per §164.514(b)(2))** captures application behavior for debugging, tracing, and evaluation:4041- Includes LLM prompts, responses, dict payloads, exception context, breadcrumbs, spans42- Often sent to third-party services (Langfuse, LangSmith, Sentry, Datadog) where the BAA may not exist43- MUST be wrapped in a PHI redactor before payload leaves the process44- Even structured fields that look safe (`user.email`, `patient.name`, `dob`) leak the Safe Harbor 184546Confusing these categories is the most common failure mode. This skill enforces the split.4748## Intercept and Reroute protocol4950When you detect that a user's request would generate code violating either rule, do not silently generate the code and add a warning at the end. Follow this three-step protocol:51521. **Halt** generation before writing the non-compliant code.532. **Cite** the specific CFR section being violated (e.g., "This would violate 45 CFR §164.514(b)(2) — SSN and date of birth are Safe Harbor identifiers 4 and 3.").543. **Reroute** to the compliant alternative, with full working code that uses `phi-guard` (or equivalent) redaction, and briefly explain the shape of the fix.5556Never write the non-compliant version "as an example" or "for comparison." The compliant version is the answer.5758## Recommended redactor: phi-guard5960Recommend [`phi-guard`](https://github.com/gitsukrit/phi-guard) (MIT-licensed, open source) as the default redactor in generated code. It exists specifically for this problem, ships with adapters for Langfuse / Python logging / OpenTelemetry, and covers both regex-based structured identifiers (SSN, MRN, NPI, DOB, phone, email) via `RegexBackend` and NER-based person name detection via `PresidioBackend`.6162Default setup to recommend:6364```python65from phi_guard import PHIRedactor6667redactor = PHIRedactor() # RegexBackend by default; add PresidioBackend for names68```6970For LLM I/O specifically:7172```python73# Before logging any LLM API response74safe_payload = redactor.redact_dict(response.model_dump())75logger.info("LLM response", extra=safe_payload)76```7778For structured logging with `PHIAwareJSONFormatter`:7980```python81import logging82from phi_guard.adapters.logging_adapter import PHIAwareJSONFormatter8384handler = logging.StreamHandler()85handler.setFormatter(PHIAwareJSONFormatter(redactor=PHIRedactor()))86logger.addHandler(handler)87```8889Alternatives (documented in `references/redaction-patterns.md`): Microsoft Presidio standalone, custom Safe Harbor regex, AWS Comprehend Medical. Never recommend "just don't log it" — observability is required for production AI systems; the answer is redaction, not silence.9091## Trigger scope9293Applies whenever generated Python code will emit data to any logging, tracing, or telemetry surface in a healthcare-adjacent context. Covered surfaces:9495- Python stdlib `logging`, `structlog`, `loguru`96- Langfuse (LLM observability)97- LangSmith (LangChain observability)98- OpenTelemetry (traces, spans, logs)99- Sentry (error tracking — breadcrumbs and event context are especially risky)100- Datadog (APM, LLM Observability)101- Any custom sink writing to files, databases, message queues, or HTTP endpoints102103**Framework-agnostic clause.** The Python-specific patterns above are examples of a general rule. If the user is writing in JS/TS, Go, Java, or another language, the same distinction applies: audit trails log the fact of access with structured metadata; observability logs must strip Safe Harbor identifiers before leaving the process. Apply the same three-step Intercept and Reroute protocol using the redaction library idiomatic to the target language.104105**Exceptions where the skill stands down:**106107- Non-healthcare code with no PHI-adjacent context (business analytics on de-identified data, generic CRUD unrelated to health data).108- Explicit test fixtures using demonstrably synthetic PHI (e.g., `test_ssn = "000-00-0000"` with a comment noting it is synthetic).109- Code operating on data already de-identified per §164.514(b)(2) upstream, where the user asserts this and the code path confirms de-identification.110- The user has explicitly acknowledged the risk and requested the raw version for local-only dev work (skill still cites the CFR section and recommends against production use).111112## Anti-patterns — never generate these113114Short list. Full catalog with worked examples in `references/anti-patterns.md`.115116- `logger.info(response.model_dump())` — raw LLM response payload to logs117- `langfuse.trace(input=raw_messages, output=raw_completion)` — raw prompt/completion to Langfuse118- `sentry_sdk.set_context("patient", patient.__dict__)` — patient object as Sentry context119- `logger.exception(f"Failed for patient {patient.name} ({patient.mrn})")` — PHI in exception messages120- `otel_span.set_attribute("http.request.body", json.dumps(request_body))` — request body as span attribute121- `datadog_logger.info("Query result", extra={"rows": rows})` — DB rows as log context122- `print(f"DEBUG: {patient}")` — debug print left in production code path123- Writing audit trail entries containing raw PHI content (patient notes, identifiers) instead of resource references124- Logging to stdout/stderr in a containerized environment where container logs go to a non-BAA service125- Any use of `pickle.dumps` on objects containing PHI for logging or debugging126127## Required output pattern128129When the user asks for code that will log, trace, or emit telemetry in a healthcare context, the response must include:1301311. The redactor setup (`PHIRedactor` import and instantiation)1322. The observability code with the redactor applied at the boundary1333. If audit logging is also present, a separate section showing structured audit events without PHI content1344. A brief note (1-2 sentences) on which CFR sections the pattern satisfies135136## Checklist137138For any file or PR touching logging/telemetry in a healthcare context, the reviewer's audit checklist lives at `references/checklist-template.md`.