DLP Pipeline Architecture
import { Aside } from ‘@astrojs/starlight/components’;
DLP Pipeline Architecture
Section titled “DLP Pipeline Architecture”This reference documents the Arbitex DLP pipeline as a system: how requests flow from gateway ingress through detection, policy evaluation, action dispatch, and audit event creation. For per-tier implementation details (regex patterns, NER model, DeBERTa), see DLP Pipeline — Technical Reference.
System Overview
Section titled “System Overview”The DLP pipeline intercepts every AI request and response passing through the Arbitex gateway. It operates as an inline inspection layer — not an out-of-band scanner — meaning the gateway holds the request until inspection completes before forwarding to the AI provider or returning the response to the client.
Client → [Arbitex Outpost] → [Arbitex Platform] → [AI Provider] ↑ DLP Pipeline (inline) Policy Engine (inline)The pipeline has three primary responsibilities:
- Detection — Identify sensitive content using a 3-tier detection stack
- Policy evaluation — Apply org-specific policy rules to the findings
- Action dispatch — Execute the policy decision and produce an audit record
End-to-End Data Flow
Section titled “End-to-End Data Flow”┌─────────────────────────────────────────────────────────────────────┐│ Arbitex Platform ││ ││ Client Request ││ │ ││ ▼ ││ ┌─────────────┐ ││ │ Gateway │ Auth, rate-limit, request ID allocation ││ │ Middleware │ ││ └──────┬──────┘ ││ │ request_text (prompt) ││ ▼ ││ ┌──────────────────────────────────────────────────────────────┐ ││ │ DLP Pipeline │ ││ │ │ ││ │ ┌──────────────┐ ┌──────────────────┐ │ ││ │ │ Tier 1 │ │ Tier 2 │ │ ││ │ │ RegexDetect │◄──┬───►│ NER GPU :8200 │ │ ││ │ │ (in-process)│ │ │ GLiNER v2.1 │ │ ││ │ └──────┬───────┘ │ └────────┬──────────┘ │ ││ │ │ │ │ │ ││ │ └─────────┬─┘ │ │ ││ │ ▼ ▼ │ ││ │ ┌───────────────────────┐ │ ││ │ │ Merge + Deduplicate │ (longest span wins) │ ││ │ └──────────┬────────────┘ │ ││ │ │ merged_findings[] │ ││ │ ▼ │ ││ │ ┌───────────────────────┐ │ ││ │ │ Tier 3 │ │ ││ │ │ DeBERTa :8201 │ │ ││ │ │ Contextual validate │ │ ││ │ └──────────┬────────────┘ │ ││ │ │ validated_findings[] │ ││ │ ▼ │ ││ │ ┌───────────────────────┐ │ ││ │ │ Confidence threshold │ │ ││ │ │ filter │ │ ││ │ └──────────┬────────────┘ │ ││ │ │ findings[] │ ││ └──────────────────────┼──────────────────────────────────────┘ ││ │ ││ ▼ ││ ┌──────────────────────────────────────────────────────────────┐ ││ │ Policy Engine │ ││ │ │ ││ │ Load org policy rules (priority-sorted) │ ││ │ Evaluate conditions against: findings + user context │ ││ │ Return: matched_rule | no_match │ ││ └──────────┬───────────────────────────────────────────────────┘ ││ │ ││ ▼ ││ ┌──────────────────────────────────────────────────────────────┐ ││ │ Action Dispatcher │ ││ │ │ ││ │ allow → forward to provider │ ││ │ redact → apply redactions, forward sanitized text │ ││ │ block → return 400 to client, drop request │ ││ │ flag → forward to provider + create alert │ ││ └──────────┬───────────────────────────────────────────────────┘ ││ │ ││ ├─────────────────────────────────┐ ││ ▼ ▼ ││ ┌────────────────────┐ ┌─────────────────────┐ ││ │ Audit Event │ │ Provider Request │ ││ │ Writer │ │ (if allow/redact/ │ ││ │ (async, Postgres) │ │ flag) │ ││ └────────────────────┘ └─────────────────────┘ ││ │└─────────────────────────────────────────────────────────────────────┘Request and response inspection
Section titled “Request and response inspection”The DLP pipeline runs twice per conversation turn:
| Inspection point | Text inspected | Blocking capable |
|---|---|---|
| Request phase | User prompt text | Yes — can block before sending to provider |
| Response phase | Provider response text | Yes — can block/redact before returning to client |
Request inspection is synchronous. Response inspection is synchronous for block and redact actions; flag-only rules on responses are evaluated asynchronously to reduce added latency.
Tier Summary
Section titled “Tier Summary”| Tier | Technology | Location | Latency (p50) | Latency (p99) | Circuit breaker |
|---|---|---|---|---|---|
| 1 — Pattern matching | Regex + checksum validators | In-process (CPU) | < 1ms | < 5ms | None (never fails) |
| 2 — NER | GLiNER gliner_medium-v2.1 | GPU microservice :8200 | 30–80ms | 200ms | 3 failures / 60s reset |
| 3 — DeBERTa validation | deberta-v3-base NLI | GPU microservice :8201 | 45–120ms | 300ms | 3 failures / 60s reset |
Pipeline total latency (p50): ~80–120ms (Tier 1 + 2 concurrent, Tier 3 sequential) Pipeline total latency (p99): ~350–500ms under normal load
Tier 1 and Tier 2 execute concurrently. Tier 3 waits for both to complete.
Tier 1 — Regex / Pattern Detection
Section titled “Tier 1 — Regex / Pattern Detection”The RegexDetector runs 65+ compiled regex patterns across four modules:
dlp_patterns_pii— API keys, JWT tokens, private keys, connection stringsdlp_patterns_financial— Credit cards (Luhn-validated), IBAN (mod-97), SWIFT/BICdlp_patterns_medical— DEA numbers, NPI (Luhn-validated), NHS numbersdlp_patterns(core) — SSN, passport, phone, email, IP, driver licenses
All structured identifiers (credit cards, IBANs, NPIs, DEAs) include checksum validation. A regex match that fails its checksum is discarded before being forwarded to Tier 3.
Tier 2 — Named Entity Recognition (NER)
Section titled “Tier 2 — Named Entity Recognition (NER)”The MicroserviceNERDetector sends the full text to the NER GPU service with a list of entity type labels. GLiNER performs zero-shot NER — it can detect any entity type specified as a label without task-specific fine-tuning.
POST http://ner-gpu:8200/detect{ "text": "...", "labels": ["person", "health_info", "date_of_birth", ...], "threshold": 0.5}Tier 2 is designed to catch entities that are syntactically ambiguous or context-dependent — person names, health information, addresses — where regex cannot reliably distinguish real data from similar text.
Tier 3 — DeBERTa Contextual Validation
Section titled “Tier 3 — DeBERTa Contextual Validation”The DeBERTaValidator re-evaluates merged Tier 1 + Tier 2 findings using Natural Language Inference (NLI). For each candidate finding, it constructs an NLI premise-hypothesis pair:
Premise: "...the full text context around the finding..."Hypothesis: "This text contains [entity_type]"DeBERTa returns entailment/neutral/contradiction scores. Findings where contradiction probability exceeds a configurable threshold are demoted or discarded. This dramatically reduces false positives from pattern matching and NER.
DeBERTa also performs content category classification (see Content Categories — Coming Soon) in the same inference pass when enable_content_categories is enabled.
DLP Finding Schema
Section titled “DLP Finding Schema”Each detection result is a DLPMatch object:
interface DLPMatch { entity_type: string; // e.g., "credit_card", "person", "health_info" entity_text: string; // The matched text string start: number; // Character offset start in input text end: number; // Character offset end in input text confidence: number; // Final confidence score [0.0–1.0] detection_tier: 1 | 2 | 3; // Which tier produced the final finding validated: boolean; // Whether Tier 3 validated this finding redaction_replacement: string; // Text to use if action=redact (e.g., "[CREDIT_CARD]")}Finding confidence scoring
Section titled “Finding confidence scoring”Confidence is computed and adjusted across tiers:
| Stage | Confidence value |
|---|---|
| Tier 1 initial | Pattern-specific static value (0.75–0.95) |
| Tier 1 checksum pass | Unchanged |
| Tier 2 initial | NER model score (0.50–0.99) |
| After Tier 1 + Tier 2 merge | Higher confidence retained on duplicate spans |
| After Tier 3 NLI validation | Score adjusted by entailment_probability * original_confidence |
| After threshold filter | Findings below DEBERTA_CONFIDENCE_THRESHOLD (default 0.70) discarded |
Finding deduplication
Section titled “Finding deduplication”When Tier 1 and Tier 2 both detect the same span:
- Exact span overlap: Higher confidence wins
- Partial span overlap: Longer span wins (subsumed finding is discarded)
- Same span, same entity type: Higher confidence wins
- Same span, different entity types: Both retained (multi-type finding)
Policy Engine Integration
Section titled “Policy Engine Integration”The Policy Engine receives the complete findings[] array from the DLP pipeline along with the request context:
interface PolicyEvaluationInput { request_id: string; user_id: string; user_groups: string[]; model_id: string; findings: DLPMatch[]; content_category?: string; // If enable_content_categories content_category_confidence?: float; request_metadata: { org_id: string; outpost_id?: string; source_ip: string; timestamp: string; };}Rule evaluation order
Section titled “Rule evaluation order”Policy rules are evaluated in descending priority order (highest priority number evaluated first). Evaluation stops at the first matching rule (first-match wins):
Priority 1000: "Block all GPT-4 for contractors" ← evaluated firstPriority 900: "Block credit card data for all users"Priority 800: "Redact SSN in responses"Priority 100: "Allow all other traffic" ← fallthrough defaultRule condition matching
Section titled “Rule condition matching”A rule matches when all conditions in its conditions array evaluate to true (logical AND). To express OR logic, create multiple rules at the same priority with a flag: true action, plus a final blocking rule.
Policy rule conditions applicable to DLP findings:
| Condition field | Type | Example |
|---|---|---|
dlp.findings | has_type | entity_type eq "credit_card" |
dlp.findings | count_gte | At least N findings of any type |
dlp.entity_confidence_min | float | Minimum confidence for findings to trigger |
dlp.entity_types | list | Finding must be one of listed types |
user.groups | contains | User must be in specified group |
model.id | in | Request targets specific model |
content.category | equals | (Coming Soon — requires feature flag) |
No-match default behavior
Section titled “No-match default behavior”If no rule matches, the org default action is applied. The org default is configured via Admin → Policy → Default Action:
| Default | Behavior |
|---|---|
allow | Request forwarded to provider (most common) |
block_on_findings | Block any request where findings are non-empty |
audit_only | Allow + write audit event for every request with findings |
Action Dispatch
Section titled “Action Dispatch”The Action Dispatcher executes the policy decision. There are four actions:
No intervention. The request is forwarded to the AI provider (or the response is forwarded to the client) unchanged.
Audit event is created with action: "allow" and the full findings list for traceability.
redact
Section titled “redact”Sensitive spans are replaced with placeholder tokens before the text is forwarded. Redaction is applied to the entity_text at the [start, end] span positions.
Redaction replacement tokens (configurable per entity type):
| Entity type | Default replacement |
|---|---|
credit_card | [CREDIT_CARD] |
ssn | [SSN] |
person | [NAME] |
health_info | [PHI] |
api_key | [REDACTED_SECRET] |
email_address | [EMAIL] |
phone_number | [PHONE] |
| (custom entity) | [REDACTED] |
When multiple overlapping findings are redacted, the longest span is redacted (subsumed spans are not re-applied).
Response body after redaction:
The AI provider receives the redacted text. The redaction is transparent to the provider. If the provider’s response contains the original (non-redacted) text — for example, the model echoes back the prompt — response inspection runs again and redacts the provider response before it reaches the client.
Audit event includes both the original finding spans and the applied redaction positions.
The request is dropped. The AI provider is not called. The gateway returns a structured error response to the client:
HTTP/1.1 400 Bad RequestContent-Type: application/json
{ "error": { "type": "content_policy_violation", "code": "dlp_block", "message": "Your request was blocked by a content policy rule.", "rule_name": "block-credit-card-data", "request_id": "req_abc123", "findings_summary": [ { "entity_type": "credit_card", "count": 1 } ] }}The findings_summary includes entity type and count but NOT the matched text, to avoid leaking sensitive data back to the client in the error response.
For response-phase blocking (the provider responded with sensitive content), the gateway returns:
HTTP/1.1 502 Bad Gateway{ "error": { "type": "response_policy_violation", "code": "dlp_response_block", "message": "The AI provider response was blocked by a content policy rule.", "request_id": "req_abc123" }}The request is allowed to proceed (forwarded to provider / returned to client) but an alert event is created and sent to configured alert channels (Slack, PagerDuty, webhook).
flag is used for monitoring and review workflows where blocking would be too disruptive. It can be combined with allow implicitly (flag rules do not stop processing — the next matching rule’s action still applies):
Rule A (priority 500): flag → sends alert, continues evaluationRule B (priority 100): allow → final action applied→ Result: request forwarded + alert createdIf a flag rule matches and no subsequent rule returns allow, the org default action applies.
Alert payload for flag action
Section titled “Alert payload for flag action”{ "alert_type": "dlp_flag", "request_id": "req_abc123", "rule_name": "flag-financial-data-for-review", "severity": "medium", "user_id": "user_xyz", "user_email": "alice@example.com", "model_id": "gpt-4o", "findings": [ { "entity_type": "credit_card", "confidence": 0.96, "redaction_replacement": "[CREDIT_CARD]" } ], "timestamp": "2026-03-12T17:00:00Z", "org_id": "org_abc"}Note: entity_text (the actual matched content) is NOT included in alert payloads. Alert channels receive metadata only.
Audit Event Creation
Section titled “Audit Event Creation”Every request that passes through the DLP pipeline creates an audit event, regardless of action taken. Audit events are written asynchronously to the audit_logs PostgreSQL table after the action is dispatched.
Audit event schema
Section titled “Audit event schema”interface AuditEvent { id: string; // UUID, primary key request_id: string; // Correlates with gateway request ID org_id: string; user_id: string; model_id: string; inspection_phase: "request" | "response"; findings: DLPFindingSummary[]; // Sanitized — no entity_text policy_rule_id?: string; // NULL if no rule matched policy_rule_name?: string; action: "allow" | "redact" | "block" | "flag"; action_meta: { redaction_count?: number; // For redact action alert_id?: string; // For flag action block_reason?: string; // For block action }; dlp_latency_ms: number; // Total DLP pipeline duration tier1_latency_ms: number; tier2_latency_ms: number; tier3_latency_ms: number; timestamp: string; // ISO 8601 UTC content_hash: string; // HMAC-SHA256 of original text for tamper detection}
interface DLPFindingSummary { entity_type: string; confidence: number; detection_tier: 1 | 2 | 3; span_start: number; span_end: number; // entity_text is NOT stored in audit_log for privacy compliance}Audit event write path
Section titled “Audit event write path”Audit events are written asynchronously:
- DLP action is dispatched (request forwarded, blocked, or redacted)
- A
WriteAuditEventtask is queued to the in-process task queue - The task writes to
audit_logstable via the async DB pool - On write failure, the event is retried up to 3 times with exponential backoff
- If all retries fail, the event is written to the
audit_log_failuresdead-letter table for operator recovery
Write latency (p50): < 5ms Write latency (p99): < 50ms Async — does not add to request latency
Audit log retention
Section titled “Audit log retention”Audit events are partitioned by month in PostgreSQL (range partitioning on timestamp). Retention policy is configured per org:
| Tier | Storage | Default retention |
|---|---|---|
| Hot | PostgreSQL (partitioned) | 90 days |
| Warm | PostgreSQL (compressed) | 1 year |
| Cold | Azure Blob (WORM) | 7 years |
See Data Retention & Archival for partition management and cold-tier archival configuration.
Audit event integrity
Section titled “Audit event integrity”Each audit event includes a content_hash — an HMAC-SHA256 computed over (request_id + org_id + timestamp + findings_json) using a per-org HMAC key. This allows verification that audit records have not been tampered with.
See Audit Chain Integrity for verification procedures.
Failure Modes and Degradation
Section titled “Failure Modes and Degradation”The DLP pipeline is designed to fail closed for blocking policies and fail open for detection-only policies, based on the dlp_circuit_breaker_policy org setting.
GPU service failure handling
Section titled “GPU service failure handling”When the NER (Tier 2) or DeBERTa (Tier 3) GPU service is unavailable:
| Circuit state | Behavior |
|---|---|
| Closed (normal) | Full 3-tier inspection |
| Open (Tier 2 failed) | Tier 1 only; Tier 2 findings are absent; Tier 3 validates only Tier 1 findings |
| Open (Tier 3 failed) | Tier 1 + Tier 2 findings without NLI validation; confidence threshold raised by 0.10 to compensate |
| Open (both GPU services failed) | Tier 1 only; all requests subject to pattern-match findings only |
When GPU services are in an open circuit state, the platform emits dlp_tier_degraded audit events and alerts the configured operations channel.
Fail-closed vs fail-open policy
Section titled “Fail-closed vs fail-open policy”The dlp_circuit_breaker_policy org setting controls behavior when all GPU tiers fail:
| Setting | Value | Behavior |
|---|---|---|
fail_closed | Block all requests when Tier 1 has no findings but GPU unavailable | More secure; may disrupt operations |
fail_open | Allow requests with only Tier 1 inspection (default) | Less disruptive; reduces NER/DeBERTa-only detection coverage |
Regardless of setting, Tier 1 findings (regex/checksum) always apply. fail_closed only affects requests where GPU inspection would have been the primary detection method.
Performance Tuning
Section titled “Performance Tuning”Parallel execution
Section titled “Parallel execution”Tier 1 and Tier 2 execute concurrently using asyncio.gather(). The combined latency is max(T1, T2) rather than T1 + T2. Since Tier 1 typically completes in < 1ms and Tier 2 in 30–80ms, Tier 2 dominates the parallel phase.
Text length limits
Section titled “Text length limits”| Configuration key | Default | Description |
|---|---|---|
DLP_MAX_INSPECT_CHARS | 50,000 | Maximum characters inspected per text chunk |
DLP_CHUNK_OVERLAP | 200 | Character overlap between chunks for entity boundary detection |
Texts longer than DLP_MAX_INSPECT_CHARS are chunked. Each chunk is inspected independently; findings are merged after all chunks complete. Chunking adds latency proportional to the number of chunks.
GPU batch size
Section titled “GPU batch size”| Configuration key | Default | Description |
|---|---|---|
NER_BATCH_SIZE | 1 | Texts per NER inference request (increase for throughput) |
DEBERTA_BATCH_SIZE | 8 | Findings per DeBERTa validation request |
For high-throughput deployments, increasing DEBERTA_BATCH_SIZE reduces GPU inference overhead when many findings require validation.
Observability
Section titled “Observability”Prometheus metrics
Section titled “Prometheus metrics”| Metric | Type | Labels | Description |
|---|---|---|---|
dlp_inspection_duration_seconds | Histogram | tier, phase | Per-tier latency |
dlp_findings_total | Counter | entity_type, tier, action | Findings by type and action |
dlp_circuit_breaker_state | Gauge | tier | 0=closed, 1=half-open, 2=open |
dlp_gpu_request_duration_seconds | Histogram | service | GPU microservice call latency |
dlp_gpu_errors_total | Counter | service, error_type | GPU service error counts |
policy_evaluation_duration_seconds | Histogram | — | Policy Engine evaluation latency |
policy_action_total | Counter | action, rule_name | Actions taken by rule |
audit_write_duration_seconds | Histogram | — | Async audit write latency |
audit_write_failures_total | Counter | — | Audit write failures (dead-lettered) |
Trace correlation
Section titled “Trace correlation”Every DLP inspection is traced via OpenTelemetry. The inspection span is a child of the gateway request span:
gateway.request [request_id] └── dlp.inspect [phase=request] ├── dlp.tier1 [latency_ms=0.8] ├── dlp.tier2 [latency_ms=68] ├── dlp.merge [findings_count=3] ├── dlp.tier3 [latency_ms=92] └── dlp.policy_eval [action=allow, rule=null]See Distributed Tracing for trace query examples.
Related Documentation
Section titled “Related Documentation”| Document | Content |
|---|---|
| DLP Pipeline — Technical Reference | Per-tier implementation details, entity taxonomy, confidence tuning |
| DLP Overview | Introduction and key concepts |
| Policy Engine Overview | Rule language and evaluation semantics |
| Policy Rule Reference | Complete condition and action reference |
| DLP Pipeline Configuration | Admin guide for configuring detection thresholds |
| DLP Event Monitoring | Querying and alerting on DLP audit events |
| Alert Configuration | Prometheus alert rules for DLP latency and errors |
| Kubernetes Deployment | GPU node pool setup for NER and DeBERTa services |