Back to blog

How to evaluate an AI traffic proxy with policies and sensitive data redaction

A support API sends tickets to a language model. A worker summarizes incidents from logs. An assistant retrieves internal documents and invokes tools. Each needs access to inference, but none should indiscriminately transmit email addresses, credentials, or confidential business information.

OWASP identifies sensitive information disclosure as a risk for applications using large language models, or LLMs. Prompt instructions are not a replacement for access controls and appropriate data handling. (genai.owasp.org)

This article defines an evaluation protocol: establish which bytes cross the boundary, which decisions are enforced, and what happens when dependencies fail. The criteria and numbers below are engineering proposals, not product benchmarks or guarantees.

The scope complements network capacity planning for agent sandboxes. Here, the focus is content inspection and security decisions rather than another network isolation configuration.

1. Define what you are evaluating

For this evaluation, an AI traffic proxy is an intermediary that receives application requests and forwards them to inference services. It must provide or integrate authentication, policy decisions, content inspection, and transformation.

Keep three responsibilities separate:

  • Gateway: route requests, manage connections, and enforce limits.
  • Policy engine: decide whether an operation is permitted.
  • Detector and transformer: identify sensitive content and modify approved fields.

Open Policy Agent, or OPA, evaluates structured data using Rego. It can produce an authorization decision, but the integration remains responsible for enforcing that decision. (openpolicyagent.org)

Specify what redaction actually means for each data category:

ActionExampleProposed evaluation criterion
RemovalDelete a credentialNo usable fragment remains
ReplacementReplace an email address with [EMAIL]The original value never crosses the boundary
Partial maskingRetain part of an identifierThe remaining portion is explicitly permitted
Reversible tokenizationReplace a person with PERSON_17Isolate the mapping and authorize restoration

Presidio separates entity identification from transformation through operators. Its documentation explicitly warns that automated detection cannot guarantee finding all sensitive information. No findings must therefore not be treated as proof that no sensitive data exists. (data-privacy-stack.github.io)

As a design rule, do not treat identifier replacement as a guarantee of anonymity. Context, relationships, and reversible mappings also belong in the risk assessment.

2. Architecture and trust boundaries

The proposed reference architecture is:

Application → authentication → structural validation → inspection → transformation → final authorization → provider adapter → model

The return path is:

Model → output inspection → transformation or blocking → application

The control plane publishes policies and configuration. The data plane applies them to individual requests. Require separate responsibilities and permissions for these planes.

The evaluation contract should require:

  1. Identity derived from verified credentials, not an arbitrary X-Tenant header.
  2. Size, decompression, and execution limits before expensive processing.
  3. Inspection of every relevant field in the supported protocol.
  4. A decision bound to the body actually forwarded.
  5. Provider credentials unavailable to applications when credential separation is part of the security model.
  6. Fresh destination, region, and model checks for every fallback.

Inventory conversation history, retrieved documents, tool arguments, files, images, audio, responses, errors, and telemetry. Mark each surface as inspected, blocked, or out of scope. Under this proposal, unsupported content is not implicitly authorized.

Changing an application endpoint must be accompanied by a test that attempts direct provider access. For tools and Model Context Protocol, or MCP, extend the assessment with MCP authentication and trust boundaries.

3. Write the policy before comparing products

Start with a decision table approved by the teams responsible for the data:

ConditionRequired behavior in this example
Invalid identity or unknown tenantDeny before inference
Credential or private key detectedBlock
Email address in a ticket with an allowed classificationReplace and verify the transformation
Restricted documentDeny external provider access
Unsupported formatReject explicitly
Detector unavailable or inspection incompleteBlock the protected request
Unapproved fallback destinationDo not execute the fallback

Define precedence: a blocking rule must override a permissive rule. Record a version, owner, rationale, and expiration date for exceptions.

Also specify behavior when the control plane is unavailable. One option is to keep using the last validated policy bundle for an approved period without accepting changes. Without a valid policy, the recommendation for protected routes is to deny access.

An executable authorization example with OPA

The following example uses Rego v1. It is an educational post-transformation authorization policy, not a detector, redactor, or proxy implementation. Its input fields form an internal contract for this example; they are not product configuration properties.

Save this as policy.rego:

package ai.egress

import rego.v1

default allow := false

