The views here are my own. This is a demonstration of what is achievable with OCI’s native controls, not a prescriptive standard or a compliance certification. Nothing in it should be read as Oracle asserting HIPAA guarantees on your behalf.


Every healthcare AI project I see stalls in the same place. Not on model quality. Not on cost. On one question from someone in legal or security:

Where does the patient data go, and can you prove it?

The usual answer is a document. A policy says PHI must stay in our environment. A design review confirms the team read the policy. A developer is trusted to know whether the free-text note their service is about to send contains identifiers.

That is not a control. It is an agreement, and agreements drift. What follows is a pattern where the answer stops being a document and becomes something you can demonstrate — built on OCI constructs that already exist, with a working implementation you can run yourself.


The failure nobody plans for

The obvious threat model is a careless developer sending patient data to a public model API. Real, and worth preventing.

But it is not the common case. The common case is a developer who genuinely does not know. They are looking at a free-text clinical note in a variable called content. Does it contain a name? An MRN? A phone number? Sometimes. Not always. Not in a way they can determine by reading their own code.

No amount of training fixes that, because it is not a knowledge problem. It is an information problem — the answer depends on runtime data.

So the safe organizational response is to over-restrict, run everything on in-house models. Which means every developer accepts worse output on the majority of requests that never touch patient data at all.The goal is not to restrict developers. It is to stop making them guess — and, having done so, to give them the good models back for everything that isn’t PHI.


Two ideas, and only the second is unusual

Classify at the door. Inspect the request content at the gateway, before it goes anywhere, and determine what kind of data it actually is. Not what the caller says it is.

Enforce with topology, not policy. Once you know it is PHI, the model that handles it should be somewhere that physically cannot send it elsewhere.

The first idea is common. Plenty of gateways do content inspection.

The second is where OCI does real work, and where most designs stop short. There is a meaningful difference between:

  • a policy denies egress from this subnet — a rule that can be changed, misconfigured, or exempted, and
  • this subnet has no route to anywhere — a topology where there is nothing to change

Under OCI, the second is a route table with zero rules. Not a firewall rule denying traffic. No route at all. A packet leaving that subnet has no destination to be denied. That distinction matters in a review, because the reviewer is not being asked to trust a configuration. They are being shown that the path does not exist.


The architecture

PHI for regulated AI on OCI

Walking the OCI constructs that make it work, and why each is load-bearing:

The empty route table

The PHI inference subnet is attached to a route table containing no rules. No internet gateway, no NAT gateway, no service gateway.

This is the control. Everything else supports it.

Note what it is not: a security list denying outbound traffic, or an NSG with a restrictive egress rule. Both are policies about a path that exists. An empty route table means the path does not exist. In a design review you can put it on screen in one click, and it needs no explanation.

prohibit-public-ip-on-vnic

The PHI subnet is created with public IPs prohibited at the subnet level. Even if someone later attaches an internet route by mistake, no instance in that subnet has a public address to be reached at.

Two independent controls that both have to fail. This is what defense in depth looks like when it is cheap.

Gateways attached to subnets, not to the VCN

An easy misconception: a NAT gateway is a VCN-level resource, so surely everything in the VCN can use it?

No. A gateway is only reachable if a subnet’s route table points at it. The NAT gateway here serves the gateway tier only — the subnet that legitimately needs to reach approved external providers. The PHI subnet’s route table does not reference it, so for that subnet it may as well not exist.

A gateway that is not routed to cannot be used.

Service Gateway, and the boundary people draw in the wrong place

The gateway tier needs OCI Vault for provider credentials, Object Storage for audit records, and OCI Logging. Under a naive design, all of that means internet egress.

The Service Gateway routes to OCI services over Oracle’s backbone without traversing the internet. So a tier can be fully functional — secrets, audit, logs — while never having a route to a public network.

This is also where OCI Generative AI sits, and it is the thing most architecture diagrams get wrong.

The instinct is to draw two categories: my instances and everyone else. So managed models end up next to third-party APIs, both outside the boundary. That is the wrong cut.

The boundary that matters for a regulated workload is not “is this my instance?” It is “does this API call leave Oracle’s network?”

By that measure there are three destinations, not two:

DestinationWhere it runsLeaves OCI?
Self-hosted models in your subnetYour GPUs, your VCNNo — and no route out at all
OCI Generative AI, on-demand or dedicated clusterOracle-managed, region-pinnedNo — Service Gateway, Oracle backbone
Third-party provider APIsSomeone else’s cloudYes — this is the only true egress

That middle row is the one worth dwelling on, because it is where the story gets better rather than more restrictive. OCI Generative AI serves frontier models — OpenAI, Llama, Cohere and others — on demand, and dedicated AI clusters for reserved capacity and hosted fine-tunes. Frontier quality, Oracle-managed, and the request never leaves OCI’s network.

So the non-PHI lane does not have to mean “send it to a third party”. For many estates it means managed OCI models, and the third-party lane is never enabled at all.

Self-hosting then earns its place for specific reasons — a particular model, a fine-tune you control, or an explicitly customer-owned execution environment for regulated data — rather than being the only option that keeps data in bounds.

Compartments and IAM

The whole stack sits in a compartment, which is the IAM and blast-radius boundary. Instances read credentials from Vault through instance principals — no static API keys on disk, nothing to leak from an image or a config file.

Object Storage with versioning and retention rules

Audit records land in a bucket with versioning enabled and a retention rule applied. That gets you an audit trail that cannot be quietly rewritten.

One practical warning: retention rules can be locked, and a locked bucket cannot be deleted until the period expires — not even by a tenancy admin. That is exactly what you want in production and exactly what you do not want in a proof of concept you intend to tear down. Know which one you are building.


What the developer sees

A control that only says no is a control developers route around. The experience matters as much as the enforcement.

