Skip to main content
The Google Model Armor policy sends request prompts and model responses to Google Cloud Model Armor and applies the verdict at the gateway. One call covers five filters: sensitive data, Responsible AI categories, prompt injection and jailbreak, malicious URLs, and CSAM. It either blocks the request, or rewrites it in place — replacing detected sensitive data with the de-identified text Model Armor itself returns, before the request reaches the model. The model never sees the original.
Only the last user message is sent on the request leg. Not the system prompt. Not the earlier turns. Not tool definitions, tool calls or tool results. Not attachments. The gateway walks back through the messages, takes the most recent one whose role is user, and sends that single string — and if there is no user message with text in it, the request is forwarded without calling Model Armor at all.This is the single biggest limit on the policy, and it is not configurable. An instruction planted in a system prompt, smuggled into a tool result, or built up across several turns is invisible to this guardrail. Size your expectations around one message.
This page covers the whole setup, from an empty Google Cloud project to a verified policy. No prior Model Armor experience is assumed.
Every request is inspected — that call cannot be turned off from the console. How much it costs depends on your region, your template and the size of the prompt, so measure it in your own environment rather than assuming. Only once you have measured that should you decide about Inspect model responses, which adds a second call per turn.

Part 1 — Build the template in Google Cloud

Everything in this part happens in your own Google Cloud project. TrustGate never creates or changes anything there.
1

Choose a region and enable the API

Model Armor has no global endpoint. It is reachable only through regional hosts, and the gateway derives the host from the Location you will enter in the policy. Pick a region close to your gateway: every request makes this round trip.Enable the API in the project that will own the template:
Every resource below must be created in that same project and that same region. A template created in a different region is invisible to the gateway and calls fail with a template-not-found error.
2

Create the inspection template — the first half of masking

This step and the next one are a pair, and masking needs both. Skip both if you only want to block sensitive data and never rewrite it; skipping only this one leaves the next step with nothing to pair with, and the Model Armor template in step 4 asks for the two paths together.In the Google Cloud console go to Security → Sensitive Data Protection → Configuration → Templates → Create template.Click Create and copy the template’s full resource path — you will paste it into the Model Armor template:
3

Create the de-identify template (this is what makes masking work)

Return to Create template and this time choose De-identify (remove sensitive data).Click Create and copy its resource path:
Without a de-identify template there is no masking, and the request is blocked instead. An inspect-only configuration reports what it found but returns no rewritten text, so the gateway has nothing to put back. Rather than forward the original, it refuses the request. Choosing Anonymize in TrustGate without this template turns the policy into a blocking one.
4

Create the Model Armor template

Go to Security → Model Armor → Create template.Then enable the detections you want:CSAM detection is always on in Google’s template and cannot be turned off.Click Create.You now have the three values the policy needs: the project, the region, and the template ID.

Part 2 — Grant the gateway access

TrustGate must prove to Google that it may call Model Armor in your project. It does that with a service account key: you create a service account in your project, give it access to Model Armor, and paste its key into the policy. You revoke access by deleting the key in Google Cloud and, if you want to replace it, pasting a new one into the policy.
1

Create a service account and give it Model Armor access

roles/modelarmor.user is not part of Editor or Owner. Granting it needs the resourcemanager.projects.setIamPolicy permission, which project editors do not have. If the second command is refused, a project administrator has to run it.
IAM changes take a minute or two to propagate. A permission error on the first call right after granting is expected — retry before changing anything.
2

Create a key for the service account

Paste the contents of the JSON file into the policy.The key is never returned to the browser. Reopen the policy and the field shows only that a key is set, not the key itself: you replace it rather than read it. Google Cloud hands you the key file once, when you create it, so keep your own copy where you keep your other secrets, and rotate it on your normal schedule.
The credential field cannot be left empty. The form refuses to save until the Service account key (JSON) has a value.

Part 3 — Configure the policy in TrustGate

Open Policies → Library → Google Model Armor.

Google Cloud connection

Block on

Five toggles — Sensitive data, Responsible AI, Prompt injection & jailbreak, Malicious URIs, CSAM. A new policy in the console starts with no filter selected, and the console requires at least one before it will save.
A stored policy with no block_on at all blocks on all five. That shape does not come from the console, which always saves an explicit selection — it is what you get from a policy created directly through the API with the field left out. Absent entirely, the plugin defaults to every filter Model Armor can evaluate, rather than silently letting traffic through because nobody opted a category in.
These toggles decide what is evaluated, not only what is blocked. A filter you leave off is never read out of Model Armor’s answer: it cannot refuse a request, and nothing about it reaches the event either, so there is no “detect it but do not block on it” for a single filter. Turning off Sensitive data also turns off masking: the Sensitive data action below has no effect unless that toggle is on.

Sensitive data

Sensitive data action — Block request (the default) or Anonymize. Anonymize reinjects the de-identified text and lets the request continue; it requires the de-identify template from Part 1.

Inspection

The request is always screened. Every call on its way to the model goes through this policy — there is no control to turn that off, and it is one call to Model Armor per request. Inspect model responses, in this Inspection section — off by default. Turn it on and the model’s answer is screened too, under the same Block on toggles and Sensitive data action as the request: a second call to Model Armor on every turn. There is no way to screen the response only — the request is always the first call.
With Inspect model responses on, a streamed response is inspected as it is generated, and a failure of that check lets the stream continue.