allow if {
    input.identity.tenant == "support"
    input.destination == "approved-provider"
    input.model == "support-approved"
    input.original_classification in {"public", "internal"}
    input.scan.complete == true
    input.scan.remaining_sensitive == 0
    input.transform.status == "verified"
}

approved-provider and support-approved are illustrative internal aliases. In production, the adapter must resolve them to approved destinations without allowing a client to substitute an arbitrary URL.

Save this as input.json:

{
  "identity": {"tenant": "support"},
  "destination": "approved-provider",
  "model": "support-approved",
  "original_classification": "internal",
  "scan": {"complete": true, "remaining_sensitive": 0},
  "transform": {"status": "verified"}
}

With OPA 1.x installed, run:

opa eval --fail --data policy.rego --input input.json 'data.ai.egress.allow == true'

The comparison with true matters. --fail fails on an undefined or empty result; simply querying a defined decision whose value is false does not have the same behavior. (openpolicyagent.org)

Repeat the test with missing identity, a different destination, incomplete inspection, and a restricted classification. Each must be denied.

The proxy must construct this metadata from trusted components. remaining_sensitive: 0 describes a detector result, not an absolute truth. Set verified only after completing the required transformation and validating the final body. Never accept these assertions directly from a client or reuse approval for another body, tenant, or policy version.

4. Build a corpus that can fail the product

Use synthetic data with expected labels at both entity and request level. Include Brazilian Portuguese and English, benign examples, and ambiguous cases. Vendor demonstration samples are not enough.

The proposed corpus should cover:

  • Email addresses, phone numbers, Brazilian CPF identifiers, addresses, and internal identifiers.
  • Synthetic secrets with representative formats, never active credentials.
  • Unicode, unusual separators, multilingual text, and adjacent entities.
  • Nested JSON, conversation history, retrieved documents, and tool arguments.
  • Large, compressed, malformed, and unsupported payloads.
  • Entities split across streaming events.
  • Errors, retries, fallback, caches, and cross-tenant isolation.

Keep recognizer tuning data separate from the acceptance set. Require evidence for the specific languages and document types you need rather than inferring coverage from a product name.

Quality metrics

Use explicit definitions:

  • Precision: TP / (TP + FP).
  • Recall: TP / (TP + FN).
  • Request-level leakage rate: requests forwarding any prohibited content divided by requests containing prohibited content.
  • Utility preservation: task performance after transformation compared with an approved reference.

TP, FP, and FN mean true positives, false positives, and false negatives. Decide whether a match must cover the entire entity. Masking half of a credential should not count as success.

Under this proposed protocol, any leakage in the critical acceptance set fails the release. Record the size and composition of that set: zero observed failures does not establish zero risk outside it.

5. Inspect the bytes, not just the decision

Create an isolated environment with a controlled test destination in place of the provider. Capture the bodies received after transformation and protocol adaptation, using synthetic data only.

For each case:

  1. Submit an input containing a non-sensitive test identifier.
  2. Capture what reaches the destination and the client.
  3. Check that prohibited values are absent and the expected transformation is present.
  4. For blocked requests, verify that no inference call reached the destination.
  5. Search for the synthetic markers in logs, traces, errors, and temporary storage.

An HTTP 403 at the client is insufficient evidence. The test must rule out the possibility that the proxy forwarded the body before returning the denial.

For reversible tokenization, also test restoration from another session and tenant, mapping expiration, and storage failure. In this design, failing to restore data is preferable to restoring it for the wrong recipient.

6. Streaming requires a precise guarantee

Evaluate request streaming and response streaming separately. For requests, the protected boundary is the provider; for responses, it is the final consumer.

Make the test destination emit a sensitive entity in pieces, such as ana@ followed by example.test. Acceptance must depend on the concatenated content received by the client, not merely the inspection result for each isolated chunk.

Require the implementation to document one of these strategies:

  • Buffer the full content before release.
  • Use a retained window sufficient for a clearly bounded pattern class.
  • Disable streaming on routes requiring complete inspection.

A finite window must not be presented as a guarantee of detecting arbitrary secrets of unlimited length. Specify maximum sizes, retention time, and behavior when the required guarantee cannot be met.

For Envoy integrations, ext_proc supports external processing. However, observability_mode observes traffic without applying processor responses; it is not enforcement. failure_mode_allow also changes failure handling. Check the documentation matching the deployed version. (envoyproxy.io)

