Skip to content

Policy configuration guide

Arbitex’s Policy Engine evaluates rules against every request and response before they are forwarded to or returned from AI providers. Policies are organized into policy packs (collections of rules), chains (ordered sequences of packs), and combining algorithms (how multiple rule matches are resolved). This guide explains how these components work together and provides practical configuration patterns for common governance scenarios.

For the full rule condition reference, see Policy rule reference. For the admin UI walkthrough, see Policy Engine admin guide.


The Policy Engine follows a Palo Alto firewall model with two evaluation passes per request:

  1. Input pass — evaluated before the prompt is forwarded to the AI provider
  2. Output pass — evaluated before the response is returned to the user

For each pass, the engine evaluates chains in this order:

  1. User chain (scope="user") — personal overrides assigned to the individual user
  2. Org chain (scope="org") — the organization’s primary policy chain

Within each chain, packs are evaluated in sequence order. Within each pack, rules are evaluated in sequence order. Evaluation stops when a terminal action is matched (in first_applicable mode) or when all packs have been processed (in deny_overrides mode).

ActionTerminal?Effect
ALLOWYesPermit the request. Stop evaluation.
BLOCKYesReject the request with a block message. Stop evaluation.
CANCELYesCancel the request silently. Stop evaluation.
ROUTE_TOYesRoute to a specified model or tier. Stop evaluation.
PROMPTYesPresent a governance challenge to the user (interactive only). Stop evaluation.
ALLOW_WITH_OVERRIDEYesPermit with an override acknowledgement. Stop evaluation.
REDACTNoApply redaction and continue evaluation to the next rule.

REDACT is the only non-terminal action. Multiple REDACT rules can match and accumulate across a single evaluation pass — each adds its replacement to the list of accumulated redactions. Terminal actions carry the accumulated redactions with them.

Default behavior: If no rule matches anywhere, the request is allowed (default ALLOW).


A policy pack is a named, ordered collection of rules with a shared scope. Rules within a pack are evaluated in ascending sequence order. A pack can be included in multiple chains.

Each rule in a pack has:

  • Conditions (all of which must be true for the rule to match — AND logic)
  • Action (what to do when the rule matches)
  • Applies to (input, output, or both)

Example: a DLP pack with two rules

Pack: "PII Detection"
Rule 1 (sequence 1): entity_types=[CREDIT_CARD, SSN], entity_confidence_min=0.85 → BLOCK
Rule 2 (sequence 2): entity_types=[EMAIL_ADDRESS], entity_confidence_min=0.75 → REDACT "[EMAIL]"

In this pack, a request containing a credit card number with ≥85% confidence is blocked. A request containing an email address with ≥75% confidence (but no credit card/SSN) has the email redacted and evaluation continues.


Each chain specifies a combining algorithm that controls what happens when multiple rules match across packs in the chain.

The first terminal action matched across any pack in the chain wins. Evaluation stops immediately on the first terminal match.

Chain (first_applicable):
Pack 1: Rule A (ALLOW_WITH_OVERRIDE, user in "finance-team")
Pack 2: Rule B (BLOCK, entity SSN detected)

If Rule A matches (user is in finance-team), the decision is ALLOW_WITH_OVERRIDE and Pack 2 is never evaluated. If Rule A does not match, Pack 2 is evaluated. If Rule B matches, the decision is BLOCK.

This mirrors how firewall ACLs work: pack order matters. Place more specific allowances before general blocks.

All packs are evaluated even after a terminal match, unless the match is BLOCK or CANCEL. If any pack produces a BLOCK or CANCEL, that decision wins regardless of what other packs decided. If no deny is found, the most severe non-deny decision wins.

Severity ordering (highest to lowest): BLOCK > CANCEL > REDACT > ROUTE_TO > PROMPT > ALLOW

Chain (deny_overrides):
Pack 1: Rule A (ALLOW, no conditions — catch-all)
Pack 2: Rule B (BLOCK, content_regex matches "confidential")

Even though Rule A fires first (and would win under first_applicable), the deny_overrides algorithm continues evaluating Pack 2. If Rule B also matches, BLOCK wins.

Use deny_overrides when you have a compliance pack that must never be bypassed by an earlier allowance — for example, a regulatory BLOCK rule that must fire even if a user-level override pack would otherwise allow the request.


ROUTE_TO redirects the request to a different model or model tier when a rule matches. Use it to implement cost-based routing (downgrade complex requests to a cheaper model), capability-based routing (send code generation to a code-specialized model), or risk-based routing (send high-risk-score requests to a restricted model).

{
"type": "ROUTE_TO",
"route_to_model": "claude-haiku-4-5-20251001"
}

The route_to_model value is the exact model identifier as configured in your provider settings.

{
"type": "ROUTE_TO",
"route_to_tier": "haiku"
}

Valid tier values: haiku, sonnet, opus. The engine resolves the tier to the configured model for that tier at evaluation time.

Route simple requests to a lower-cost model:

