PHI-safe mode¶
Available in v0.1.0. Engineering flag that narrows what Personally Identifiable / Protected Health Information can travel with connector diagnostics, and hardens the transport against obvious foot-guns.
Enabled per call via the PhiSafe = true option on OpenEHR.Aql, OpenEHR.StoredQuery, and OpenEHR.Template.
What it affects¶
flowchart LR
subgraph Default mode
A[AQL body] --> EC1["Error.Detail.Context"]
R[Response body] --> E1["Error.Detail.Body"]
H1[Basic over http://] --> OK1[Accepted]
end
subgraph PHI-safe mode
A2[AQL body] -->|"<redacted>"| EC2["Error.Detail.Context"]
R2[Response body] -->|"Redacted + ContentBytes"| E2["Error.Detail"]
H2[Basic over http://] -->|Blocked| X1["OpenEHR.AuthError"]
M["Error.Message"] -->|"Generic by category"| G["'The CDR rejected the AQL query.'"]
end
Behaviours (what the code actually does)¶
Implemented in src/Aql.pqm.
| Surface | Default | With PhiSafe = true |
|---|---|---|
Basic auth over http:// |
Allowed | Refused with OpenEHR.AuthError before the request leaves the machine. |
Error.Detail.Body |
Parsed JSON or raw body text | [ContentBytes = n, Redacted = true] |
Error.Detail.Context |
AQL text / stored-query name | "<redacted>" |
Error.Message |
Server-provided message | Generic per category (e.g. "The CDR rejected the AQL query.") |
Authorization in cache key |
Excluded | Excluded (same) |
X-Audit-Context in cache key |
Excluded | Excluded (same) |
Row-level Diagnostics.Trace |
Never | Never (same — default-safe) |
| Credentials in function args | Never accepted | Never accepted (same — design invariant) |
The flag narrows what travels with errors. Datasets still fail loudly when the CDR says no; the log just stops carrying content that could identify patients.
How to enable¶
Per call:
OpenEHR.Aql(
"https://cdr.example.org/rest/openehr/v1",
"SELECT c/uid/value AS Uid FROM EHR e CONTAINS COMPOSITION c",
[ PhiSafe = true ]
)
Set it once at the top of your query file as a variable and reuse across steps to keep queries DRY:
let
Opts = [ PhiSafe = true, AuditContext = "prod-dashboard" ],
Uids = OpenEHR.Aql(cdr, sampleAql, Opts),
Bps = OpenEHR.StoredQuery(cdr, "org.example::bp_trend", null, Opts)
in
Table.Combine({Uids, Bps})
What's always on (PHI-safe or not)¶
These hold unconditionally because they are baked into the connector's design:
- No row-level tracing. Page payloads are never written to trace, whatever the trace level.
- No AQL text at
Information. Query text is only traced atVerbose. ExcludedFromCacheKey = {"Authorization", "X-Audit-Context"}on everyWeb.Contentscall.- Credentials via
Extension.CurrentCredential()— they cannot leak into a saved.pbix.
Interplay with Power BI tracing¶
Power BI Desktop writes traces to:
Even with PhiSafe = true, the Mashup engine itself may emit row counts and schema info at Verbose. If you are threat-modelling the trace directory, treat the whole directory as sensitive and rotate it off the host.
Deferred (post-v0.1.0)¶
- AQL-body SHA-256 prefix in traces (replace the
Verbosefull-text emission). - Stable pseudonyms for nav-table
ehr_idfolder names. QueryParameterskey-only tracing.
These land in Phase 3 once the trace layer itself is overhauled.