Mode

Start in Observe, which records every decision without blocking anything, and switch to Enforce once the decisions look right.

How the decision is made

1

Blocking filters are checked first

If any enabled filter matches and calls for a block, the request is refused and never reaches the model. The caller gets 403. The refusal wording is fixed, This content violates our usage policy., with nothing to configure, and it names the filter that fired: sensitive data, Responsible AI, prompt injection and jailbreak, malicious URIs, or CSAM.
2

Masking applies only if nothing blocked

With Anonymize, the de-identified text replaces the original and the request continues to the model.
3

Otherwise the request passes through untouched

A block anywhere discards the masking. If sensitive data would be masked but another filter blocks the same request, the block wins and nothing is sent.This has a practical consequence worth knowing before you demonstrate the feature: Responsible AI classifies some sensitive data — a bare payment card number, for instance — as dangerous content. With Responsible AI enabled, a prompt you expected to be masked is refused instead. If you want masking rather than refusal for that content, turn off Responsible AI or raise its confidence thresholds in the Model Armor template.

Modes and failures

Observe never masks. It records what would have happened and forwards the original text. Masking is only ever applied in Enforce. The decision recorded on the event is allowed, block, anonymized, reported, failed_open or failed_closed (the content caused the failure and Enforce refused it). anonymized is Enforce-only: in Observe a prompt that would have been masked records reported instead, which is also what Observe records in place of a block. failed_open is the one to look for first on a new policy — it is what Model Armor being unreachable, or a credential that cannot be resolved, records on every request, in Enforce and Observe alike. The event’s failure_reason field says why: transport (the call failed, timed out, or Model Armor answered with an error), verdict_incomplete (a filter selected in Block on did not run), config_invalid (the stored settings could not be used), or decode_failed (the gateway’s own body could not be decoded before Model Armor was ever called).
A Model Armor outage or a configuration problem fails open in Enforce. If Model Armor cannot be reached, times out, answers with a server error or rate limits, or the credential is rejected, requests are forwarded unscreened rather than refused, and the caller sees a normal reply. Only the event and the trace record it, as failed_open with a failure_reason. Validate in Observe, and treat a run of failed_open as the policy not guarding anything.A failure caused by the content is different: a buffered text larger than 512 KiB, a filter Model Armor skipped that decides blocking, a body the gateway cannot decode, or Anonymize when the masked text cannot be applied. In Enforce the request is refused with 403, because forwarding it would send the content unscreened. In Observe these are only recorded and never block.
A filter that did not run counts as a failure, not as a clean result. Model Armor can answer successfully and still report that one of the filters you selected in Block on never executed. Silence from a filter that did not run looks exactly like silence from a filter that found nothing. If Model Armor skipped it, for example because the content is above its token limit, the policy refuses in Enforce with failure_reason verdict_incomplete. If your template does not enable the filter, that is not the content’s fault: the request continues, recorded as failed_open. Deselect the filter if you do not need it, or check it is still configured on the Model Armor template.

Limits

  • Only the last user message is inspected on the request leg. No system prompt, no conversation history, no tool definitions, no tool calls or results.
  • Masking that fails is a block. If Model Armor returns no de-identified text, or the request format cannot be re-encoded with it, the request is refused in Enforce rather than forwarded. The original is never sent.
  • A filter that did not run is not a result. Skipped by Model Armor, it blocks in Enforce; not enabled in the template, it fails open. See the note above.
  • Inspect model responses is off by default. Only the request is screened until you turn it on; turning it on doubles the calls to Model Armor per turn.
  • A streamed response is inspected as it is generated, with Inspect model responses on. If the check itself fails, the stream is not cut.
  • Text only. Files, images, and audio are not sent to Model Armor. A request on a route that carries no chat is not inspected: it is forwarded and recorded as not inspected, never refused. The same holds for an upstream error response, which carries no completion to inspect.
  • Size. Below 512 KiB the whole text goes to Model Armor, never a part of it. Above 512 KiB a buffered text is refused in Enforce, before the call. If Model Armor itself skips a detection, for example above its token limit, Enforce refuses too.
  • LLM traffic only. The policy does not apply to MCP (Model Context Protocol) tool calls.

Verify the policy

Open the Playground, pick the application the policy applies to, and send these three prompts in this order. Starting with the control means a failure tells you something specific.
1

Control — the path works at all

Expect a normal reply and the decision allowed. If this fails, the problem is the application, the model or the provider — not the guardrail. Fix it before going on. A decision of failed_open here means the gateway could not get an answer out of Model Armor at all: the location, the credential, or the grant. The reply looks normal, so read the decision.
2

Sensitive data — masking works

With Anonymize and the policy in Enforce, expect the decision anonymized. In Observe the same prompt records reported and the original text still reaches the model, so switch to Enforce before reading this step.Read the reply, not just the status. It must not contain the email address. That is what proves the model never received it.Use an email address rather than a payment card number — see the warning above about Responsible AI.
3

Prompt injection — blocking works

Expect the decision block. To prove the request never reached the model, open Activity, open the request, and read its Policies tab: the Aggregated Latency breakdown there has a Provider row, and that row is empty. The Playground shows a single latency figure and does not break it down.
Every decision is emitted as a metadata event, in Observe mode too, where nothing is blocked. See the event schema for the fields recorded.

Troubleshooting