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
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 equalLogical operators
// 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
// 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
// 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
// Business hours only (UTC)
now.getHours() >= 9 && now.getHours() < 17
// Weekdays only
now.getDayOfWeek() >= 1 && now.getDayOfWeek() <= 5Common Policy Templates
These are available as one-click templates in the dashboard.
Block prompt injection
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
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)
!(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
attrs["gen_ai.tool.name"] in ["delete", "drop_table", "send_email", "deploy", "shutdown"]Action: require_approval
Budget cap per model
attrs["gen_ai.request.model"] == "gpt-4o" && attrs["gen_ai.usage.cost"] > 0.10Action: throttle
Block secret leakage
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
| Attribute | Type | Description |
|---|---|---|
attrs["gen_ai.tool.name"] | string | Tool being called |
attrs["gen_ai.agent.name"] | string | Agent name |
attrs["gen_ai.agent.id"] | string | Agent unique ID |
attrs["gen_ai.prompt"] | string | Prompt text (on LLM spans) |
attrs["gen_ai.completion"] | string | Response text (on LLM spans) |
attrs["gen_ai.request.model"] | string | Model name |
attrs["gen_ai.usage.cost"] | float | Dollar cost of the call (on LLM spans) |
attrs["gen_ai.usage.total_tokens"] | int | Total token count |
attrs["gen_ai.usage.input_tokens"] | int | Input token count |
attrs["gen_ai.usage.output_tokens"] | int | Output token count |
attrs["gen_ai.workflow.name"] | string | Workflow name |
attrs["gen_ai.conversation.id"] | string | Conversation ID |
now | timestamp | Current 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 withlogandalertpolicies 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:
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"
attrs["gen_ai.tool.name"] in ["shell_exec", "bash", "exec", "system"]Tell the AI: "Require human approval when research-bot wants to send emails"
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"
attrs["gen_ai.usage.cost"] > 1.0Attribute 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 importantlystrathon.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