CEL Policy Reference

Strathon uses CEL (Common Expression Language) for policy rules. CEL is Google's open standard used in Firebase, Kubernetes, and Envoy.

Quick Reference

Comparisons

cel
attrs["gen_ai.tool.name"] == "search"           // exact match
attrs["gen_ai.tool.name"] != "shell_exec"       // not equal
attrs["gen_ai.usage.cost"] > 0.50               // greater than
attrs["gen_ai.usage.cost"] <= 1.00              // less or equal

Logical operators

cel
// AND — both conditions must be true
attrs["gen_ai.tool.name"] == "delete" && attrs["gen_ai.agent.name"] == "cleanup-bot"

// OR — either condition
attrs["gen_ai.tool.name"] == "rm" || attrs["gen_ai.tool.name"] == "drop"

// NOT — negate
!(attrs["gen_ai.tool.name"] in ["search", "read", "summarize"])

String matching

cel
// Regex match
attrs["gen_ai.prompt"].matches("(?i)ignore previous instructions")

// Contains substring
attrs["gen_ai.prompt"].contains("password")

// Starts with
attrs["gen_ai.tool.name"].startsWith("db.")

// Ends with
attrs["gen_ai.request.model"].endsWith("-mini")

List membership

cel
// Tool is in allowed list (whitelist approach — block everything NOT in list)
!(attrs["gen_ai.tool.name"] in ["search", "read_file", "summarize", "calculate"])

// Agent is in blocked list
attrs["gen_ai.agent.name"] in ["untrusted-bot", "test-agent"]

// Model is expensive
attrs["gen_ai.request.model"] in ["gpt-4o", "claude-opus-4", "gemini-1.5-pro"]

Time-based rules

cel
// Business hours only (UTC)
now.getHours() >= 9 && now.getHours() < 17

// Weekdays only
now.getDayOfWeek() >= 1 && now.getDayOfWeek() <= 5

Common Policy Templates

These are available as one-click templates in the dashboard.

Block prompt injection

cel
attrs["gen_ai.prompt"].matches("(?i)(ignore previous|disregard|forget your|you are now|act as|pretend to be|system prompt)")

Action: block

Block PII outbound

cel
attrs["gen_ai.completion"].matches("\\b\\d{3}-\\d{2}-\\d{4}\\b") || attrs["gen_ai.completion"].matches("\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b")

Action: block: stops the call when the response carries an SSN or email address. (Redaction itself is not a policy action: Strathon redacts PII from stored spans automatically at ingest: see redaction.)

Tool allowlist (production)

cel
!(attrs["gen_ai.tool.name"] in ["search", "read_document", "summarize", "calculate", "send_response"])

Action: block: blocks anything NOT in the list

Require approval for destructive actions

cel
attrs["gen_ai.tool.name"] in ["delete", "drop_table", "send_email", "deploy", "shutdown"]

Action: require_approval

Budget cap per model

cel
attrs["gen_ai.request.model"] == "gpt-4o" && attrs["gen_ai.usage.cost"] > 0.10

Action: throttle

Block secret leakage

cel
attrs["gen_ai.prompt"].matches("(?i)(sk-[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]{36}|-----BEGIN.*PRIVATE KEY-----)")

Action: block

Shadow mode (test without blocking)

Any policy can be set to shadow mode in the dashboard. Shadow policies evaluate every span but never enforce; results appear in traces for review. Use shadow mode to test a new policy before enabling enforcement.


Attributes Reference

AttributeTypeDescription
attrs["gen_ai.tool.name"]stringTool being called
attrs["gen_ai.agent.name"]stringAgent name
attrs["gen_ai.agent.id"]stringAgent unique ID
attrs["gen_ai.prompt"]stringPrompt text (on LLM spans)
attrs["gen_ai.completion"]stringResponse text (on LLM spans)
attrs["gen_ai.request.model"]stringModel name
attrs["gen_ai.usage.cost"]floatDollar cost of the call (on LLM spans)
attrs["gen_ai.usage.total_tokens"]intTotal token count
attrs["gen_ai.usage.input_tokens"]intInput token count
attrs["gen_ai.usage.output_tokens"]intOutput token count
attrs["gen_ai.workflow.name"]stringWorkflow name
attrs["gen_ai.conversation.id"]stringConversation ID
nowtimestampCurrent UTC time

Usage fields (gen_ai.usage.cost, gen_ai.usage.total_tokens, token counts) are filled in once a call completes, so use them with log and alert policies rather than to block a call before it runs. For hard dollar spend caps across many calls, use Budgets, which track cost per project, agent, or model.


Don't know CEL? Use AI to generate it

Copy this prompt into Claude, ChatGPT, or any AI assistant:

text
You are a Strathon CEL policy generator. Convert my plain English
description into a CEL expression for the Strathon AI agent firewall.

Available attributes in every span:
- attrs["gen_ai.tool.name"]      tool being called (string)
- attrs["strathon.tool.args"]    tool input arguments, as a JSON string
- attrs["gen_ai.agent.name"]     agent name (string)
- attrs["gen_ai.prompt"]         prompt text, on LLM spans (string)
- attrs["gen_ai.completion"]     response text, on LLM spans (string)
- attrs["gen_ai.request.model"]  model name (string)
- attrs["gen_ai.usage.cost"]     dollar cost, on LLM spans (float)
- attrs["gen_ai.usage.total_tokens"]  total token count (int)
- attrs["gen_ai.workflow.name"]  workflow name (string)
- now                            current UTC timestamp

Available actions: block, steer, throttle, log, alert, require_approval, allow

CEL syntax basics:
- == for equality, != for not equal
- && for AND, || for OR, ! for NOT
- "in" for list membership: x in ["a", "b"]
- .matches("regex") for regex matching
- .startsWith("prefix"), .endsWith("suffix")
- .contains("substring")

Output only the CEL expression. No explanation.

My policy: [DESCRIBE WHAT YOU WANT HERE]

Examples

Tell the AI: "Block shell commands from any agent"

cel
attrs["gen_ai.tool.name"] in ["shell_exec", "bash", "exec", "system"]

Tell the AI: "Require human approval when research-bot wants to send emails"

cel
attrs["gen_ai.agent.name"] == "research-bot" && attrs["gen_ai.tool.name"] == "send_email"

Tell the AI: "Alert when any agent spends more than $1 on a single call"

cel
attrs["gen_ai.usage.cost"] > 1.0

Attribute namespaces (which prefix to use)

Attribute names use one of two prefixes, and the prefix matters; a policy that references an attribute the engine doesn't emit silently never matches:

  • gen_ai.*: OpenTelemetry's standard GenAI attributes (tool name, agent name, prompt and completion text, model, token counts, workflow). Use these for the common fields; they are the convention and what most examples use.
  • strathon.*: Strathon's own additions that aren't in the OTel standard, most importantly strathon.tool.args (the tool's input arguments, as a JSON string).

The one easy mistake: the tool name is gen_ai.tool.name, but the tool arguments are strathon.tool.args: different prefixes. There is no gen_ai.tool.args. When in doubt, use the names exactly as listed in this reference rather than guessing the prefix.


Need Help?

  • Templates: Use one-click policy templates in the dashboard, no CEL needed
  • AI Generation: Copy the prompt above into Claude or ChatGPT
  • Documentation: getstrathon.com/docs
  • Community: Discord
  • Issues: GitHub Issues