Operator Reference
Edictum's expression grammar supports 15 operators across five categories.
Right page if: you need the exact syntax, value types, and behavior of Edictum's 15 YAML operators (exists, equals, contains, matches, gt, etc.). Wrong page if: you need the full contract bundle schema -- see https://docs.edictum.ai/docs/contracts/yaml-reference. For boolean combinators (all/any/not), see https://docs.edictum.ai/docs/contracts/patterns/advanced. Gotcha: type mismatches are fail-closed -- if a numeric operator receives a string, the contract denies. Missing fields evaluate to null, and `exists: false` is the only way to check for absence. Single-quote regex in YAML (`'\b'` = word boundary; `"\b"` = backspace).
Edictum's expression grammar supports 15 operators across five categories. Each leaf in a when expression uses exactly one operator applied to one selector.
For the full schema context, see the YAML Contract Reference.
Quick Reference
| Operator | Category | Selector Type | Value Type | Semantics |
|---|---|---|---|---|
exists | Presence | any | boolean | Field is present (true) or absent/null (false) |
equals | Equality | scalar | scalar | Strict equality |
not_equals | Equality | scalar | scalar | Strict inequality |
in | Membership | scalar | array | Value appears in array |
not_in | Membership | scalar | array | Value does not appear in array |
contains | String | string | string | Substring match |
contains_any | String | string | array of strings | Any element is a substring |
starts_with | String | string | string | Field starts with value |
ends_with | String | string | string | Field ends with value |
matches | String | string | string (regex) | Regex search matches |
matches_any | String | string | array of strings | Any regex matches |
gt | Numeric | number | number | Greater than |
gte | Numeric | number | number | Greater than or equal |
lt | Numeric | number | number | Less than |
lte | Numeric | number | number | Less than or equal |
Presence
exists
Tests whether a field is present and non-null, or absent/null.
- Value type: boolean (
trueorfalse) - Selector type: any
- Behavior with
true: passes when the field exists and is not null. - Behavior with
false: passes when the field is missing or null.
This is the only operator that works on missing fields. All other operators evaluate to false when the field is absent.
# Deny if no ticket reference is attached to the principal
- id: require-ticket
type: pre
tool: deploy_service
when:
principal.ticket_ref: { exists: false }
then:
effect: deny
message: "A ticket reference is required for deployments."# Deny only when the optional 'force' flag is explicitly set
- id: block-force-flag
type: pre
tool: delete_resource
when:
args.force: { exists: true }
then:
effect: deny
message: "The 'force' flag is not permitted."Equality
equals
Strict equality comparison using Python's == operator.
- Value type: any scalar (string, number, boolean)
- Selector type: scalar
# Deny tool calls in the production environment
- id: block-production
type: pre
tool: "*"
when:
environment: { equals: production }
then:
effect: deny
message: "Tool calls are disabled in production."# Deny when a specific tool argument matches a value
- id: block-admin-database
type: pre
tool: query_database
when:
args.database: { equals: "admin" }
then:
effect: deny
message: "Direct queries to the admin database are denied."not_equals
Strict inequality comparison using Python's != operator.
- Value type: any scalar
- Selector type: scalar
# Deny if the request is not targeting the staging environment
- id: staging-only
type: pre
tool: run_migration
when:
environment: { not_equals: staging }
then:
effect: deny
message: "Migrations are only allowed in the staging environment."Membership
in
Tests whether the selector's value appears in a provided list.
- Value type: array (at least one element)
- Selector type: scalar
# Allow only specific roles to use the deploy tool
# (deny everyone NOT in the list)
- id: deploy-role-gate
type: pre
tool: deploy_service
when:
principal.role: { not_in: [sre, admin, senior_engineer] }
then:
effect: deny
message: "Your role does not have deploy permissions."# Deny calls to known dangerous tools
- id: block-dangerous-tools
type: pre
tool: "*"
when:
tool.name: { in: [drop_database, truncate_table, format_disk] }
then:
effect: deny
message: "Tool '{tool.name}' is permanently denied."not_in
Tests whether the selector's value does NOT appear in a provided list.
- Value type: array (at least one element)
- Selector type: scalar
# Only allow API calls to approved endpoints
- id: approved-endpoints-only
type: pre
tool: call_api
when:
args.endpoint: { not_in: ["/v1/users", "/v1/search", "/v1/health"] }
then:
effect: deny
message: "Endpoint '{args.endpoint}' is not in the approved list."String
String operators require the selector to resolve to a string. If the resolved value is not a string, the operator triggers a policy_error (fail-closed).
contains
Substring match. Passes when the operator value appears anywhere in the selector's string value.
- Value type: string
- Selector type: string
# Block reads of files with ".env" in the path
- id: block-env-reads
type: pre
tool: read_file
when:
args.path: { contains: ".env" }
then:
effect: deny
message: "Reading .env files is not allowed."contains_any
Passes when any element in the provided array is a substring of the selector's value. This is a convenience operator equivalent to an any block with multiple contains leaves.
- Value type: array of strings (at least one element)
- Selector type: string
# Block reads of multiple sensitive file patterns in one contract
- id: block-sensitive-reads
type: pre
tool: read_file
when:
args.path:
contains_any: [".env", ".secret", "credentials", ".pem", "id_rsa", "kubeconfig"]
then:
effect: deny
message: "Sensitive file '{args.path}' is denied."
tags: [secrets, dlp]starts_with
Passes when the selector's string value starts with the operator value.
- Value type: string
- Selector type: string
# Block writes to absolute paths (enforce relative-only writes)
- id: block-absolute-writes
type: pre
tool: write_file
when:
args.path: { starts_with: / }
then:
effect: deny
message: "Write to absolute path '{args.path}' denied. Use relative paths."
tags: [write-scope]ends_with
Passes when the selector's string value ends with the operator value.
- Value type: string
- Selector type: string
# Warn when output is written to a log file
- id: warn-log-output
type: post
tool: write_file
when:
args.path: { ends_with: ".log" }
then:
effect: warn
message: "Output written to log file '{args.path}'. Verify no sensitive data is logged."
tags: [logging]matches
Regular expression match using Python's re.search(). The pattern can match anywhere in the string (it is not anchored).
- Value type: string (valid Python regex)
- Selector type: string
Patterns are compiled once at load time. An invalid regex causes a validation error.
YAML tip: Always use single-quoted strings for regex patterns. In YAML, '\b' is a literal backslash-b (regex word boundary). Double-quoted "\b" is interpreted as a backspace character.
# Block recursive delete commands
- id: block-rm-rf
type: pre
tool: bash
when:
args.command: { matches: '\brm\s+(-rf?|--recursive)\b' }
then:
effect: deny
message: "Recursive delete denied: '{args.command}'."
tags: [destructive]# Warn on SSN patterns in tool output
- id: ssn-in-output
type: post
tool: "*"
when:
output.text: { matches: '\b\d{3}-\d{2}-\d{4}\b' }
then:
effect: warn
message: "Possible SSN detected in output. Redact before using."
tags: [pii]matches_any
Passes when any regex pattern in the array matches the selector's value. Equivalent to an any block with multiple matches leaves, but more concise.
- Value type: array of strings (valid Python regex patterns, at least one)
- Selector type: string
# Detect multiple PII patterns in a single contract
- id: pii-detection
type: post
tool: "*"
when:
output.text:
matches_any:
- '\b\d{3}-\d{2}-\d{4}\b' # US SSN
- '\b[A-Z]{2}\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{0,2}\b' # IBAN
- '\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b' # credit card
then:
effect: warn
message: "PII pattern detected in output. Redact before using."
tags: [pii, compliance]Numeric
Numeric operators require both the selector value and the operator value to be numbers (int or float). If the selector resolves to a non-numeric type, the operator triggers a policy_error (fail-closed).
gt
Greater than.
- Value type: number
- Selector type: number
# Deny requests with a batch size greater than 1000
- id: limit-batch-size
type: pre
tool: bulk_insert
when:
args.batch_size: { gt: 1000 }
then:
effect: deny
message: "Batch size {args.batch_size} exceeds the maximum of 1000."gte
Greater than or equal.
- Value type: number
- Selector type: number
# Deny requests where retry count is 5 or more
- id: limit-retries
type: pre
tool: call_api
when:
args.max_retries: { gte: 5 }
then:
effect: deny
message: "Max retries of {args.max_retries} is too high. Use 4 or fewer."lt
Less than.
- Value type: number
- Selector type: number
# Warn when the confidence score is below threshold
- id: low-confidence-warning
type: post
tool: classify_document
when:
args.min_confidence: { lt: 0.5 }
then:
effect: warn
message: "Classification confidence threshold is below 0.5. Results may be unreliable."lte
Less than or equal.
- Value type: number
- Selector type: number
# Deny requests with a timeout of 0 or negative
- id: block-zero-timeout
type: pre
tool: call_api
when:
args.timeout: { lte: 0 }
then:
effect: deny
message: "Timeout must be a positive number. Got {args.timeout}."Type Mismatch Behavior
When an operator receives a value of the wrong type (for example, contains applied to an integer, or gt applied to a string), the evaluation triggers a policy_error. This means:
- The contract fires (fail-closed design).
- The audit event includes
policy_error: true. - For preconditions and session contracts, this results in a deny.
- For postconditions, this results in a warn.
This behavior is intentional. If Edictum cannot evaluate a contract, it assumes the worst case and fires the contract rather than silently ignoring it.
Missing Field Behavior
When a selector references a field that does not exist:
- The
existsoperator withfalsereturnstrue(the field is indeed absent). - The
existsoperator withtruereturnsfalse(the field is not present). - All other operators return
false(the contract does not fire).
This means a contract like args.path: { contains: ".env" } will not fire if the tool call has no path argument. No error is raised.
Custom Operators
Beyond the 15 built-in operators, you can register domain-specific operators via the custom_operators parameter on from_yaml(), from_yaml_string(), and from_template(). Each operator is a callable receiving (field_value, operator_value) and returning bool.
import ipaddress
def ip_in_cidr(field_value: str, cidr: str) -> bool:
return ipaddress.ip_address(field_value) in ipaddress.ip_network(cidr)
guard = Edictum.from_yaml(
"contracts.yaml",
custom_operators={"ip_in_cidr": ip_in_cidr},
)Custom operators follow the same missing-field and type-mismatch behavior as built-in operators. Names must not clash with the 15 built-in operators. Unknown operator names in YAML are caught at compile time.
For a full guide with examples, see Custom Operators.
Last updated on