Skip to main content
Reference

CEL functions reference

Keep rule expressions use CEL (Common Expression Language) with custom functions for temporal logic, rate limiting, content detection, string manipulation, and secret detection.

Standard CEL

All built-in CEL operators, macros, and functions are available in Keep expressions. This includes:

  • Arithmetic: +, -, *, /, %
  • Comparison: ==, !=, <, >, <=, >=
  • Logical: &&, ||, !
  • String methods: .contains(), .startsWith(), .endsWith(), .matches(), .size()
  • List operators: .exists(), .all(), .filter(), .map(), in
  • Ternary: condition ? a : b

See the CEL language spec for the full list.

Input variables

Every expression has access to three variables:

VariableTypeDescription
paramsdynCall parameters — fields vary by operation
contextdynCall metadata (agent ID, timestamp, etc.)
nowtimestampCurrent timestamp, injected from context.timestamp

Missing field accesses on params or context evaluate to false rather than returning an error.

Temporal functions

inTimeWindow

inTimeWindow(now, start, end, tz) -> bool

Returns true if now falls within the time-of-day window [start, end) in the given timezone.

ParameterTypeDescription
nowtimestampTimestamp to check (use the now variable)
startstringStart time in "HH:MM" format (24-hour)
endstringEnd time in "HH:MM" format (24-hour)
tzstringIANA timezone name (e.g., "America/New_York")

Example:

inTimeWindow(now, "09:00", "17:00", "America/New_York")

Note: Midnight-wrapping is not supported. If end <= start, the function returns false. To match overnight windows, use two rules or combine with !.

dayOfWeek

dayOfWeek(now) -> string
dayOfWeek(now, tz) -> string

Returns the lowercase weekday name (e.g., "monday", "friday"). The one-argument form uses UTC; the two-argument form converts to the given IANA timezone first.

ParameterTypeDescription
nowtimestampTimestamp to check (use the now variable)
tzstringOptional. IANA timezone name

Example:

dayOfWeek(now) == "saturday" || dayOfWeek(now) == "sunday"
dayOfWeek(now, "Asia/Tokyo") == "monday"

Rate limiting

rateCount

rateCount(key, window) -> int

Increments a counter for key and returns the number of hits within window. Use this to enforce rate limits per agent, per operation, or any arbitrary grouping.

ParameterTypeDescription
keystringCounter key — typically built from context fields
windowstringTime window: "30s", "5m", "1h". Min 1s, max 24h

Example:

rateCount(context.agent_id + ":create_issue", "1h") > 100

Note: rateCount always increments the counter, including during audit_only evaluation. Counters are local to the process and are not shared across instances.

Content detection

containsAny

containsAny(field, terms) -> bool

Returns true if field contains any of the strings in terms. Matching is case-insensitive.

ParameterTypeDescription
fieldstringText to search
termslist(string)Substrings to match against

Example:

containsAny(params.body, ["password", "secret", "api_key"])

estimateTokens

estimateTokens(field) -> int

Returns a rough token count for field, calculated as the character (rune) count of field divided by 4.

ParameterTypeDescription
fieldstringText to estimate

Example:

estimateTokens(params.prompt) > 10000

Note: This is a character-count heuristic, not a tokenizer. Actual token counts vary by model.

String manipulation

lower

lower(field) -> string

Returns field converted to lowercase.

Example:

lower(params.status) == "urgent"

upper

upper(field) -> string

Returns field converted to uppercase.

Example:

upper(params.priority_label) == "P0"

matchesDomain

matchesDomain(email, domains) -> bool

Returns true if the domain part of email matches any entry in domains. Subdomain matching is supported: "eng.example.com" matches "example.com".

ParameterTypeDescription
emailstringEmail address to check
domainslist(string)Allowed domain names

Example:

matchesDomain(context.user_id, ["example.com", "example.org"])

Note: Returns false if email does not contain an @ character. Comparison is case-insensitive.

Secret detection

hasSecrets

hasSecrets(field) -> bool

Returns true if the field contains patterns that look like secrets (API keys, tokens, passwords). Detection uses gitleaks pattern rules.

ParameterTypeDescription
fieldstringText to scan

Example:

hasSecrets(params.message)

Note: Pattern-based detection. It catches common secret formats but does not guarantee detection of all secrets, and false positives are possible.