Rules & effects
How rules and effects are expressed in security.yaml.
Every entry under rules: matches a resource (a tool or endpoint) and says what happens when
an agent calls it. rules: is required and must have at least one entry —
security.yaml: at least one rule is required otherwise.
effect — a strict 3-value enum
rules:
- effect: allow
resource: search_docs
- effect: review
resource: stripe/refund
- effect: block
resource: stripe/payoutseffect accepts exactly three values: allow, review, block. There is no fourth
effect: modify — modification is a separate field (below). Anything else fails to load:
security.yaml: rules[1].effect "foo" is unsupported; use allow, review, or blockWhat each one resolves to at decision time:
effect: | Decision | Type | What the caller sees |
|---|---|---|---|
allow | permit | ALLOW | the tool runs |
review | defer | DEFER | suspended, routed to human approval (defer.policy) |
block | deny | DENY | the tool never runs (deny.default_or_forbid) |
review resolves to a plain defer (core.TypeDefer, reason defer.policy) by default. Add
a when: condition to a review rule and it resolves instead to a step-up-typed defer
(core.TypeStepUp, reason defer.step_up): "a step-up rule requires explicit human approval
for this call; approve via: raxit approvals approve <token>." See
Permit / defer / deny / modify / step-up for the full resolution
chain.
resource (required)
- effect: allow
resource: read_fileExact-match only against a Cedar entity — no glob support on the rule's own resource field.
(Globs do exist elsewhere in the schema — egress, credentials.resources,
warrants.templates[].tool, classify — just not here.) Empty fails:
security.yaml: rules[0].resource is required.
resourceType (optional)
- effect: allow
resource: api.internal.example.com
resourceType: endpoint"tool" (default) or "endpoint" — selects which Cedar entity type the resource targets.
Anything else: security.yaml: rules[0].resourceType "x" is unsupported; use tool or endpoint.
when (optional) — single-comparison condition
- effect: block
resource: send_email
when: recipient_count > 50A single <field> <op> <value> comparison only — no boolean combinators (no and/or).
op is one of < <= > >= == !=; field may contain letters, digits, _, .; value parses
as an integer first, else a string. Combines with validate: (below) using AND when both are
present on the same rule.
denyWhenTainted (optional) — lethal-trifecta sugar
- effect: block
resource: post_to_webhook
denyWhenTainted: [PII, SECRET]Only legal on effect: block rules — security.yaml: rules[0].denyWhenTainted requires effect: block otherwise. The rule fires only when the session's taint set carries any of the listed
labels. The closed vocabulary is 7 labels: PII, SECRET, FINANCIAL, CREDENTIAL, PHI,
PROMPT_INJECTION, COST_ANOMALY. See Taint & the lethal trifecta for
how a session accumulates these labels across calls.
You don't have to hand-write these: raxit init --taint-denial-posture strict generates one
co-located denyWhenTainted rule per discovered egress-capable tool automatically. See
taint_denial: for the document-level posture record this
pairs with, and raxit init for exactly what gets generated.
modify — arg transform (mutually exclusive with effect)
- resource: send_email
modify:
redact: [body, subject]A rule carries either effect: or modify:, never both —
security.yaml: rules[0] has both effect "allow" and a modify block; they are mutually exclusive. modify: is a single-op map: { <op>: [<arg-path>, ...] }.
The op set is redact, hash, drop — not truncate. truncate is explicitly rejected
with a load-bearing error, not silently ignored:
security.yaml: rules[0].modify: truncate is not yet supported via security.yaml — it needs a
max length the current shape cannot express, and a no-max truncate would silently forward the
original args; use redact, hash, or dropA different truncate action does exist, but only under postcondition.rules[].action (a
separate output-scanning subsystem, see Advanced) — don't
conflate the two.
At decision time a modify: rule resolves to permit with TypeModify and reason
modify.policy if the transform parses; if it doesn't, it fails closed to deny.modify_error
rather than forwarding the original, untransformed arguments.
validate — parameter-validation guard
- effect: allow
resource: send_email
validate:
field: recipient
allowlist: ["*@internal.example.com"]Exactly one of like (regex-ish), allowlist, or blocklist must be set, plus a required
field. Semantically a guard, not a denial of its own: a non-matching arg means the rule
simply does not fire, and the call falls through to whatever rule (or the default) matches
next — it never itself denies.
Duplicate rules
Two rules with the same computed effect (allow/review/block/modify), the same
resource, the same when, and the same validate signature are a load error:
security.yaml: duplicate allow rule for "calc". Rules for the same resource with different
when/validate are not duplicates.
Reference: every field
| Field | Required | Notes |
|---|---|---|
effect | yes, unless modify: is set | allow | review | block |
resource | yes | exact match, no glob |
resourceType | no | tool (default) | endpoint |
when | no | single comparison, no and/or |
denyWhenTainted | no | only with effect: block; 7-label vocabulary |
modify | no, mutually exclusive with effect | redact | hash | drop |
validate | no | exactly one of like/allowlist/blocklist |

