Skip to main content

Security Guardrails

Every chat, message and Responses request is scanned before it is proxied. The scanners run inside the gateway, in parallel, and they run before the cache, before HiveState and before request logging — so a prompt that must not reach a provider is stopped before anything else has seen it, and a cached answer can never be returned in place of a refusal.

The policies​

PolicyName in the dashboardWhat it does
prompt-injectionPrompt InjectionRefuses jailbreaks and attempts to override the system prompt
toxicityToxicity CheckRefuses toxic or harmful content
sensitive-dataPII RedactionReplaces PII with [ENTITY] placeholders and forwards the request. Still refuses on secrets
sensitive-data-blockPII BlockingRefuses with 403 and forwards nothing
abuse-protectionCost & Abuse ProtectionBlocks flooding, oversized prompts and nonsense

Redaction and blocking are two policies, not one setting​

They read the same PII. They differ only in what they do with it.

PII Redaction rewrites the value and lets the request through. The model sees [EMAIL_ADDRESS] where the address was:

Scrivimi a [email protected] → Scrivimi a [EMAIL_ADDRESS]

PII Blocking refuses the request outright:

{"error": {"message": "PII detected: EMAIL_ADDRESS",
"type": "content_policy_violation",
"code": "guardrail_blocked"}}

Both can be on at once, and that is not a contradiction: blocking is evaluated first, so it wins and nothing is redacted-and-sent behind it. The dashboard says so on the card when both are active.

PII Blocking is opt-in

A tenant that has never chosen it does not get it, even though the gateway offers it. Redaction is the default; moving from rewriting a prompt to refusing it is a decision a tenant makes deliberately, not one that arrives with a deployment.

Secrets are always refused, never redacted​

sensitive-data is called PII Redaction because that is what it does to PII. An API key is not a PII entity: replacing it with a placeholder is not what anyone means by protecting it. So the same policy still returns 403 when it finds an AWS key, a GitHub token, an OpenAI key or a private key block.

Which entities are looked for​

The selection is shared by both PII policies and is configured in the dashboard under Security → PII Redaction → Configure:

EntityRecognises
EMAIL_ADDRESSEmail addresses
PHONE_NUMBERInternational (+39, 0039) and Italian mobile numbers
PERSONA name introduced by a phrase — mi chiamo…, my name is…, Sig., Dr.
CREDIT_CARDCard numbers, validated with Luhn
CRYPTOBitcoin and Ethereum wallet addresses
IBAN_CODEItalian and other European IBANs
IP_ADDRESSIPv4 and IPv6
US_SSNUS social security numbers
US_BANK_NUMBERUS account numbers, introduced by a phrase
LOCATIONA place introduced by a phrase — residente a…, based in…

Two more are always on and are not in the list, because they predate it and are not selectable: codice fiscale (checksum-validated) and partita IVA. Unticking every box does not switch those off.

A tenant that has never opened the dialog has no selection stored, and every entity applies. An empty selection means the opposite — nothing is redacted — so the two states are not the same.

What the scanner reads​

Whatever the model can see, on every request shape the gateway serves:

  • chat messages, as plain strings or content blocks;
  • Anthropic's top-level system field;
  • the Responses API instructions and input;
  • the legacy Completions prompt;
  • tool results — tool_result blocks and function_call_output items.

That last one matters more than it looks. A document does not usually arrive as a typed user message: an agent reads the file and its contents come back as a tool result. Pasting a file into the chat and letting an agent read it are the same exposure, and both are covered.

Redaction rewrites exactly what the scanner reads, and nothing else: the envelope of a tool result — tool_use_id, call_id, block types — survives untouched, so the provider can still match a result to its call.

Where it runs​

PII and secrets are handled first, before any component can observe or persist the prompt. The remaining scanners sit under the rate limiter and the concurrency semaphore — the toxicity scanner calls a model, and that call has to be counted and bounded like any other work — but still ahead of the cache.

Per-request override​

x-hive-guard selects the policies for one request, ignoring the tenant's configuration. It can narrow what runs; it cannot widen which entities the PII policies cover.

# no guardrail at all
curl https://api.modelhive.ai/v1/chat/completions \
-H "Authorization: Bearer sk-mh-..." \
-H "x-hive-guard: none" \
-d '{"model": "gpt-4o", "messages": [...]}'

# only these two, whatever the tenant has enabled
curl https://api.modelhive.ai/v1/chat/completions \
-H "Authorization: Bearer sk-mh-..." \
-H "x-hive-guard: prompt-injection,sensitive-data-block" \
-d '{"model": "gpt-4o", "messages": [...]}'
ValueEffect
noneEvery policy off for this request
prompt-injectionInjection only
toxicityToxicity only
sensitive-dataPII redacted, secrets refused
sensitive-data-blockPII refused
abuse-protectionAbuse limits only
Testing a policy

Send x-hive-cache: false alongside x-hive-guard when you are checking whether a policy fires. A cached answer is a legitimate response, and on a prompt you have already sent it will arrive without the request needing to be scanned again — which reads like the guardrail failing when it is the cache succeeding.

Latency​

The scanners run in parallel and add roughly 25–35 ms, against LLM responses measured in seconds. The exception is toxicity, which calls a small model: it is billed to the calling key as @hiveguard spend and is visible in analytics.