Policy Engine
Hierarchical deny, allow, rate-limit, and approval rules evaluated before execution.
The policy engine decides whether an action is allowed to run — before any LLM call or tool invocation happens. Rules are evaluated in a strict priority order.
Evaluation order
Deny list — highest priority
If the action matches any deny pattern, it is blocked immediately, before cost is incurred.
Allow list
If an allow list is configured, the action must match at least one pattern, or it is blocked.
Rate limits
Sliding-window counters per pattern; actions exceeding the configured rate are blocked.
Approval gate — lowest priority
Matching actions require human approval via a registered callback before they proceed.
Configuration
from steerplane import PolicyEngine, RateLimitSpec
policy = PolicyEngine(
denied_actions=["delete_*", "drop_*", "sudo_*"],
allowed_actions=["search_*", "read_*", "summarize"],
rate_limits=[RateLimitSpec(pattern="send_email", max_count=5, window_seconds=60)],
require_approval=["refund_*", "wire_transfer"],
approval_callback=my_approval_fn,
)
decision = policy.check("delete_users")
# raises PolicyViolationError(rule="deny:delete_*")Matching uses POSIX glob patterns (fnmatch), so delete_* blocks delete_users, delete_db,
and so on — deterministically, with no model in the loop.
A policy violation always hard-terminates the run — it is a non-overridable safety invariant, independent of the enforcement mode.
Typical pattern
@guard(
agent_name="ops_agent",
denied_actions=["delete_*", "drop_*"], # destructive → blocked
# rate limits & approvals configured via PolicyEngine or .steerplane.yml
)
def run():
agent.run()