Skip to content

Enforcement Chain Guide

This guide explains how the Policy Engine’s enforcement chain operates. It covers how packs are ordered in a chain, how combining algorithms determine the final action when multiple rules match, how to filter the chain view by group membership, and how to simulate requests before deploying changes.

For a conceptual overview of policy packs, rules, actions, and user-visible outcomes, see Policy Engine — User Guide. For step-by-step admin configuration (creating packs, rules, and chains), see Policy Engine admin guide.


An org’s policy chain is the ordered sequence of policy packs evaluated for every AI request. When a request comes in, the Policy Engine evaluates each pack in chain order, applying the combining algorithm to determine the final enforcement action.

Your org has exactly one chain. The chain is configured in Settings → Policies → Policy Chain.

The chain holds references to packs — not copies of rules. If you modify a pack’s rules, those changes apply immediately to the chain the next time the chain is evaluated.


For every AI request, the Policy Engine:

  1. Identifies the applicable chain (your org’s chain)
  2. Iterates packs in sequence order (position 1 first, then 2, then 3, …)
  3. Within each pack, evaluates rules in ascending sequence number order
  4. Applies the combining algorithm to determine when to stop and what action to return

The result is a single enforcement action: ALLOW, BLOCK, CANCEL, REDACT, ROUTE_TO, PROMPT, or ALLOW_WITH_OVERRIDE.

If no rule matches in any pack, the default action is ALLOW.


Packs appear in the chain in the order shown in the Policy Chain editor. Position 1 is evaluated first. Order matters significantly with the first_applicable combining algorithm (see below).

In the Policy Chain editor:

  • Drag a pack card up or down to change its position
  • Use the up (↑) and down (↓) buttons on each card to move it one position at a time
  • Click Save chain to persist the new order

Changes take effect immediately after saving. There is no separate publish step.

  1. Navigate to Settings → Policies → Policy Chain.
  2. Click Add Pack.
  3. Select from the list of packs not yet in the chain. Packs already in the chain do not appear in the picker.
  4. The selected pack is appended to the end of the chain.
  5. Reorder it as needed, then click Save chain.

Click the remove button (×) on a pack card. The pack is removed from the chain but not deleted — its rules remain intact and you can re-add it later.


The combining algorithm controls how the chain resolves situations where multiple rules across multiple packs could apply to a single request.

Set the combining algorithm using the Combining algorithm dropdown in the Policy Chain editor, then click Save chain.

The engine evaluates rules in pack/sequence order and stops at the first terminal rule that matches. All subsequent rules and packs are skipped.

Terminal actions: ALLOW, BLOCK, CANCEL, ROUTE_TO, PROMPT, ALLOW_WITH_OVERRIDE.

Non-terminal: REDACT. A matching REDACT rule applies its redaction and evaluation continues to the next rule.

Example chain:

Pack 1 — "Engineering exceptions"
Rule 1 (seq 1): user_groups=["engineering"] → ALLOW
Pack 2 — "PCI-DSS Bundle"
Rule 1 (seq 1): entity_types=["CREDIT_CARD"] → REDACT
Rule 2 (seq 2): entity_types=["SSN"] → BLOCK
Pack 3 — "Default deny"
Rule 1 (seq 1): (unconditional) → BLOCK

Request from engineering user with no PII detected:

  1. Pack 1, Rule 1 matches (user is in engineering) → ALLOW → evaluation stops
  2. Pack 2 and Pack 3 are never reached

Request from non-engineering user with credit card number detected:

  1. Pack 1, Rule 1 does not match (user not in engineering) → continue
  2. Pack 2, Rule 1 matches (CREDIT_CARD detected) → REDACT applied, evaluation continues (REDACT is non-terminal)
  3. Pack 2, Rule 2 does not match (no SSN) → continue
  4. Pack 3, Rule 1 matches (unconditional) → BLOCK → evaluation stops
  5. Both REDACT and BLOCK are combined: content is redacted AND the request is blocked

This is the standard firewall model. Use it when rule priority should be determined by pack/rule position.

All rules across all packs are evaluated regardless of matches. Any BLOCK or CANCEL action takes precedence over any ALLOW. A single matching deny in any pack blocks the request.

Behavior in deny_overrides mode:

Rule matchesWhat happens
BLOCK or CANCELImmediately terminal — evaluation stops; request is denied
ALLOWCollected but does not stop evaluation
ROUTE_TOCollected but does not stop evaluation
REDACTAccumulated as in first_applicable
PROMPT / ALLOW_WITH_OVERRIDECollected but overridden by any BLOCK/CANCEL

If no BLOCK or CANCEL fires, the most severe collected terminal action is returned (e.g., ROUTE_TO over ALLOW).

When to use deny_overrides: When your compliance or security policy requires that any explicit deny rule — regardless of position — overrides all permissive rules. For example, a compliance bundle’s BLOCK rule must win even if an engineering exception pack’s ALLOW rule would otherwise have matched first.

