Skip to content

Relay Compliance

Relay Compliance scans every AI Warden request/response payload in-line against compliance templates (HIPAA, SOC2, GDPR, PCI, and M-26-14) for PHI, PII, PAN/PCI, credentials and secrets. It produces one of three deterministic verdicts:

  • ALLOW — clean / sub-threshold.
  • ALLOW + NOTIFY — findings present; the request passes through but a violation is recorded and notifications fire (the default for hygiene-class findings, so traffic is never broken).
  • BLOCK — a fired rule’s action is block (critical leaks).

It reuses the same production-tuned engine that scans Logrite source code — LogLeakDetector / LEAK_PATTERNS — no new pattern library, no new AI vendor. It ships as one product surface with AI Warden (Sprint 1) and also bootstraps M-26-14 for relay use; the full M-26-14 framework is owned by M-26-14 Maturity.

SettingValue
EngineLogLeakDetector.checkSingle / shared leakPatterns.ts (analyzer dev + vendored byte-identical into the relay)
Scan locationIn-relay, in-process (logrite-ai-service/src/warden/complianceCheck.ts) — no synchronous hop on the hot path
Events indexrelay-compliance-events-{tenant} (OpenSearch, redacted)
Bindingrelay_compliance_binding — since 2026-10-15 read only to resolve a request’s workspace and project
UIFrameworks are framework items on a business-rules policy, edited in the policy editor or turned on from Rules & Policies → Frameworks (/policies/frameworks); see Rules & Policies. Violations surface in AI Warden → Overview.

Verdict composition (most-restrictive-wins)

Section titled “Verdict composition (most-restrictive-wins)”

resolve(business, compliance) → (1) if either is block → BLOCK; (2) else if either is allow_notify → ALLOW_NOTIFY; (3) else ALLOW. Each event records businessDecision, complianceDecision, and finalDecision.

TemplateCanonical rules
SOC2soc2_log_leak_credentials, soc2_log_leak_confidentiality
HIPAAhipaa_log_leak_phi, hipaa_log_leak_phi_object
GDPRgen_log_leak_pii
PCI (implicit)gen_log_leak_payment
M-26-14m2614_pii_in_ai_prompt, m2614_sensitive_output (+ reused gen_log_leak_pii, soc2_log_leak_credentials)
GENgen_log_leak_request_body, gen_log_leak_query_params — only when GEN is explicitly selected (no always-on floor)

UX Lifecycle — Configure → Connect → Test → Operate → Review

Section titled “UX Lifecycle — Configure → Connect → Test → Operate → Review”
  1. Configure — frameworks are framework items on a business-rules policy with the AI traffic feature (any of the 30 library frameworks), each with an optional action override (Block / Flag / Record) and the policy’s scan-scope setting. Only selected frameworks gate — there is no always-on floor. In Monitor only status (evaluate) the backend caps a framework owned only by Monitor-only policies to notify via evaluateFrameworks.
  2. Connect — the relay resolves the effective ai_traffic policies per relay key and request project, and caches the bundle (LRU + short TTL). The legacy per-project binding and compliance-profile fallback were removed on 2026-10-15.
  3. Test — use the in-tab relay test (Connect & Keys Step 3, or Business Rules → Test your configuration) to send a message through the relay and verify the right rules fire. See the AI Warden in-browser relay test for the built-in sample payloads and expected verdicts.
  4. Operate — LLM-first: the judge seeded by the selected frameworks is the primary decider; for each message the relay also normalizes in-scope strings → checkSingle (sub-ms regex pre-filter / fallback) → composes verdict most-restrictive → redacts → fans out the ComplianceEvent asynchronously (off the hot path). See AI Warden — LLM-first judging.
  5. Review — the Violations view (relay-compliance-events-*): ALLOWED-BUT-FLAGGED filter, BLOCKED filter, mark-false-positive. The requests table shows a per-framework compliance chip deep-linking to the violation. Outside the app, the same events are read through GET /api/v1/warden/compliance-events and /compliance-events/stats (2026-10-14; session or API key — see AI Warden), which MCP’s warden_relay list_events / event_stats and compliance_logs use.

