Skip to content

DLP Pipeline Architecture

import { Aside } from ‘@astrojs/starlight/components’;

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.


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:

  1. Detection — Identify sensitive content using a 3-tier detection stack
  2. Policy evaluation — Apply org-specific policy rules to the findings
  3. Action dispatch — Execute the policy decision and produce an audit record

┌─────────────────────────────────────────────────────────────────────┐
│ 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) │ │
│ └────────────────────┘ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘

The DLP pipeline runs twice per conversation turn:

Inspection pointText inspectedBlocking capable
Request phaseUser prompt textYes — can block before sending to provider
Response phaseProvider response textYes — 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.


TierTechnologyLocationLatency (p50)Latency (p99)Circuit breaker
1 — Pattern matchingRegex + checksum validatorsIn-process (CPU)< 1ms< 5msNone (never fails)
2 — NERGLiNER gliner_medium-v2.1GPU microservice :820030–80ms200ms3 failures / 60s reset
3 — DeBERTa validationdeberta-v3-base NLIGPU microservice :820145–120ms300ms3 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.

The RegexDetector runs 65+ compiled regex patterns across four modules:

  • dlp_patterns_pii — API keys, JWT tokens, private keys, connection strings
  • dlp_patterns_financial — Credit cards (Luhn-validated), IBAN (mod-97), SWIFT/BIC
  • dlp_patterns_medical — DEA numbers, NPI (Luhn-validated), NHS numbers
  • dlp_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.

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.

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.


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]")
}

Confidence is computed and adjusted across tiers:

StageConfidence value
Tier 1 initialPattern-specific static value (0.75–0.95)
Tier 1 checksum passUnchanged
Tier 2 initialNER model score (0.50–0.99)
After Tier 1 + Tier 2 mergeHigher confidence retained on duplicate spans
After Tier 3 NLI validationScore adjusted by entailment_probability * original_confidence
After threshold filterFindings below DEBERTA_CONFIDENCE_THRESHOLD (default 0.70) discarded

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)

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;
};
}

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 first
Priority 900: "Block credit card data for all users"
Priority 800: "Redact SSN in responses"
Priority 100: "Allow all other traffic" ← fallthrough default

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 fieldTypeExample
dlp.findingshas_typeentity_type eq "credit_card"
dlp.findingscount_gteAt least N findings of any type
dlp.entity_confidence_minfloatMinimum confidence for findings to trigger
dlp.entity_typeslistFinding must be one of listed types
user.groupscontainsUser must be in specified group
model.idinRequest targets specific model
content.categoryequals(Coming Soon — requires feature flag)

If no rule matches, the org default action is applied. The org default is configured via Admin → Policy → Default Action:

DefaultBehavior
allowRequest forwarded to provider (most common)
block_on_findingsBlock any request where findings are non-empty
audit_onlyAllow + write audit event for every request with findings

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.

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 typeDefault 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 Request
Content-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 evaluation
Rule B (priority 100): allow → final action applied
→ Result: request forwarded + alert created

If a flag rule matches and no subsequent rule returns allow, the org default action applies.

{
"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.


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.

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 events are written asynchronously:

  1. DLP action is dispatched (request forwarded, blocked, or redacted)
  2. A WriteAuditEvent task is queued to the in-process task queue
  3. The task writes to audit_logs table via the async DB pool
  4. On write failure, the event is retried up to 3 times with exponential backoff
  5. If all retries fail, the event is written to the audit_log_failures dead-letter table for operator recovery

Write latency (p50): < 5ms Write latency (p99): < 50ms Async — does not add to request latency

Audit events are partitioned by month in PostgreSQL (range partitioning on timestamp). Retention policy is configured per org:

TierStorageDefault retention
HotPostgreSQL (partitioned)90 days
WarmPostgreSQL (compressed)1 year
ColdAzure Blob (WORM)7 years

See Data Retention & Archival for partition management and cold-tier archival configuration.

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.


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.

When the NER (Tier 2) or DeBERTa (Tier 3) GPU service is unavailable:

Circuit stateBehavior
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.

The dlp_circuit_breaker_policy org setting controls behavior when all GPU tiers fail:

SettingValueBehavior
fail_closedBlock all requests when Tier 1 has no findings but GPU unavailableMore secure; may disrupt operations
fail_openAllow 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.


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.

Configuration keyDefaultDescription
DLP_MAX_INSPECT_CHARS50,000Maximum characters inspected per text chunk
DLP_CHUNK_OVERLAP200Character 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.

Configuration keyDefaultDescription
NER_BATCH_SIZE1Texts per NER inference request (increase for throughput)
DEBERTA_BATCH_SIZE8Findings per DeBERTa validation request

For high-throughput deployments, increasing DEBERTA_BATCH_SIZE reduces GPU inference overhead when many findings require validation.


MetricTypeLabelsDescription
dlp_inspection_duration_secondsHistogramtier, phasePer-tier latency
dlp_findings_totalCounterentity_type, tier, actionFindings by type and action
dlp_circuit_breaker_stateGaugetier0=closed, 1=half-open, 2=open
dlp_gpu_request_duration_secondsHistogramserviceGPU microservice call latency
dlp_gpu_errors_totalCounterservice, error_typeGPU service error counts
policy_evaluation_duration_secondsHistogramPolicy Engine evaluation latency
policy_action_totalCounteraction, rule_nameActions taken by rule
audit_write_duration_secondsHistogramAsync audit write latency
audit_write_failures_totalCounterAudit write failures (dead-lettered)

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.


DocumentContent
DLP Pipeline — Technical ReferencePer-tier implementation details, entity taxonomy, confidence tuning
DLP OverviewIntroduction and key concepts
Policy Engine OverviewRule language and evaluation semantics
Policy Rule ReferenceComplete condition and action reference
DLP Pipeline ConfigurationAdmin guide for configuring detection thresholds
DLP Event MonitoringQuerying and alerting on DLP audit events
Alert ConfigurationPrometheus alert rules for DLP latency and errors
Kubernetes DeploymentGPU node pool setup for NER and DeBERTa services