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 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.
Author a detection rule
Start by writing the individual rules. A rule is just a named CEL expression you can reuse across policies.- In the Console, go to Runtime Security > Policy and open the Rules tab.
- Click Create Rule.
- Fill in:
- Name
- Description
- Threat Level: Critical, High, Medium, or Low.
- Expression: the CEL expression (see below).
- Click Create.
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: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:
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.Redact PII in outputs
Redaction is configured on the policy, not as a CEL rule. Choose the PII entity types to redact (for exampleUS_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.Detect suspicious URLs
Flag a URL pointing at a known exfiltration host; set the rule’s action to Detect. Match hosts case-insensitively withmatches("(?i)…") so a rule can’t be bypassed with different casing (for example WEBHOOK.SITE).
URL as an entity, which you can use for a simple presence check:
Block oversized inputs
Stop abusively large inputs on the current turn; set the rule’s action to Block.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.More examples
Detect code in the latest input: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, andtools(plus the reservedexprnamespace for cross-referencing other rules). - Macros:
exists,all,exists_one,filter, andmap(receiver-style, including two-variable forms), plus the globalhas()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 ascidr("10.0.0.0/8").containsIP(...). - Regex: author-facing regular expressions are available via
.matches("...").
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.- In the Console, go to Runtime Security > Policy and open the Policy tab.
- Create a policy and attach your rules, setting each to Block or Detect.
- Configure Redact entities and strategy as needed.
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 theHL-Project-Id header (the hl_project_id keyword in the SDK) on each evaluation call, and that project’s policy is applied.
Next
Integrations
Put Agentic Runtime Security in front of your AI traffic with a guardrail or gateway integration.

