- Deny-by-default. Nothing is permitted unless a rule permits it.
- Forbid-overrides-permit. A single matching
forbidrule vetoes any number ofpermitrules.
The
PolicyEngine protocol is a seam. The default PythonPolicyEngine can be swapped for an embedded Cedar engine later without changing any call site.The Policy object
Policy is the ergonomic surface you write. It compiles to low-level rules and satisfies the PolicyEngine protocol, so you can pass it anywhere an engine is expected. It has four fields, all optional.
Allowlist vs permit-all semantics
Theallow field flips the engine between two modes.
- Permit-all (allow is None)
- Allowlist (allow is set)
When
allow is None, the engine emits a permit-all rule. Every tool is permitted unless it appears in deny. Use this for a permissive base where you subtract specific dangerous tools.Forbid overrides permit
Evaluation runs in a fixed order insidePythonPolicyEngine.evaluate:
1
Collect matching forbids
If any
forbid rule matches the request, evaluation stops and returns FORBID. No permit can rescue a call that a forbid matches.2
Collect matching permits
If no forbid matched, gather matching
permit rules. If any match, return PERMIT and merge their obligations (deduplicated and sorted).3
Default deny
If nothing matched, return
FORBID with reason default-deny.deny beats allow. Putting a tool in both lists still forbids it.
Fail-closed behavior
The entire evaluation body is wrapped in atry/except. If any rule condition raises (a buggy approve_when predicate, for example), the engine does not propagate the exception and does not fall through to permit. It returns a FORBID decision with reason fail-closed:<ExceptionType>.
What the engine evaluates
Each call is anAuthRequest, evaluated at a chokepoint. Its fields are available to your approve_when predicate and to any custom rule condition.
Decision.
reasons tuple traces which named rules fired (permit:allowlist, forbid:denylist, default-deny, fail-closed:KeyError), which makes decisions auditable.
The only shipped obligation is
Obligation.REQUIRE_APPROVAL. Other obligations such as redaction are intentionally not shipped until they are enforced, because a declared-but-unenforced control is worse than no control.The PolicyEngine seam
PolicyEngine is a runtime-checkable Protocol with a single method.
Policy and PythonPolicyEngine satisfy it. To go beyond the four-field Policy surface, build rules directly. Each Rule has an effect, an optional tools tuple (None matches any tool), an optional condition predicate (None matches unconditionally), obligations, and a name for audit reasons.
evaluate method is a valid engine, so you can also supply your own implementation behind the same protocol.
Related
Governance overview
How policy, risk, approval, and audit compose on the agent loop.
Approval
Turning a
REQUIRE_APPROVAL obligation into a human decision, including durable approval.