Pack: "Cost Optimization"
Rule 1: intent_complexity=simple → ROUTE_TO tier=haiku
Rule 2: intent_complexity=complex → ROUTE_TO tier=opus
(no rule for medium → falls through to default model)

Route requests for specific providers to a restricted model when the user risk score is elevated:

Pack: "Risk-Based Routing"
Rule: user_risk_score_min=0.7, providers=[openai] → ROUTE_TO model=gpt-4o-mini

The org chain (scope="org", scope_id=tenant_id) is the primary policy chain for your organization. All users in the org are subject to it. Rules in the org chain can use user_groups conditions to target specific groups without requiring separate per-user chains.

Org chain (first_applicable):
Pack: "Compliance Baseline"
Rule 1: entity_types=[SSN, CREDIT_CARD] → BLOCK (applies to all users)
Rule 2: user_groups=["finance"], entity_types=[BANK_ACCOUNT] → ALLOW (finance team exempt)
Rule 3: entity_types=[BANK_ACCOUNT] → BLOCK (all other users)

The user_groups condition uses OR logic: the user must be a member of any of the listed groups for the condition to match.

The user chain (scope="user", scope_id=user_id) contains overrides for a specific user. It is evaluated first — before the org chain. A terminal action in the user chain prevents the org chain from being evaluated at all.

Use user chains sparingly. The recommended pattern is to express group-level overrides via user_groups conditions in the org chain rather than creating individual user chains.


Pattern 1: DLP baseline with group override

Section titled “Pattern 1: DLP baseline with group override”

Block PII for all users, but allow the security-audit group to see masked output:

Org chain (first_applicable):
Pack: "Security Audit Override" (sequence 1)
Rule: user_groups=["security-audit"] → ALLOW_WITH_OVERRIDE
Pack: "DLP Baseline" (sequence 2)
Rule: entity_types=[SSN, CREDIT_CARD, PASSPORT] → BLOCK

When a security-audit member sends a request, Pack 1 matches and returns ALLOW_WITH_OVERRIDE — the DLP Baseline is never reached. All other users fall through to the DLP Baseline.

Pattern 2: Cost-tiered routing with compliance block

Section titled “Pattern 2: Cost-tiered routing with compliance block”

Implement cost optimization routing but ensure compliance blocks always fire:

Org chain (deny_overrides):
Pack: "Cost Routing" (sequence 1)
Rule: intent_complexity=simple → ROUTE_TO tier=haiku
Rule: intent_complexity=complex → ROUTE_TO tier=opus
Pack: "Compliance Block" (sequence 2)
Rule: content_regex="export controlled|ITAR|EAR" → BLOCK

With deny_overrides, the compliance block in Pack 2 fires even if Pack 1 has already produced a routing decision.

Pattern 3: Channel-gated PROMPT governance

Section titled “Pattern 3: Channel-gated PROMPT governance”

Present a governance challenge only for interactive users, not API callers:

Org chain (first_applicable):
Pack: "Interactive Governance"
Rule: channel=["interactive"], content_regex="generate.*code" → PROMPT
prompt_message="Code generation requires confirmation. Proceed?"

API callers (channel="api") do not match the channel condition and pass through without a challenge.

Route high-risk users to a restricted model regardless of their request:

Org chain (first_applicable):
Pack: "Risk Escalation"
Rule: user_risk_score_min=0.8 → ROUTE_TO model=gpt-4o-mini
Pack: "Default Policy"
Rule: (no conditions — catch-all) → ALLOW

ConditionTypeMatch logic
applies_toinput, output, bothMust match request direction
user_groupslist of group names/IDsUser must be in ANY listed group
entity_typeslist of entity type stringsANY detected entity must be in the list with ≥ entity_confidence_min
entity_confidence_minfloat (0.0–1.0)Minimum confidence for entity match
content_regexregex stringPattern must match anywhere in prompt_text
providerslist of provider namesprovider must be in the list
modelslist of model identifiersmodel must be in the list
user_risk_score_minfloat (0.0–1.0)user_risk_score must be ≥ this value
intent_complexitysimple, medium, complexintent_complexity must equal this value
channellist: interactive, apichannel must be in the list

All specified conditions must be satisfied (AND logic). A rule with no conditions is a catch-all that matches every request.


Pack order in first_applicable determines which rules fire. If a broad allowance rule (e.g., catch-all ALLOW) is in Pack 1 and a block rule is in Pack 2, the block will never be reached. Arrange packs from most-specific to least-specific.

REDACT is non-terminal — it does not stop evaluation. If you want redaction without also allowing the request through, follow the REDACT rule with a subsequent rule that produces a terminal action.

User chains take precedence over org chains. A user with a personal ALLOW chain entry can bypass org-level blocks under first_applicable mode. Use deny_overrides on the org chain if compliance blocks must be absolute.

intent_complexity is populated by the intake pipeline after DLP scanning. Rules that reference intent_complexity will never match on the first pass if the pipeline has not yet computed the value. Check that the intake pipeline version supports Epic J5 complexity scoring.