diff --git a/app/core/privacy.py b/app/core/privacy.py new file mode 100644 index 0000000..4f0087a --- /dev/null +++ b/app/core/privacy.py @@ -0,0 +1,16 @@ +"""PII scrubbing and pseudonymisation (issue #2). Contract stub — implementation lands in #2.""" + +from app.core.types import ScrubbedText + + +def scrub(text: str) -> ScrubbedText: + """Replace personal data (phones, emails, SNILS, names, addresses...) with placeholders.""" + raise NotImplementedError + + +def pseudo_id(snils: str, key: str | None = None) -> str: + """HMAC-SHA256(PSEUDO_KEY, normalised SNILS) as 32 hex chars. + + The token is the only link between the identity and clinical databases. + """ + raise NotImplementedError diff --git a/app/core/types.py b/app/core/types.py new file mode 100644 index 0000000..98d43a4 --- /dev/null +++ b/app/core/types.py @@ -0,0 +1,27 @@ +"""Shared core types. Frozen contract between zones: change only via a separate small PR.""" + +from enum import IntEnum +from typing import NewType + +# Text that has passed `app.core.privacy.scrub()`. The LLM gateway accepts only this type (guardrail #1). +ScrubbedText = NewType("ScrubbedText", str) + + +class RiskLevel(IntEnum): + """Safety risk level. Ordered, so the combined signal is `max(lexicon, llm)`.""" + + NONE = 0 + POSSIBLE = 1 + ACUTE = 2 + + +class AlertLevel(IntEnum): + """Alert level stored in DB. + + UI colour is derived (ADR-006): ACUTE=red, HIGH/MODERATE=yellow, INFO=grey. + """ + + INFO = 0 + MODERATE = 1 + HIGH = 2 + ACUTE = 3