> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hiddenlayer.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Configure Policy in the Console

Agentic Runtime Security policies are authored in the HiddenLayer Console using CEL detection rules. This page walks through the policy model and how to create rules, build a policy, and apply it.

***

The policy experience lives in the Console at **Runtime Security > Policy** and has three tabs: **Policy**, **Rules**, and **Configuration**.

## How policy works

The pieces build on each other:

* A **detection rule** is a reusable, named [CEL](https://cel.dev/) expression (with a threat level) that flags something in a session, for example prompt injection in user input.
* A **policy** composes one or more detection rules and assigns each an action: **Block**, **Detect**, or **Redact**.
* A policy is associated with a **project**, and that project's policy is applied at evaluation time when you send the project header.

Detection and enforcement are separate: a rule only describes a condition, and the policy decides what to do about it. Because the action lives on the policy rather than the rule, one rule can be reused by several policies, for example detect-only in staging and blocking in production, with no change to the rule itself.

So the workflow is: write the rules you care about, compose them into a policy with actions, attach the policy to a project, then reference that project at evaluation time. The rest of this page walks each step.

## Author a detection rule

Start by writing the individual rules. A rule is just a named CEL expression you can reuse across policies.

1. In the Console, go to **Runtime Security > Policy** and open the **Rules** tab.
2. Click **Create Rule**.
3. Fill in:
   * **Name**
   * **Description**
   * **Threat Level**: Critical, High, Medium, or Low.
   * **Expression**: the CEL expression (see below).
4. Click **Create**.

The built-in **System Rules** are viewable on the Rules tab as starting references.

## Choosing the message scope

A rule evaluates over the messages in a session, and the most important choice is which messages it looks at. For real-time detect/block on the current turn, scope the rule to the **latest message** so it fires once, on the turn that produced the condition:

```
size(messages) > 0 && messages[size(messages) - 1].analysis.signals.prompt_injection.detected
```

Be mindful with `messages.exists(m, ...)`, which matches **any** message in the session. Because a matched message stays in the conversation history, a `messages.exists(...)` rule used for enforcement fires on the original turn **and every turn after it**. Scope to the latest message instead; a scope mismatch like this is the most common cause of a rule that misbehaves.

Guard indexed access with `size(messages) > 0` so a rule is safe on an empty conversation.

## Writing CEL expressions

Detection-rule expressions evaluate over the session. The most common pattern targets the latest message, `messages[size(messages) - 1]`, reading `m.analysis.signals.<signal>.<field>` and optionally filtering on `m.role` (`user`, `assistant`, `system`, or `tool`).

The signal fields available on each message:

| Signal field                                   | Type            | Meaning                                                                                                     |
| ---------------------------------------------- | --------------- | ----------------------------------------------------------------------------------------------------------- |
| `prompt_injection.detected`                    | boolean         | Prompt injection detected on an input message (input phase).                                                |
| `personally_identifiable_information.entities` | list of strings | Detected PII entity types (for example `US_SSN`, `EMAIL_ADDRESS`, `PHONE_NUMBER`). Detected on any message. |
| `code.languages`                               | list of strings | Programming languages detected on the message.                                                              |
| `url.urls`                                     | list of strings | URLs detected in the content.                                                                               |
| `language.detected`                            | string          | Detected language code, ISO 639-1 (for example `EN`, `FR`). Empty when undetermined.                        |
| `denial_of_service.token_count`                | integer         | Token count of the message.                                                                                 |
| `guardrails.detected`                          | boolean         | A guardrail or model refusal fired on an output message (output phase).                                     |

The PII entity vocabulary is configurable. The default entity pack includes `EMAIL_ADDRESS`, `PHONE_NUMBER`, `CREDIT_CARD`, `US_SSN`, `IBAN_CODE`, `IP_ADDRESS`, `US_BANK_NUMBER`, `US_DRIVER_LICENSE`, `US_ITIN`, `US_PASSPORT`, `UK_NINO`, `URL`, and `GENERIC_API_KEY`.

`code.languages` is drawn from a fixed set: `bash`, `c`, `cpp`, `css`, `go`, `java`, `javascript`, `php`, `python`, `ruby`, `rust`, `sql`, and `typescript`. A single message can match more than one.

## Common policy examples

Once you understand the policy model and message scope, these examples show common rules and actions you can adapt. Each example includes the CEL expression when a rule is required, and the policy action to assign to that rule.

### Block prompt injection in the latest input

Detect prompt injection on the current turn; set the rule's action to **Block**.

```
size(messages) > 0 && messages[size(messages) - 1].analysis.signals.prompt_injection.detected
```

### Redact PII in outputs

Redaction is configured on the policy, not as a CEL rule. Choose the PII entity types to redact (for example `US_SSN`, `EMAIL_ADDRESS`, `PHONE_NUMBER`) and the redaction strategy in the policy configuration. Use a CEL rule only when you want to detect or block PII rather than redact it.

### Detect or block PII

To act on PII instead of redacting it, write a rule that fires on a specific entity (here, a Social Security number in an output); set its action to **Detect** or **Block**.

```
size(messages) > 0 &&
messages[size(messages) - 1].role == "assistant" &&
"US_SSN" in messages[size(messages) - 1].analysis.signals.personally_identifiable_information.entities
```

### Detect suspicious URLs

Flag a URL pointing at a known exfiltration host; set the rule's action to **Detect**. Match hosts case-insensitively with `matches("(?i)…")` so a rule can't be bypassed with different casing (for example `WEBHOOK.SITE`).

```
size(messages) > 0 && messages[size(messages) - 1].analysis.signals.url.urls.exists(u, u.matches("(?i)webhook\\.site"))
```

The PII detector also reports `URL` as an entity, which you can use for a simple presence check:

```
size(messages) > 0 && "URL" in messages[size(messages) - 1].analysis.signals.personally_identifiable_information.entities
```

### Block oversized inputs

Stop abusively large inputs on the current turn; set the rule's action to **Block**.

```
size(messages) > 0 && messages[size(messages) - 1].analysis.signals.denial_of_service.token_count > 10000
```

### Monitor tool use

Watch for a specific tool call; set the rule's action to **Detect**. Match on the content-part type rather than the message role, so the rule works across providers.

```
size(messages) > 0 && messages[size(messages) - 1].content.exists(c, c.type == "tool_use" && c.tool_name == "fetch")
```

### More examples

Detect code in the latest input:

```
size(messages) > 0 && messages[size(messages) - 1].analysis.signals.code.languages.size() > 0
```

Or target a specific language:

```
size(messages) > 0 && messages[size(messages) - 1].analysis.signals.code.languages.exists(l, l == "python")
```

Flag non-English input on the current turn:

```
size(messages) > 0 &&
messages[size(messages) - 1].analysis.signals.language.detected != "" &&
messages[size(messages) - 1].analysis.signals.language.detected != "EN"
```

## CEL reference

Detection rules use standard CEL plus a set of extensions. The following is what rule authors can rely on.

* **Root bindings:** `metadata`, `messages`, and `tools` (plus the reserved `expr` namespace for cross-referencing other rules).
* **Macros:** `exists`, `all`, `exists_one`, `filter`, and `map` (receiver-style, including two-variable forms), plus the global `has()` to safely check optional fields.
* **Operators:** `==`, `!=`, `&&`, `||`, `!`, `in`, the ternary `? :`, arithmetic, comparisons, and `[...]` indexing.
* **Functions:** strings (`contains`, `matches`, `startsWith`, `endsWith`, `size`, and more), list/set operations, math, base64, and IP/CIDR helpers such as `cidr("10.0.0.0/8").containsIP(...)`.
* **Regex:** author-facing regular expressions are available via `.matches("...")`.

For the exhaustive standard library, see the <a href="https://cel.dev/" target="_blank">CEL documentation</a>.

## Common CEL patterns

A few idioms recur across rule expressions:

* **Guard before indexing** (safe on empty conversations): `size(messages) > 0 && messages[size(messages) - 1]...`
* **Restrict by role**: `messages.exists(m, m.role in ["user", "system"] && ...)`
* **Combine signals** with boolean operators: `... .url.urls.size() > 0 && ... .personally_identifiable_information.entities.size() > 0`
* **Set membership** with `in`: `"US_SSN" in m.analysis.signals.personally_identifiable_information.entities`
* **Content-part traversal**: `m.content.exists(c, c.type == "text" && c.text.contains("password"))`

## Build the policy

With your rules created, compose them into a policy and decide what each rule does.

1. In the Console, go to **Runtime Security > Policy** and open the **Policy** tab.
2. Create a policy and attach your rules, setting each to **Block** or **Detect**.
3. Configure **Redact** entities and strategy as needed.

**Redact** is a policy action, not a rule: you choose which PII entity types to redact on the policy, and no rule expression is required. The PII detector runs on every message.

## Apply the policy

A policy takes effect once a project references it. A **project** is the unit you attach a policy to and reference at evaluation time. Associate the policy with a project, then send that project's id with the `HL-Project-Id` header (the `hl_project_id` keyword in the SDK) on each evaluation call, and that project's policy is applied.

<Tip>
  **Tip**

  You can prototype and validate CEL in **Runtime Security > Search**, save it as a query, and import it when creating a rule.
</Tip>

## Next

<Columns cols={1}>
  <Card title="Integrations" href="/docs/products/runtime/agentic/integrations">
    Put Agentic Runtime Security in front of your AI traffic with a guardrail or gateway integration.
  </Card>
</Columns>