Include cancellation, backpressure, timeouts, structured JSON, and tool arguments in the tests. If a response has already started, require an interruption signal the client recognizes as failure, without silent retries that repeat tool side effects.

7. A production-like scenario

Problem: a support platform runs an API and workers on Kubernetes. Tickets contain contact details and occasionally credentials pasted by users.

Proposed solution: centralize inference access, retain the original data classification, redact allowed contact information, and block secrets.

Implementation: authenticate workloads; prevent unauthorized direct egress; inspect before forwarding; keep policies in Git; run synthetic tests in the delivery pipeline; roll out through tenant-level canaries.

Expected outcome: reproducible evidence of blocking, transformation, and isolation, together with measured latency and utility impact. No percentage improvement or complete risk elimination is assumed.

Kubernetes: make autoscaling part of the experiment

The complete manifest below creates only a HorizontalPodAutoscaler, or HPA. It assumes that namespace ai-gateway and Deployment ai-proxy already exist and are configured for the selected product. It does not install a proxy or enable redaction.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: ai-proxy
  namespace: ai-gateway
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ai-proxy
  minReplicas: 3
  maxReplicas: 12
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 65
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 25
          periodSeconds: 60

The HPA measures CPU utilization relative to requests and requires an available metrics API. scaleTargetRef identifies the Deployment; behavior controls scale-down. (kubernetes.io)

Three to twelve replicas, 65%, and the time intervals are illustrative. Adjust them through load testing. For this assessment, also track open connections, queue depth, and buffered bytes rather than relying on CPU alone. Autoscaling is not a substitute for admission limits or available node capacity.

Require reviewed requests and limits, zone distribution, a disruption budget, and stream draining. Topology distribution and a PodDisruptionBudget address different availability concerns; the latter does not prevent involuntary failures or govern Deployment rolling updates. (kubernetes.io)

Probes have separate responsibilities: startup protects initialization, readiness determines traffic eligibility, and liveness identifies a need to restart. For this design, do not make liveness depend on the external provider being available. (kubernetes.io)

Development versus production

In development, use one replica, synthetic data, and a simulated destination. Preserve blocking behavior from the start.

For production, add multiple replicas, concurrency limits, alerts, workload isolation, and tested recovery. Require a dedicated ServiceAccount, minimal role-based access control, or RBAC, TLS between components, and workload identity for secret-manager access. Where Secrets are necessary, control access and rotation; do not place credentials in images or Git.

Back up and restore-test policies and configuration. If reversible mappings are persisted, define recovery, expiration, encryption, and tenant separation. Retaining raw payload copies should not become the backup strategy.

8. Observability must not become another leakage path

OpenTelemetry recommends addressing sensitive data in telemetry and avoiding unnecessary collection. The evaluation must cover instrumentation as well as the payload sent to the model. (opentelemetry.io)

For the proposed design, record a random request identifier, policy version, action, detected category, logical destination, duration, and sizes. Do not record complete prompts, responses, credentials, or matched sensitive values.

Build dashboards for:

  • Inspection, decision, and end-to-end latency at p50, p95, and p99.
  • Time to first token and stream duration.
  • Throughput, queue depth, connections, buffer memory, and restarts.
  • Expected policy denials separately from operational errors.
  • Detector timeouts and inconsistent policy versions.

Define a service-level objective, or SLO, for eligible requests. A correct denial is not an availability failure; an otherwise permitted request blocked by infrastructure failure is a different operational event. Avoid user identifiers and sensitive values as metric labels.

9. Performance, cost, and FinOps

Compare runs with and without inspection using the same corpus, concurrency, payload sizes, and streaming profile. Separate proxy overhead from model variability. Do not estimate overhead by subtracting percentiles from independent experiments.

As an initial buffer estimate, suppose there are 400 concurrent requests retaining 512 KiB each. That is approximately 200 MiB of content alone. These are hypothetical values. Add copies, internal structures, connections, and detector memory, then validate peak usage under realistic load.

Include inference, retries, detectors, nodes, cross-zone traffic, network egress, load balancers, telemetry, and temporary storage in the cost model. Compare cost per useful completed task rather than cost per accepted request alone.

The recommendation is to retain stable capacity for the security boundary and consider Spot capacity only with sufficient interruption headroom. Require caches to be partitioned by tenant, model, and policy version. Start without sensitive-content caching until isolation and invalidation have been demonstrated.

