Cases · Healthcare · 8 min · Oct 24, 2026
Encrypted. Still Readable.
The warehouse was encrypted. Every analyst could still read every Social Security number. This is how a hospital stopped moving real patient identifiers into places that only needed to join on them.
Case summary
At a glance
| Client | Regional hospital system, composite of programs I have worked |
|---|---|
| Trigger | An AI assistant pilot was about to get read access to the analytics warehouse |
| Constraint | Dashboards, models, the call center, and billing could not break |
| Decision | Tokenize patient identifiers at ingest with vaultless tokenization, and control clear access by role |
| Outcome | Analysts and the AI pilot work on tokens. Clear values go only to the roles whose job is the patient. |
The ask
The AI team wanted their assistant pilot connected to the analytics warehouse, so staff could ask questions about readmissions and capacity in plain English. Security was asked to sign off.
The review started with one question: who can read a patient's SSN outside the EHR?
The answer was "the warehouse is encrypted." True, and not the question. A nightly job copied name, SSN, MRN, date of birth, address, phone, email, and insurance member ID into the warehouse in clear text. Encryption at rest protected the disk. The database decrypted on read for every analyst, every data scientist, the platform admins, and now, the AI pilot's service account.
A prompt-injected assistant would have been a patient export with a chat window.
The constraints
- Analytics could not break. Dashboards and the readmissions model join across a dozen tables on MRN.
- The call center verifies callers with the last four of the SSN.
- Revenue cycle needs real values for claims and insurance member IDs.
- The EHR was off limits. No changes to clinical workflows or the source system.
- Dev and test still needed realistic data. Synthetic data had been tried and never matched production.
The options we weighed
Five approaches were on the table. Only one kept the joins working without putting real identifiers in the warehouse.
| Option | What it does | Why it did or did not fit |
|---|---|---|
| Encryption at rest | Protects storage and backups | Already in place. Does nothing about who can query. |
| Dynamic masking in the warehouse | Hides values at query time | Real values still land and sit in the warehouse, dev copies, and exports. One misconfigured view undoes it. |
| De-identification | Strips identifiers out | Breaks the call center and billing, and the readmissions model loses its join key. |
| Synthetic data | Replaces production data | Already tried for dev. Never matched reality, and useless for production analytics. |
| Tokenization at ingest | Replaces identifiers with consistent, format-preserving tokens before they land | Joins keep working on tokens. Clear values come back only for roles with detokenize permission. Chosen. |
What we did, in phases
- The warehouse and the AI pilot first. That is where the blast radius was largest. Identifiers were tokenized in the ETL job, before they reached the warehouse, and the AI service account was set to get nulls for every identifier.
- Dev and test next. New copies are refreshed from the tokenized warehouse, so real patients stop appearing in lower environments.
- The role policy last. Detokenize permission was granted field by field, role by role, in the central policy server, starting from no access.
Vaultless tokenization generates tokens algorithmically where the data is processed, so there is no central lookup table of real values to steal or to bottleneck on. Two properties carried the analytics: the same input always produces the same token, so MRN still joins to MRN; and tokens keep the original format, so schemas and BI tools did not break.
| Field | Token type | What stays clear | Why |
|---|---|---|---|
| SSN | Numeric, delimiters preserved | Last 4, only for roles that need it | Call center verification |
| MRN | Alpha-Numeric | Nothing | It is the join key. Consistency is all analytics needs. |
| Name | Unicode | Nothing | No consumer in the warehouse needs it |
| Date of birth | Datetime | Only the date parts the model needs | Readmission risk needs age, not a birthday |
| Phone, email | Numeric, Email | Nothing | Outreach runs from the EHR, not the warehouse |
| Insurance member ID | Alpha-Numeric | Clear for billing roles only | Claims need the real value |
Diagnosis and procedure codes stayed in the clear. They are the analytics. With the identifiers tokenized, a code is no longer attached to a name anyone in the warehouse can read.
Who sees what
Each role gets tokenize or detokenize permission per field. Roles without detokenize get a null, the token, or an error. Masks apply on detokenize, so a role can see the last four and nothing else.
| Role | SSN | Name | DOB | Without detokenize |
|---|---|---|---|---|
| BI analysts | Token | Token | Token | Token, so joins and counts still work |
| Data science (readmissions model) | Token | Token | Partial, per the date setting | Token |
| AI assistant service account | None | None | None | Null. The agent cannot return what it never receives. |
| Call center | Masked, last 4 | Clear | Clear | Can detokenize: verifying the caller is the job |
| Revenue cycle | Clear | Clear | Clear | Can detokenize: claims need real values |
| Platform and DBA admins | Token | Token | Token | Token. They run the system; they do not read the patients. |
The AI row is what got the pilot approved. A fooled assistant still runs as its service account, and that account only ever receives nulls for identifiers.
What pushed back
- Will my dashboards break? The analysts' first question, every time. The answer was a side-by-side run: the same dashboards on tokenized data, with the same counts.
- Everyone wanted detokenize access. "Just in case" requests came from every team. Each one needed a named job reason, or it was a no.
- Free text. Clinical notes and comment fields carry names and numbers in prose. Structured tokenization does not touch them. They became a separate workstream with discovery first.
- Time. Tokenization services, field definitions, policy, and every pipeline that touches the fields. This was a program measured in months, not a setting someone flipped.
Where it landed
- The AI pilot went live on tokenized data. Identifiers come back as nulls.
- Analysts kept their dashboards and joins. The readmissions model trains on tokens.
- Dev and test stopped holding real patients.
- "Who can see a clear SSN outside the EHR?" now has an answer by role: the call center, masked to the last four, and revenue cycle.
One line stayed in every review: tokenized data is not automatically de-identified under HIPAA. It is still PHI, with a much smaller blast radius.
What I would do differently
- Start with the policy conversation, not the tooling. Deciding who truly needs detokenize access took longer than the engineering.
- Run discovery on free text in parallel, not after. It was always going to be the next finding.
- Treat the detokenize list like any other grant. Put it on a quarterly review from day one, before it quietly grows back to "all analysts."
The takeaway
The analyst needed the join, not the patient. Tokenize identifiers before they land, give clear values only to the roles whose job is the patient, and give the AI agent nulls.
- Sites.Read.All Is the Index: the same broad-grant miss, on the AI agent side.
- The Org Can Assume Prod: a wide trust condition instead of a wide warehouse role.