One endpoint. Applications call an OpenAI-compatible API. No branching on model choice, no second SDK for the in-tenancy path, no separate credential. Where the request runs is the platform’s decision, not the application’s.

Denials that are actionable. When a request is blocked, the response says what was detected, which lane is required, and where to send it instead:

{
  "error": "POLICY_DENY",
  "reason_code": "PHI_LANE_VIOLATION",
  "detected_entities": ["MRN", "PERSON", "PHONE_NUMBER"],
  "required_lane": "in-tenancy",
  "claimed_data_class": "non-phi",
  "claim_overridden": true,
  "suggested_model": "intenancy-llama",
  "how_to_fix": "Send this request to 'intenancy-llama', or remove the identifiers."
}

claim_overridden: true is the field worth pausing on. The caller labelled that request as containing no PHI. The gateway checked for itself and disagreed. A mistake by a developer cannot widen the boundary.

Checkable before it fails. A developer can ask the gateway whether a payload would be blocked, without calling a model or spending a token:

$ phi_check.py "Patient Marcus Delgado, MRN 4471822, call 919-555-0177."
  BLOCKED  PHI_LANE_VIOLATION · detected: MRN, PERSON, PHONE_NUMBER
           send to 'intenancy-llama' (lane: in-tenancy)

With a non-zero exit code, that becomes a CI assertion. The data boundary stops being something you discover at runtime and becomes something you design against:

assert phi_check(clinical_note)["allowed"] is False

Proving it, in three artifacts

The point of this pattern is that the claims are demonstrable rather than asserted. In a review, three things take about thirty seconds.

One — a request carrying PHI, aimed at a third-party model, is refused. 403, with the detected identifier types named. No provider was contacted; the classification happens in-process, before anything leaves.

Two — the same request, with the caller asserting it contains no PHI, is refused identically. The assertion is recorded as evidence, never honoured as instruction.

Three — the model host cannot reach the internet. A curl from that instance to any public endpoint returns network unreachable. Then, in the same breath, an in-tenancy inference request succeeds — because the gateway reaches the model over the VCN, which is a different path entirely.

Show the route table with zero rules next to a working inference call, and the residency claim stops needing to be believed.

Then the question compliance actually asks each quarter:

PHI executed outside the tenancy:      0
Caller data-class claims overridden:   1

That is a query against a structured record, not an investigation across service logs.


Detection is local, and that is the point

One design decision I would defend harder than any other: the classifier does not call a model.

It is tempting to use an LLM for PHI detection — they are good at it. But sending text to a model to ask whether it contains PHI means the PHI has already left your boundary. The control is inverted before it runs.

So detection is a local NER pass running in-process inside the gateway, with the language model baked into the container image. No network dependency. It cannot fail open because a detection service was unreachable — if it cannot load, the gateway does not start.

Measured overhead: under 6 ms per request.


What this does not do

A pattern that only lists its strengths is marketing. These are the limits, and you should state them before a customer finds them.

This is not HIPAA Safe Harbor. Safe Harbor enumerates eighteen identifier classes. A NER-based classifier covers a subset. Full-face imagery, biometric identifiers and device serials are out of scope entirely.

Site-specific identifiers need work. Universal formats — SSN, email, phone — work out of the box. Medical record numbers need a hand-written pattern. Accession numbers, specimen barcodes and clinical trial subject IDs are invisible until someone writes recognisers for them. Subject 0042 passes undetected. This is the honest answer to “will it work on our data?”: the universal identifiers yes, your formats are a scoping conversation.

It over-denies, deliberately. A hospital switchboard number classifies as PHI. So does a clinician’s name in a staffing note. For a control, failing closed is the correct direction — but production needs context rules, an allow-list and a documented exception path, or people will route around it.

Enforcement is not certification. This enforces where inference runs and records what happened. BAA coverage, clinical validation, and the de-identification method itself remain yours. The architecture gives you a boundary you can evidence; it does not give you an attestation.


The pattern generalises

PHI is one instance of a shape that recurs across every regulated conversation: classify at the door, enforce with topology, record the decision.

DomainSame architecture, different policy
Data sovereigntyEU or in-country workloads pinned to in-region execution
Export controlITAR / EAR technical data restricted to a tenancy-bound lane
Financial servicesMaterial non-public information barred from external providers
Multi-tenant SaaSOne customer’s data may never transit a shared model
Source codeProprietary code excluded from external inference; docs route freely

The gateway does not change. The network does not change. The policy file does.


Build it yourself

The implementation is public, including the scripts that stand up the network, launch both tiers, seal the subnet and tear everything down:

github.com/dkhopade/phi-lane-guard

It runs in about thirty minutes on a fresh tenancy. There is also a version implemented as a native guardrail for a model gateway, for teams already running one, and deployment manifests for OKE.

A note on cost, from experience: the GPU instance bills from launch until termination, boot volume included, whether or not it is serving anything. Set a teardown reminder before you launch, not after.


The thing I would leave you with

Building this surfaced several defects — in my own code, in platform behaviour, and in tooling. Every one shared a shape.

A classifier scoring just below its confidence threshold, detecting nothing, logging nothing. A firewall command that hung rather than failed, so the error-handling around it never triggered. A teardown script that exited on an argument error before deleting anything, while a GPU kept billing. A compliance query reading a documented column that was empty while the data sat in the next column over.

None of them announced itself. Every one looked like success from the outside.

That is the real argument for this architecture, and for testing controls rather than configuring them and assuming. A policy document tells you what should happen. An empty route table shows you what can happen. When the difference between those two is patient data leaving your tenancy, it is worth the afternoon it takes to make the second one true.

The implementation, findings, and reproduction steps are at: https://dkhopade.github.io/phi-lane-guard/