10. Troubleshooting and fault injection

SymptomPossible causeInvestigationProposed correction
Client sees a denial, destination receives dataDecision enforced too lateInspect the synthetic destination captureHold the body until authorization
Leakage occurs only with streamingIndependent chunk inspectionSplit entities across eventsUse adequate buffering or disallow the mode
Excessive Portuguese false positivesUnsuitable recognizersReview results by language and entityTune and validate against a separate set
Errors appear when the detector failsFail-closed behavior is workingSeparate policy denials from dependency failuresRestore capacity without enabling bypass
HPA does not scaleMissing metrics or requestsInspect HPA conditions and Pod metricsFix prerequisites and available capacity
Large payloads trigger restartsExcessive buffering or concurrencyInspect memory, events, and OOMKilled statusLimit admission and sizes; right-size resources

Real commands for the described environment:

kubectl get pods -n ai-gateway
kubectl get hpa -n ai-gateway
kubectl describe hpa ai-proxy -n ai-gateway
kubectl top pods -n ai-gateway
kubectl get events -n ai-gateway --sort-by=.metadata.creationTimestamp
kubectl rollout status deployment/ai-proxy -n ai-gateway

Inspect logs only after verifying their sanitization policy. Do not enable unrestricted production body capture to troubleshoot redaction.

In the test environment, interrupt the detector, control plane, and token store; inject latency; exhaust limits; and simulate zone loss. Required outcomes must distinguish controlled degradation from forwarding without inspection.

11. Upgrades, rollback, and acceptance gates

Version the proxy, adapters, policies, recognizers, and language configuration together. Pin artifacts by version and digest where available. Do not promote a mutable tag without recording the effective artifact.

Run regression tests in the delivery pipeline, use synthetic canaries, and expand gradually. Passive observation can help identify differences, but it must not replace an existing mandatory enforcement layer.

Rollback must restore a compatible application-policy combination. Direct provider access is not an acceptable availability recovery mechanism. Before broad rollout, test reversal, restoration of required state, and handling of in-flight connections.

Apply hard acceptance gates before any weighted score:

  • No leakage in the approved critical corpus.
  • Denied requests produce no inference call at the destination.
  • Tenant isolation and no bypass in the defined tests.
  • Explicit behavior for streaming and unsupported formats.
  • Inspection failures do not trigger unauthorized forwarding.
  • Telemetry contains none of the corpus's sensitive markers.
  • Latency, capacity, and cost stay within agreed limits.
  • Rollback is reproducible and operational ownership is assigned.

12. When to use it, alternatives, and limits

This approach is appropriate when multiple applications need consistent policies, controlled destinations, and centralized evidence, and a team can operate the added dependency.

For a small application with data already minimized and only a few flows, an explicit application-level layer may be simpler. Require equivalent content and failure tests regardless of implementation location.

Use the following as an architectural decision framework:

AlternativeExpected benefitTrade-off to validate
Application libraryBusiness context close to the decisionConsistency across languages and versions
Managed gatewayLess infrastructure to operateRetention, region, coverage, and vendor dependency
Extensible proxy with an external engineControl and composabilityIntegration, availability, and maintenance
Private model with source-side minimizationGreater control over the data pathCapacity, cost, and internal access protection

Do not select a proxy solely for its integration list. Do not treat it as a substitute for tool authorization, data classification, or application security.

Practical summary

Evaluate both decisions and bytes. Test representative synthetic data, streaming, errors, and alternate paths. Separate detection from transformation, and transformation from authorization. Require isolation and recovery evidence before optimizing throughput.

Conclusion

An AI proxy should be accepted as part of a trust boundary, not merely as a model router. For Platform Engineering, that means clear contracts and reusable capabilities. For SRE, it means measuring availability without concealing security failures. For DevOps, it means turning policies and leakage tests into versioned delivery artifacts.

The final decision should combine demonstrated protection within the tested scope, preserved task utility, and sustainable operations. If those three conditions cannot be established, narrow the scope or simplify the architecture rather than allowing uncontrolled traffic.

Want to apply this to your environment?

Book a free 30-minute call. I will look at your setup and tell you where to start.

Book a call

Get new articles by email

One email when a new article on infrastructure, backend and AI is published. No spam, unsubscribe anytime.