/api/v1/relay/compliance/* (GET/PUT /binding, GET /effective-ruleset), RelayComplianceController, RelayComplianceService and warden_compliance_profile were removed on 2026-10-15. Configure frameworks through /api/v1/policies (including POST /api/v1/policies/frameworks/{libraryKey}/enable) — see Rules & Policies.

The ComplianceEvent is redacted inside the relay before any sink: leakedFields = token names only, redactedSnippet ≤ 200 chars with values masked, raw values never written. Default posture is fail-open (a detector error records + allows); fail_mode: closed is per-binding opt-in. Notification fan-out is async/fire-and-forget — never awaited on the hot path.

Event timestamps (2026-10-14). Every ComplianceEvent carries the same instant in timestamp and @timestamp; before, only timestamp existed, and a window on @timestamp matched nothing. Existing events are backfilled per estate, and new indices get the backend’s relay-compliance-events index template — details and the dev/prod mapping divergence on AI Warden.

Three kinds joined the product redaction list. They are identical, in the same order, in the backend’s RedactionPatterns and the AI service’s DEFAULT_REDACTION (urlCredential, credentialPair, email, ssn, mrn, card, iban, phone, dob, secretKey, opaqueToken, longDigits), and are used by the relay, ingest-time redaction and the demo seeder.

KindMatchesExample → stored
mrnMRN or “medical record (number/no/id)”, then up to three separators, then an id of 4+ letters, digits or hyphens (case-insensitive)MRN TEST-004417 → MRN ***
phoneNorth-American numbers: optional +1, a 3-digit area code (with or without parentheses), then 3 and 4 digits; also the short +1 NNN-NNNN formcall +1 555-0142 → call ***
doba date after a label — DOB, D.O.B., date of birth, birthdate, birth date, born, born on (case-insensitive)DOB 1984-03-12 → DOB ***

An unlabelled date (2026-10-08 12:31) is not masked. mrn also counts as a strong value for compliance findings. For Cortex answers the same kinds are rendered as labels ([SSN], [MRN], [DOB], [EMAIL], [PHONE], [CARD], [IBAN]) instead of ***.

Judge reasons mask any quoted span of 2–80 characters (up to six words) unless it is the name of a judged policy, a framework key or a snake_case rule id — so a reason quoting 'Jane Testperson' or 'MRN TEST-004417' stores neither value, while under 'No clinical advice' keeps the policy name.

Whether the relay masks at all, and which kinds, is a workspace choice since 2026-10-09 — see AI Warden → Redaction switches.

  • hipaa_log_leak_identifiers no longer flags every bare https?:// URL (an educational answer citing a guideline page raised Notify). A URL counts only when labelled as a patient or member URL or portal: \b(patient|member)[_ ]?(url|portal)\b\s*[=:]\s*https?://.
  • Response-scope word-list hits are weak. On a model response, a value-pattern hit is strong only when the matched text contains a digit, an @, or a run of 16+ opaque characters (or a redaction-kind hit). So “Biometric identifiers such as fingerprints” in an answer is allowed, while ssn: 123-45-6789 still notifies.
  • The starter HIPAA template’s No clinical advice policy now reads: “Block any request seeking a medical diagnosis, a treatment plan, or medication or dosage advice for an identifiable patient or for a person whose symptoms are described for that purpose. Questions about usual or guideline care for a condition or a generic, de-identified case (e.g. ‘usual follow-up for type 2 diabetes’) are general health education and are allowed.” Judge eval: a de-identified diabetes follow-up question is allowed; dosing for “my patient” and diagnosis from symptoms are blocked (55/55 cases on deepseek-v4.1-flash). Policies already saved keep their own text.

When the events index is empty, opensearch/scripts/seed-demo-program.sh seeds sample violations into relay-compliance-events-{tenant} (allow_notify + block, cross-linked to the warden requests) so the Violations view renders; --clean removes them.

The relay is the inline LLM-proxy hot path (/v1/:provider/*) and the surface that high-volume / agentic-coding traffic hits. Measured ceiling (single process, mock upstream, full pipeline, judge off): ~350 req/s at p95 ~500 ms / 150 concurrent (harness: perf-tests/run-warden-bench.sh, results in perf-tests/WARDEN-FINDINGS.md).

ConcernLeverDefault
Overload backpressureWARDEN_MAX_INFLIGHT — over the per-process cap → 429 over_capacity + Retry-After (no timeouts/5xx)500
Global rate/token limits + shared cachesWARDEN_REDIS_URL / REDIS_URL — makes limits + binding/judge caches GLOBAL across cluster workers/replicas; fail-soft (unset/down ⇒ in-process, never errors; ~2% hot-path overhead)unset (in-process)
Per-key request cap (DoS)WARDEN_RATE_LIMIT_PER_MIN1200
Per-tenant token budget (agentic TPM)WARDEN_TPM_BUDGET_PER_TENANT — 0 disables0
Multi-coreWARDEN_INSTANCES=max — node cluster inside the container; safe ONLY with Redis set (else per-fork caps multiply). The PM2 cluster this used to describe no longer exists1 (fork)
async_observe judge throughputWARDEN_JUDGE_WORKERS — concurrent queue drainers; /health.judgeQueueEvicted surfaces drops4
Groq judge resilienceinvokeGroq retries 429/5xx/network with jittered backoff + Retry-After—
Audit-write load-shedWARDEN_CAPTURE_ENABLED=false drops per-request capture writes under extreme loadtrue

/health exposes saturation signals: inFlight, maxInflight, judgeQueueDepth, judgeQueueEvicted, redis.

Recommended production sequence: provision Redis → set WARDEN_REDIS_URL → confirm /health shows redis: connected → set WARDEN_INSTANCES=max → (optionally) run N relay replicas behind Traefik. WARDEN_TEST_STATIC_BINDING is TEST-ONLY (isolated load tests) and MUST be empty in production.

  • mrn needs a digit. An mrn match is masked only when the captured value contains at least one digit, so phrases like “MRN field” or “medical record number” with a plain word after them are left alone (backend 2e4517eb, ai-service 05bc0561).
  • Response-scope value matches. On scope: response, a value-pattern hit is strong only when the matched text contains a digit, an @, or an opaque token of 16 or more characters; anything else is a weak word-list hit and does not raise a finding on its own.