Conditions & operators
A condition is a typed test on one field of a captured action. It narrows a rule to the actions it should govern.
Start with the rule that catches people out: all conditions on one rule are AND’d together. The rule matches only when every condition holds. There is no OR. If you need OR logic, write two rules. Because the policy is first-match-wins by order, put the more specific rule first.
The shape of a condition
A condition is four fields: { field, op, value, display }.
- field — the name of the value to read off the captured action, like
amount_usdorhost. - op — the operator, one of the five below.
- value — the typed value to compare against. A bare number for numeric operators, a string for the rest.
- display — a plain sentence that lands in the receipt’s
matched_conditions, so a reviewer reads why the rule fired without parsing TOML.
[[rule.conditions]]
field = "amount_usd"
op = "gt"
value = 5000
display = "amount is over $5,000"Operators
The engine evaluates five operators.
| op | Value type | Holds when | Example |
|---|---|---|---|
gt | number | field is greater than value | amount_usd gt 1000 |
lt | number | field is less than value | retries lt 3 |
eq | string, number, or bool | field equals value | env eq "prod" |
contains | string | field (a string) contains value as a substring | host contains "internal" |
regex | string | field matches the value as a regular expression | model regex "^gpt-4" |
Type the value to match the operator. gt and lt want a bare number (5000, not "5000"). A quoted number against a numeric operator is the most common reason a rule quietly never fires.
Worked examples
Route large payments to a human
A numeric gt on amount_usd sends any payment over $1,000 to an approver instead of letting it through.
[[rule]]
id = "pay-cap"
order = 10
enabled = true
verb = "payment"
scope = "*"
decision = "require_approval"
[[rule.conditions]]
field = "amount_usd"
op = "gt"
value = 5000
display = "amount is over $5,000"Block exports to internal hosts
A contains check on the destination host blocks a data export whenever the host string includes internal.
[[rule]]
id = "block-internal-export"
order = 20
enabled = true
verb = "data_export"
scope = "*"
decision = "block"
[[rule.conditions]]
field = "host"
op = "contains"
value = "internal"
display = "destination host looks internal"Combine two conditions with AND
Two conditions on one rule are joined with AND. This rule routes to a human only when the payment is over $1,000 and the environment is production. Both must hold, or the rule does not match.
[[rule]]
id = "approve-large-prod-payouts"
order = 5
enabled = true
verb = "payment"
scope = "api.stripe.com"
decision = "require_approval"
[[rule.conditions]]
field = "amount_usd"
op = "gt"
value = 1000
display = "amount is over $1,000"
[[rule.conditions]]
field = "env"
op = "eq"
value = "prod"
display = "running in production"A condition reads the same fields that get signed into the receipt, which is the post-redaction map. If a field is redacted before signing, its plaintext is no longer there to compare against. Keep a non-sensitive signal (like region or amount_usd) for the conditions that still need to run. See Redaction for the mechanics.
Conditions narrow a rule, they do not loosen a floor. A condition can never let a dangerous lane through without approval. The engine rejects such a policy at load. Read Pinned floors before writing a rule that allows a payment, delete, account change, or large export.