Usage Guide: Policy¶
Overview¶
This guide shows how to declare a PolicyDefinition that denies a specific capability and wire it into the orchestrator via PolicyGuard so that a tool node which requires that capability is blocked before it executes. The pattern mirrors the policy-block scenario in examples/26_governance_walkthrough.py.
Minimal example¶
from zeroth.governance.policy import PolicyGuard
from zeroth.governance.policy.models import Capability, PolicyDefinition
from zeroth.governance.policy.registry import CapabilityRegistry, PolicyRegistry
# 1. Declare a policy that denies one capability.
no_network_write = PolicyDefinition(
policy_id="block-network-write",
denied_capabilities=[Capability.NETWORK_WRITE],
)
# 2. Register the policy and every Capability value under its own ref.
policy_registry = PolicyRegistry()
policy_registry.register(no_network_write)
capability_registry = CapabilityRegistry()
for cap in Capability:
capability_registry.register(cap.value, cap)
# 3. Build the guard and hand it to the orchestrator.
guard = PolicyGuard(
policy_registry=policy_registry,
capability_registry=capability_registry,
)
orchestrator.policy_guard = guard # RuntimeOrchestrator attribute
Bind the policy to the graph or node by adding its policy_id to graph.policy_bindings or node.policy_bindings. A tool node that declares capability_bindings=["network_write"] will now terminate with RunStatus.FAILED and failure_state.reason == "policy_violation" before running — the denial is written to the audit trail as execution_metadata["enforcement"] = {"decision": "deny", "reason": "..."}.
Common patterns¶
- Graph-wide allow list. Attach one
PolicyDefinitionwithallowed_capabilities=[...]tograph.policy_bindings; every node inherits it. - Node-local override. Add a tighter policy to a single sensitive node via
node.policy_bindings— stacked checks are AND-combined. - Secret scoping. Set
allowed_secrets=[...]on a policy and useapply_secret_policy(...)so nodes see only the variables they are permitted to read. - Sandbox strictness. Set
sandbox_strictness_mode="strict"or atimeout_override_secondsto propagate constraints into the sandboxed executable-unit runner.
Pitfalls¶
- Forgetting to register capability values.
CapabilityRegistryis not pre-populated; if you don't register eachCapabilityvalue, the guard cannot resolve nodecapability_bindingsstrings. - Binding the policy but not attaching the guard.
graph.policy_bindingsis inert untilorchestrator.policy_guardis assigned aPolicyGuardinstance. - Expecting a denial to raise. Policy denials terminate the run with
failure_state.reason == "policy_violation"; they do not raise Python exceptions to the caller ofrun_graph. - Mixing allow and deny lists carelessly.
denied_capabilitieswins overallowed_capabilitieswhen the same capability appears in both. - Omitting audit review. A blocked run still produces audit records — always query them after a denial to confirm which node tripped the rule.
Reference cross-link¶
See the Python API reference for zeroth.governance.policy.
Related: Concept: policy, Usage Guide: approvals, Usage Guide: audit, Tutorial: governance walkthrough.