Skip to content

OpenWAF Egress

Your AI agents never hold a provider key.

OpenWAF Egress sits between your agents and the model providers they call. Agents get a placeholder token. Every request is checked for prompt injection, data leaks and SSRF, and only then does the gateway fetch the real key from OpenBao or HashiCorp Vault, inject it and forward the request with streaming intact.

27 detectors, no model call

84 µs for a 4 KB chat request

One Go binary, image for amd64 and arm64

Real responses from the gateway binary against a demo OpenBao and a mock provider: the key is injected (200), then a prompt injection, a card number and a request to 169.254.169.254 are refused (403).

How it works

Five checks before the key leaves the vault

The agent keeps its usual SDK. Only the base URL and the key change.

Base URL http://127.0.0.1:8070/v1/agent/<agent>/proxy/<target> · key OWE_TOKEN_…

  1. 1

    Agent token

    401

    The agent sends its placeholder in Authorization: Bearer. A missing or wrong token stops here.

  2. 2

    Target allowlist

    403 egress_blocked

    The target in the URL must be one of the agent’s allowed_targets.

  3. 3

    SSRF check

    403 ssrf_blocked

    The target’s host must resolve only to public addresses.

  4. 4

    Body inspection

    403 injection_detected · data_leak_blocked

    Prompt-injection detectors and data-leak patterns run over the body, without calling a model.

  5. 5

    Key from OpenBao

    → provider

    The real key is read from KV v2, set on the outgoing request, and the response streams back to the agent.

Prompt injection

27 deterministic detectors, English and Spanish

Regular expressions aimed at instructions addressed to a model, not at topics: talking about prompts, shells or secrets does not match. A blocked request gets 403 injection_detected with the id, label and matched text of each hit.

21 pattern detectors

Overrides, jailbreaks, forged messages, tool abuse and exfiltration orders.

  • pi.override.enInstruction override (English)
  • pi.override.esInstruction override (Spanish)
  • pi.sysprompt.enSystem prompt extraction (English)
  • pi.sysprompt.esSystem prompt extraction (Spanish)
  • pi.jailbreak.personaJailbreak persona
  • pi.jailbreak.modeJailbreak mode
  • pi.fake_systemFake system message
  • pi.compliance_noticeFake compliance notice
  • pi.concealConcealment from the user
  • pi.chat_templateChat template tokens
  • pi.fake_toolForged tool call or result
  • pi.fake_turnForged conversation turns
  • pi.memoryPersistent memory poisoning
  • pi.tool_invokeTool invocation order
  • pi.no_confirmApproval bypass
  • agent.destructiveDestructive command order
  • exfil.pipe_shellRemote script piped to a shell
  • exfil.env_secretsSecret harvesting
  • exfil.host_dataHost data exfiltration
  • exfil.markdown_imageExfiltration through a rendered link or image
  • cost.amplificationSpend amplification order (reported, not refused)

6 detectors for hidden or encoded text

Instructions hidden from a human reader. Comments, CSS-hidden elements and encoded blobs are decoded and checked with the pattern detectors.

  • hidden.zero_widthHidden zero-width text
  • hidden.tag_charsHidden Unicode tag characters
  • pi.html_commentInstructions in an HTML comment
  • hidden.css_textInstructions in CSS-hidden text
  • pi.base64Encoded instructions (base64)
  • pi.encodedEncoded instructions (percent or hex)

Detection is pattern matching: it stops known injection and exfiltration shapes, it does not prove a request is safe. The gateway refuses on every hit except cost.amplification, which it ignores. The same detector set protects AI endpoints at the OpenWAF edge.

Protections

Keys, data and destinations

