Payload Inspection
A backend MCP server returns text that goes straight into a model’s context. If that text contains instructions, the model may follow them. If it contains a credential, the credential is now in the transcript.
The Gateway inspects what passes through it: tool arguments on the way to a backend, and tool responses on the way back.
Every setting on this page lives in the Inspection group on your gateway pool, in the Britive tenant portal. Changes reach a running Gateway on its next sync - there is nothing to restart.
Audit First, Then Enforce
Inspection has three modes, and the order you adopt them in matters.
inspection.mode | Behaviour |
|---|---|
audit | Findings are recorded in the audit trail. The call proceeds unchanged. Default. |
enforce | Matching content is blocked or redacted. |
off | Inspection does not run. |
There is no separate on/off switch: off is what that switch used to mean, and one
field that says whether inspection is running beats two that can disagree about it.
Begin in audit mode. The recorded verdicts show exactly what enforcement
would have done to your real traffic, which is the only practical way to confirm a
detector suits your backends before it starts refusing calls.
Inspection runs in audit mode by default, so you get the findings without any
behaviour change until you decide otherwise.
Inspection Is Best Effort
Treat inspection as a safety net, not a boundary. The detectors are pattern-based and run locally with no model, so they catch known shapes of attack - not every possible one. A novel phrasing, an encoding nobody has characterised yet, or a payload crafted against these specific detectors can pass.
It is worth running because it catches real problems cheaply and tells you what your traffic actually contains. It is not worth treating as proof that what reached the model was safe.
The controls that are boundaries are the ones either side of it: Britive policy decides which tools an identity may call at all, and credentials are scoped and temporary, so what a compromised call can reach is bounded regardless of what any detector missed.
What Enforcement Actually Does
In enforce mode, the action depends on the highest severity finding in the
payload:
| Highest severity | Action in enforce mode |
|---|---|
critical | Block - the call fails |
high | Redact - the offending content is removed |
medium, low, info | Allowed; recorded only |
In audit mode every severity is allowed and recorded, including critical.
A blocked call fails with an INSPECTION_BLOCKED error that names the phase, the
deciding detector, its category and severity, and the finding’s label - “AWS access key
id”, for instance - along with prose telling the model this is a policy decision and
not to retry. The matched snippet and its position stay in the audit sink you control
rather than travelling back out through the model.
inspection.silentRedact is false by default, which means redactions and blocks are
visible to the caller. Set it to true to redact without indicating that anything was
removed.
The Detectors
inspection.detectors is the list of built-in detectors that run. All three are
selected by default, and the list is the whole answer - naming one turns it on, and an
unrecognised name is rejected rather than silently disabling inspection.
| Detector | What it looks for |
|---|---|
promptInjection | Instructions aimed at the model rather than the user - attempts to override its task, exfiltrate context, or issue new directives from inside tool output. |
secretScanner | Credential material in arguments or responses: keys, tokens, and similar. |
obfuscation | Attempts to evade the other two - invisible Unicode tag characters, bidirectional override sequences, zero-width characters inserted into keywords, base64-encoded instruction blocks, and homoglyph or full-width look-alikes. |
All of them run locally. There is no model call and no outbound request.
The URL blocklist
A fourth detector blocks domains you name. It has no entry in the detectors list because it runs whenever either of these is non-empty:
| Setting | Purpose |
|---|---|
inspection.urlBlocklistDomains | Domains to block on sight, for example paste.example.net. |
inspection.urlBlocklistFeedUrl | A feed of blocked domains, merged with the list above and de-duplicated. |
The feed is either a JSON array of strings or one domain per line, where # starts a
comment. Each Gateway fetches it when it starts and again whenever the address
changes - not on every settings save, so editing an unrelated setting does not call out
to the feed’s host.
If a fetch fails, the Gateway keeps the domains it last retrieved and logs a warning. An outage at the feed cannot quietly shrink your blocklist.
Timeouts
Inspection is bounded so it cannot become a latency problem. Both live in the Advanced group:
| Setting | Default |
|---|---|
inspection.requestTimeoutMs | 250 |
inspection.responseTimeoutMs | 500 |
Enforce mode fails closed on timeout. An oversized payload that stalls the
detectors is blocked rather than passed through unchecked. In audit mode a
timeout is recorded at info severity and the call proceeds, since audit mode never
blocks.
Seeing What Triggered a Decision
Response bodies are not stored, because they may contain secrets or personal data.
When you need to see what a detector matched, turn on inspection.debugCaptureResponses
(“Capture blocked responses”).
This copies the response body - up to inspection.debugResponseMaxChars, 10000 by
default - into the audit event for calls that were blocked or redacted only. It
never appears in the stdout line, only in the durable sinks you control.
This is a diagnostic setting. Turning it on means captured response bodies are
written to your audit sinks, and forwarded to your tenant if
audit.platform.includeContent is also on. Turn it off when you’re done.
Findings appear in the admin console in the expanded audit row: the decision and
phase, then each finding’s severity, detector, message, and matched snippet, ordered
worst-first. Inspection events are recorded under the tools.call.inspect action, so
filtering the audit log by that action shows every verdict on its own.
Forwarding those verdicts into your tenant’s audit history is not on by default - add
tools.call.inspect to audit.platform.events if you want them there as well as in
the Gateway’s own log and your SIEM.
Rolling It Out
Run in audit mode
Leave Mode on its default of audit and let real traffic flow for a week.
Review the findings
In the admin console, filter the audit log for inspection events and look at what matched. You are answering two questions: is anything being flagged that is actually fine, and is the severity right for your traffic?
Switch to enforce
Set Mode to enforce on the pool. Every gateway in it picks the change up on its
next sync, within five minutes by default.
Critical findings now block and high findings redact. Watch for tools.call.inspect
events with a block decision - those are calls that would previously have
succeeded.
Verify
- A tool call with an innocuous payload produces no findings and is unaffected.
- In
auditmode, a response containing an obvious injection string produces atools.call.inspectevent with findings, and the call still succeeds. - In
enforcemode, the same call is blocked, and the audit row shows the decision, the phase, and each finding worst-first.