Skip to main content
A policy is an ordered list of rules. The first rule that matches a tool call wins. If no rule matches, the default_verdict applies.
Plyra Guard Policy Flow

YAML policy

Policies are defined in your guard_config.yaml file. Each policy has a name, a list of action types to match, a condition expression, and a verdict:
version: "1.0"

global:
  default_verdict: BLOCK

policies:
  - name: block-system-config
    action_types: ["*"]
    condition: 'parameters.get("path", "").startswith("/etc/")'
    verdict: BLOCK
    message: "System config is off-limits"

  - name: block-env-files
    action_types: ["*"]
    condition: '".env" in parameters.get("path", "")'
    verdict: BLOCK
    message: "No .env access"

  - name: allow-tmp
    action_types: ["file.read", "file.write", "file.delete"]
    condition: 'parameters.get("path", "").startswith("/tmp/")'
    verdict: ALLOW

  - name: escalate-schema-changes
    action_types: ["db.execute"]
    condition: '"DROP TABLE" in str(parameters)'
    verdict: ESCALATE
    message: "Schema changes require human approval"
guard = ActionGuard.from_config("guard_config.yaml")

Policy fields

FieldTypeDescription
namestrUnique identifier for the rule
action_typeslist[str]Action types to match. ["*"] matches all.
conditionstrPython expression evaluated against the ActionIntent
verdictstrOne of ALLOW, BLOCK, ESCALATE, DEFER, WARN
messagestrHuman-readable reason shown when the rule fires
escalate_tostr | NoneTarget for escalation (e.g. "human")

Verdict types

VerdictBehaviour
ALLOWTool executes normally
BLOCKTool not called. ExecutionBlockedError raised.
ESCALATEExecution paused. ActionEscalatedError raised. Pending human approval.
DEFERDeferred for async evaluation. ActionDeferredError raised.
WARNExecutes but emits a warning to the audit log

Default verdict

Always set default_verdict: BLOCK in production. This fails closed — any tool call that doesn’t match a policy rule is blocked. Unmatched calls are the most common source of unexpected agent behaviour.

Risk levels

The RiskLevel enum sets a baseline risk score for a protected action. The dynamic risk scorer adjusts it based on context (agent trust, parameters, etc.).
from plyra_guard import RiskLevel

@guard.protect("db.delete", risk_level=RiskLevel.HIGH)
def delete_record(id: str): ...
LevelBase scoreIntent
RiskLevel.LOW0.1Read operations
RiskLevel.MEDIUM0.3Reversible writes (default)
RiskLevel.HIGH0.6Destructive or irreversible
RiskLevel.CRITICAL0.9System-level or wide-scope

Trust levels

The TrustLevel enum classifies agents in multi-agent systems. Trust determines what risk thresholds an agent can execute under.
from plyra_guard import TrustLevel

guard.register_agent("researcher", trust_level=TrustLevel.PEER)
LevelTrust scoreDescription
TrustLevel.HUMAN1.0Human operator — full trust
TrustLevel.ORCHESTRATOR0.8Top-level orchestrating agent
TrustLevel.PEER0.5Peer agent in a collaborative system
TrustLevel.SUB_AGENT0.3Delegated sub-agent
TrustLevel.UNKNOWN0.0Unregistered or unknown agent

Dry-run evaluation

Test a policy without executing anything:
from plyra_guard import ActionIntent, Verdict

intent = ActionIntent(
    action_type="file.delete",
    tool_name="delete_file",
    parameters={"path": "/etc/passwd"},
    agent_id="default",
)

result = guard.evaluate(intent)
print(result.verdict)    # Verdict.BLOCK
print(result.reason)     # "System config is off-limits"
guard.evaluate() takes an ActionIntent object, not a plain string. See the API reference for all ActionIntent fields.

Real-world policy example

A policy for a database agent that can read freely but must escalate any destructive operation and block schema changes entirely:
version: "1.0"

global:
  default_verdict: BLOCK

policies:
  - name: allow-select
    action_types: ["db.query", "db.execute"]
    condition: 'str(parameters.get("sql", "")).strip().upper().startswith("SELECT")'
    verdict: ALLOW

  - name: escalate-delete
    action_types: ["db.execute"]
    condition: |
      any(kw in str(parameters.get("sql", "")).upper()
          for kw in ["DELETE", "UPDATE", "TRUNCATE"])
    verdict: ESCALATE
    message: "Destructive DB operation requires human approval"
    escalate_to: "human"

  - name: block-schema
    action_types: ["db.execute"]
    condition: |
      any(kw in str(parameters.get("sql", "")).upper()
          for kw in ["DROP", "ALTER", "CREATE TABLE", "RENAME"])
    verdict: BLOCK
    message: "Schema changes are not permitted by agents"
Rules are evaluated top to bottom. A SELECT query matches the first rule and is allowed immediately. A DELETE matches the second rule and is paused for approval. A DROP TABLE matches the third rule and is blocked outright. Any query that matches none of the rules hits the default_verdict: BLOCK.