Turn each check on per agent. Every refusal is a JSON error the agent can read.

  • Keys injected from OpenBao or Vault

    Agents carry an OWE_TOKEN_… placeholder. After the checks, the gateway logs in with AppRole, reads the key from KV v2 (cached for 5 minutes) and sets it on the outgoing request only.

  • Per-agent allowlist

    Each agent lists the targets it may reach in allowed_targets. Any other configured target gets 403 egress_blocked before the body is read.

  • Data-leak prevention

    With block_dlp, a body carrying a Luhn-valid card number, a PEM private key, an AWS access key ID, an OpenAI-style sk- key or a GitHub token gets 403 data_leak_blocked.

  • SSRF protection

    Loopback, RFC 1918, CGNAT, link-local (including 169.254.169.254), multicast, reserved and their IPv6 equivalents are refused with 403 ssrf_blocked.

  • No redirects followed

    An upstream 3xx goes back to the agent as is, so the injected key never follows a redirect to a host outside the allowlist.

  • Whole bodies or nothing

    Inline base64 images, audio and video (data: URIs and the "data" fields of OpenAI, Gemini and Anthropic) are not inspected text. The rest must fit the 1 MiB window or the request gets 413; nothing is forwarded uninspected or cut. Bodies are capped at 8 MiB.

Performance

Detection measured in microseconds

Time to run every detector over one request body, from the repository’s benchmarks.

  • 84 µs4 KB chat request
  • 1.3 ms64 KB chat request
  • 97 ms64 KB built to trigger every detector (worst case)

Measured with go test -bench . ./pkg/aiagents/ on an Apple Silicon Mac (darwin/arm64). Each detector only runs when one of its keywords is present, so ordinary traffic skips almost all of them. The static binary is about 7 MB.

Quickstart

Running in two steps

Write a config.json with your targets and agents, then start the container and change the agent’s base URL.

  1. 1

    Run the container on loopback

    The image listens on 0.0.0.0:8070 inside the container. Publish it on 127.0.0.1 or a private network only.

    docker run -d --name openwaf-egress -p 127.0.0.1:8070:8070 \
      -v "$PWD/config.json:/app/config.json:ro" \
      ghcr.io/openwaf-dev/egress:0.1.1
  2. 2

    Point the SDK at the gateway

    Any OpenAI-compatible SDK works: change the base URL and give it the placeholder token.

    import OpenAI from "openai";
    
    const client = new OpenAI({
      baseURL: "http://127.0.0.1:8070/v1/agent/my-support-agent/proxy/openrouter",
      // placeholder; the real key never reaches the agent
      apiKey: "OWE_TOKEN_SUPPORT_AGENT",
    });

Next: the quickstart (OpenBao secret, configuration and a first request) and every configuration field.

Security notes

Where it stops

What to do when you deploy it, and what it does not claim.

Before you deploy

  • The gateway speaks plain HTTP and agent tokens are static: keep it on loopback or a private network, or put TLS in front of it. Never expose it to the internet.
  • Replace the example token OWE_TOKEN_SUPPORT_AGENT with a long random value per agent: the example one is public.
  • Give the gateway’s AppRole read access to the agent keys only.
  • Detection raises the cost of known prompt-injection and exfiltration shapes; it does not prove a request is safe.
  • Report vulnerabilities to [email protected].

FAQ

OpenWAF Egress questions

Does the agent ever see the provider key?

No. The agent only holds its OWE_TOKEN_… placeholder. The gateway reads the real key from OpenBao or Vault after the checks pass and sets it on the outgoing request. The provider’s response goes back to the agent; the key does not.

Does it call a model to detect prompt injection?

No. The detectors are deterministic regular expressions in English and Spanish, and each one only runs when one of its keywords is present. Detection takes 84 µs for a 4 KB chat request on an Apple Silicon Mac.

Which providers does it work with?

Any HTTP API you define as a target: a base URL, a host and the header that takes the key. The examples cover OpenRouter, OpenAI, Anthropic (x-api-key), Gemini (x-goog-api-key) and Hugging Face.

Does streaming still work?

Yes. The response is copied back to the agent as it arrives and flushed after every read, so streamed tokens reach the agent as the provider sends them.

What happens to large requests with images?

Inline base64 images, audio and video, as data: URIs or as the JSON data fields of OpenAI, Gemini and Anthropic, are left out of the inspected text and forwarded unchanged. The rest of the text must fit 1 MiB or the request gets 413, and no body may exceed 8 MiB.

Is it part of OpenWAF Cloud?

It is a separate open-source project under Apache-2.0 that you run yourself, next to your agents. It uses the same detector set as the AI-endpoint protection of the OpenWAF edge.

Take the keys away from your agents.

OpenWAF Egress is free and open source under Apache-2.0. Run the container next to your agents and point their SDK at it.