The protection layer
One script, wire-protection.sh, turns the "before" demo into the "after" demo. It creates
everything below and nothing else; deleting it puts the suite back exactly as it was.
# in config.sh
export DEPLOY_PROTECTION="true" # ./deploy.sh then wires the protection layer too
export PROTECTION_MODE="block" # or "log" to detect without blocking
./wire-protection.sh # apply (safe to re-run)
./wire-protection.sh --remove # tear it back down for a clean "before" run
1. DLP profiles
Five custom profiles, shared by AI Gateway and Secure Web Gateway, written specifically against the data in these apps so the matches on stage are unambiguous:
Where Cloudflare already maintains a detection, these profiles borrow it rather than re-implementing it: US and UK national identifiers, US mailing addresses, ABA routing numbers and US phone numbers are Cloudflare's own predefined entries, referenced by name and resolved at deploy time. The custom regexes are only for what is specific to this company - UK residential addresses, the field names in these apps' JSON, and the vocabulary of its HR case files.
| Profile | Detects |
|---|---|
| Employee PII | Home address formats used in WorkWeek, national ID numbers, bank sort codes and account numbers, dates of birth. |
| HR Case Files | The vocabulary of an HR case file: performance improvement plan, grievance, disciplinary, severance, redundancy, garden leave. |
| Confidential Projects and Transactions | Project Ironwood, the acquisition target's name, the Q1 restructure and board pre-read material. |
| Customer Contact Data | Direct mobile numbers and named customer contacts from Pipeline, plus deal economics (discount and margin). |
| Payment Card Data | Cardholder data pasted into a prompt. Cloudflare's own detectors validate the Luhn checksum rather than matching a shape, so a test card number fires every time — which matters when you are demonstrating in front of someone. |
2. AI Gateway policies
The gateway itself is not created here — wire-access.sh creates it and
publishes it on its own Access-protected custom domain, because that is identity plumbing rather
than inspection. Clients are therefore configured against the same endpoint whether or not this
layer is deployed. What this adds to it:
- DLP policies referencing all four profiles, checking both the request and the response — so it catches a user asking for an address and a model returning one it picked up from a tool result.
- Guardrails covering two different jobs.
Prompt injection blocks on prompts, which is what stops the injected email
and calendar invite from steering the agent. Violent crimes, non-violent
crimes, indiscriminate weapons and suicide &
self-harm block on both prompt and response, governing what staff can make the
company's AI do at all. Privacy is left on flag rather than block: these apps
discuss people all day, so blocking it would break ordinary use, and DLP already covers the
actual identifiers.
The dashboard and the logs label these by code — prompt injection isP1, the four blocked categories areS1,S2,S9andS11, privacy isS7— so that is what you will see on screen. - Logging on, so every one of the demos leaves an entry you can open on stage.
3. MCP server portal
The five MCP servers are registered with Cloudflare Access and published through a single portal at
:
- Users authenticate to the portal through Access, then to each upstream server as themselves
(
on_behalf), so Alice's own permissions still apply upstream. - Access policies control which servers each person even sees in the portal. Four allow the
All Employees policy;
allows only Executives, which is why Ledger's tools are absent from an ordinary employee's tool list rather than merely refused. See the access-control script. - Route traffic through Cloudflare Gateway is turned on, which is what makes the next section possible. The portal terminates the client connection and re-originates it, so Gateway can decrypt and inspect the tool traffic without any account-wide TLS decryption setting.
- Every tool call is logged in Access, independently of Gateway.
Registering an MCP server over the API cannot perform the upstream OAuth login it requires -
that needs a browser - so each server starts in Waiting with no tools. Authenticate
each one as alice.watson@company.com before running any demo — except Ledger,
which only the leadership team can authenticate, so use nikita.chapman@company.com for
that one:
the setup guide has the steps, including why authenticating as the admin
account fails and what to do if a server reports that Gateway blocked its tool sync.
4. Application Library review statuses
Cloudflare keeps a catalogue of SaaS applications, and every one of them carries a review status for your account: Unreviewed, In review, Approved or Unapproved. This layer sets them, because it is what lets the two AI policies below decide anything without naming a single hostname.
| Status | Applications |
|---|---|
| In review | Google Gemini — the assistant this company is piloting, and the destination the redirect below sends people to. |
| Unapproved | ChatGPT, Microsoft Copilot (Free and Enterprise), DataBot, Claude, DeepSeek, Perplexity, Amazon Bedrock. |
| Unreviewed | Everything else, which is the default — including every AI service nobody has heard of yet. |
Gemini is deliberately In review rather than Approved. It has to not match the redirect rule, since it is where that rule sends people, and "in review" is the honest description of a tool a company is still making its mind up about — which is where most of the audience actually is.
5. Gateway HTTP policies
First, TLS decryption is turned on. Without it an HTTP policy only ever sees port 80, and everything these rules read — the URL, the headers, the body DLP inspects — is inside the TLS session. The policies look perfectly healthy while inspecting nothing, and the dashboard says so in a banner above the policy list.
Then one DLP policy per upstream MCP hostname. Gateway policies for portal traffic have to match the
upstream server, not the portal, so there is a rule for each of , ,
, and , each pairing that host with the
profiles that matter for it. In block mode a matching tool call or tool result is blocked and the agent gets an
error instead of the data; in log mode it is allowed and recorded.
Two more rules have nothing to do with MCP, and both match on the Artificial Intelligence content category rather than a list of hostnames — a hostname list is a list of the services you already thought of, and the one someone pastes payroll data into next week will not be on it:
- Prevent sensitive data reaching public AI. Cloudflare's predefined
AI Prompt profiles — PII, Customer, Financial Information and Technical — on
anything posted to an AI service. When it blocks, the Cloudflare One client raises a
desktop notification — "Blocked access to AI due to detection of sensitive data."
— because the browser otherwise shows the person a failed request and no explanation.
Two details worth knowing, both learned the hard way. The rule is scoped to the request body (http.body_phase), because a DLP policy without that selector scans the response as well — which means inspecting the HTML an AI service serves back, and blocking the site on a plain page load. And these are Cloudflare's prompt-aware profiles rather than this demo's custom ones, which are built to read a prompt rather than a web page. - Redirect non approved AI. Anything in the AI category that is Unreviewed or Unapproved is redirected to Gemini instead of being refused. Lowest precedence in the account, so the DLP rule above answers first: someone pasting customer data into ChatGPT is blocked, not quietly rerouted. Nobody's work stops, so nobody goes looking for a way around it — which is the only version of a shadow-AI policy that survives contact with real users.
The five custom profiles guard the MCP path, and Cloudflare's predefined AI Prompt profiles guard the browser path. That split is deliberate, and each is wrong in the other's place.
The AI Prompt profiles are built to recognise sensitive material in a prompt. They do not match the MCP protocol's shapes at all, so portal traffic gets the custom profiles — which are written against this demo's own data and can therefore name Project Ironwood and the vocabulary of an HR case file.
Pointed at a browser, those same custom profiles are too eager: they borrow Cloudflare detections
for things like nine-digit routing numbers, which occur by accident in the JavaScript that every
large web application ships. That is not hypothetical — it blocked
gemini.google.com on a plain page load, which broke the one step of the walkthrough
whose point is that the site still opens.
The cost of the split is worth stating: the browser rule no longer matches this company's project codenames, because Cloudflare's classifiers have never heard of Ironwood. The AI Gateway's own DLP still does, so the sanctioned path keeps that cover.
Where each control shows up
| Control | Where to look on stage |
|---|---|
| AI Gateway DLP and guardrails | AI → AI Gateway → employee-gateway → Logs. Blocked requests show the profile or hazard category that matched. |
| Gateway HTTP DLP policies | Zero Trust → Insights → Logs → Gateway HTTP. Filter by the upstream MCP hostname. |
| The two AI policies | The same Gateway HTTP log, filtered by policy name. Application statuses are on the cards under Zero Trust → Team & Resources → Application library. |
| MCP portal | Zero Trust → Access controls → MCP Portals → the portal's logs: who called which tool, with which arguments. |
| Access | Zero Trust → Insights → Logs → Access, to show the login that produced the identity behind all of it. |
Before any of this runs, the model endpoint is already behind Access on its own custom
domain: AI Gateway accepts a valid Access JWT as the request credential, so an agent
authenticates as a person rather than with an API key, and every call is attributed to that
person as cf.user_id. A device enrolled in the Cloudflare One client authenticates
from its existing session, so there is no credential in the client's config at all.
That is what makes the logs below worth looking at: each one names a human.
What it deliberately does not do
It does not change a line of application code, and it does not fix the leaky endpoints. That is the argument: the apps are still exactly as over-sharing as they were on the data page, and the control sits in the path instead of in a backlog.