When to use first_applicable: When rule priority is positional — packs earlier in the chain should take precedence over later packs. This is appropriate for most deployments.


The Policy Chain editor includes a group filter that highlights which packs have rules that apply to a specific group.

  1. In the Policy Chain editor, locate the Filter by group field.
  2. Enter a group name (e.g., engineering, finance).
  3. The editor asynchronously checks each pack’s rules for user_groups conditions referencing this group.
  4. Packs that contain at least one rule with a condition matching the specified group are marked (highlighted).
  5. Packs with no group-specific rules are dimmed — their rules apply to all users regardless of group.

The group filter is display-only. It does not change the chain configuration or evaluation behavior. It is a diagnostic tool for understanding which packs are relevant to a specific user population.

The group filter value is persisted per browser session. When you open the Policy Simulator, the stored group value is pre-populated as the user groups context. You can clear it by removing the text in the filter field.

Pack appearanceMeaning
HighlightedPack has at least one rule with a user_groups condition that includes the filtered group
DimmedPack has no user_groups conditions matching this group — rules in this pack apply to all users or use other condition types
Normal (no filter set)No group filter active — all packs shown at equal prominence

Dimmed packs still apply to users in the filtered group. “Dimmed” means no group-specific rules — not that the pack is skipped.


Before saving changes to the chain or publishing new rules, use the Policy Simulator to verify how a specific request would be evaluated under the current configuration.

  1. Navigate to Settings → Policies → Simulator.
  2. Configure the evaluation context:
    • Prompt: the text of the request to simulate
    • Provider: the AI provider (anthropic, openai, google, ollama, mistral, cohere, bedrock, azure_openai, groq)
    • Model: the specific model name (e.g., claude-3-5-sonnet-20241022)
    • User groups: one or more group names representing the user’s group memberships (comma or Enter to add each group)
  3. If a group filter is active in the chain editor, click Use chain filter to pre-populate the user groups field from the stored filter value.
  4. Click Simulate.

The simulator posts to POST /api/admin/policy-chains/simulate with the evaluation context and returns the result.

The result panel shows:

FieldDescription
MatchedWhether any rule matched
ActionThe final enforcement action
Matched packThe pack containing the matching rule
Matched ruleThe specific rule that determined the final action
Match reasonWhy the rule matched (condition types that fired)
Evaluation traceFull ordered list of packs and rules evaluated, with match/skip status

Action results are color-coded in the result panel:

ActionColor
ALLOWGreen
BLOCKRed
CANCELGray
REDACTOrange
ROUTE_TOPurple

Evaluation trace rows where the match reason involves a group condition (user_group) are tagged with a group match badge. This makes it easy to identify which rules fired because of the simulated user’s group membership.

The simulator evaluates against the currently saved chain configuration, including any unsaved changes you have made in the editor (the editor uses optimistic state — changes visible in the UI are reflected in the simulation even before you click Save chain). Always run a simulation after editing the chain, and save if the results are as expected.

The simulator evaluates input direction only. It does not simulate output evaluation (model response evaluation). To test output rules, use the Policy Engine testing advanced testing workflow.


Example 1: Engineering bypass before compliance block

Section titled “Example 1: Engineering bypass before compliance block”

Goal: Engineers can send requests that would otherwise be blocked by the PCI-DSS bundle.

Chain (first_applicable):

Position 1 — Custom: "Engineering exceptions"
Rule 1: user_groups=["engineering"] → ALLOW
Position 2 — Bundle: "PCI-DSS Bundle"
(managed rules including CREDIT_CARD BLOCK)

Result: Engineering users match Pack 1 Rule 1 → ALLOW. The PCI-DSS bundle is never evaluated for them. All other users fall through to the PCI-DSS bundle.

Simulate this: Set user groups to engineering, enter a prompt containing a credit card number. Result should be ALLOW with Pack 1 trace showing match.


Example 2: Mandatory deny regardless of exceptions

Section titled “Example 2: Mandatory deny regardless of exceptions”

Goal: A specific entity type must always be blocked, even for privileged groups.

Chain (deny_overrides):

Position 1 — Custom: "Engineering exceptions"
Rule 1: user_groups=["engineering"] → ALLOW
Position 2 — Custom: "Hard blocks"
Rule 1: entity_types=["PATIENT_RECORD"] → BLOCK

Result: In deny_overrides mode, both packs are evaluated. If Pack 2 Rule 1 fires (PATIENT_RECORD detected), the BLOCK overrides the ALLOW from Pack 1 regardless of order.


Example 3: Identifying group-specific packs with the group filter

Section titled “Example 3: Identifying group-specific packs with the group filter”

Goal: Understand which packs affect the finance group before changing their exception rules.

  1. Set the group filter to finance.
  2. Review which packs are highlighted — these have user_groups=["finance"] conditions.
  3. Dimmed packs apply to all users including finance users, but do not have finance-specific targeting.
  4. Open the simulator, click Use chain filter to pre-populate finance in the user groups field, and run test prompts.