Repository navigation
verify: capture the F5 Guardrails block (HTTP 200) live #1186
Description
What needs verifying
The F5 Guardrails (F5 AI Guardrails / CalypsoAI) response shapes, captured live against a deployed proxy. The expected shape below comes from the policy's own contract (f5-guardrails-policy 1.0.0), not from a capture. Nobody has F5 AI Guardrails access yet: F5 offers no public trial, only a sales proof of concept. This issue owns the live capture, so #1177 can implement against a confirmed shape, or ship from the contract the way #1176 did and close.
Expected shape, which the live capture must confirm or correct:
-
Block: HTTP
200, not403. The body is the caller's native refusal format, carrying the messageYour request was blocked because it matched: <guardrail names> (phase request, scan <id>).:- OpenAI: a
chat.completionwithchoices[0].finish_reason: "content_filter"and"model": "f5-guardrails"; - Anthropic:
stop_reason: "refusal"; - Gemini:
finishReason: "SAFETY".
Some public docs still describe a
403. That was the shape of earlier versions; 1.0.0 returns200. - OpenAI: a
-
Headers (prefix
x-llm-proxy-f5-calypsoai-guardrail-):-action:allow,flag,redactorreject;-phase;-reason: the display names of the scanners that fired;-ids: the scanner IDs;-scan-id: the F5 scan ID.
-
Streaming: a blocked call is never streamed. The block comes back as non-stream JSON even when the request had
stream: true. -
Non-blocking:
flagandredactlet the call continue, andredactrewrites the body with F5's masked text. WithapplyRedaction: false, aredactoutcome becomes a200block. A failed redaction is also a200block, with-reason: redaction_failed. -
Fail-closed:
503with body{"error":{"message":"F5 AI Guardrails call failed","type":"service_error","code":"<reason>"}}. The reasons areauth_failed,bad_request,not_found,upstream_error,connection_errorandparse_error. A missing verdict returns"F5 AI Guardrails returned no verdict"withnone_response.
Why it blocks work
#1177 (detect the 200 block) needs a new 2xx refusal hook in the shared transport. Building that hook against an uncaptured shape risks one of two failures: misreading a genuine model content_filter stop as a block, or missing a real block entirely. The capture settles which header and body fields the hook can safely key on.
Cross-surface impact
- core: feeds refusals: detect the F5 Guardrails block, which returns HTTP 200 #1177. The capture decides the discriminator (
-action: rejecton a2xx) and the_wireheader constants. - llm: the streaming path. Confirm what a
stream: truecall receives when it's blocked. - registry / tools / integrations: not affected by the capture itself.
- TypeScript parity (planned): not affected by the capture itself.
- CLI:
donkey mockwill serve the captured200fixtures once refusals: detect the F5 Guardrails block, which returns HTTP 200 #1177 lands. - Nextra docs site: not affected by the capture itself.
- docs/verified-apis.md: a new F5 row, VERIFIED (LIVE), with date and instance.
Depends on
- Donkey-Development-Kit/donkey-development-kit-provisioning#61: the live proxy, which needs F5 AI Guardrails credentials.
Related
- refusals: detect the F5 Guardrails block, which returns HTTP 200 #1177: the typing, which needs this capture.
- verify: capture the CrowdStrike AIDR Guardrails rejection live #1184: the CrowdStrike AIDR sibling capture.
- upstream: guardrail-verdict error contract is under-documented #251: the upstream guardrail-verdict contract gap. Why F5 blocks with
200when every other guardrail uses403. - Donkey-Development-Kit/donkey-development-kit-acceptance#69: acceptance coverage.
Done when
- A live
200block captured for an OpenAI-format call: status, allx-llm-proxy-f5-calypsoai-guardrail-*headers and the exact body. - The same block captured on a
stream: truecall, confirming it arrives as non-stream JSON. -
flagandredactoutcomes captured, if reproducible. - A fail-closed
503captured, if reproducible (for example with a bad token, which should giveauth_failed). - The captures committed as fixtures for refusals: detect the F5 Guardrails block, which returns HTTP 200 #1177, and an F5 row added to
docs/verified-apis.mdas VERIFIED (LIVE).
Activity