PolicyMediator connects the zamburak-policy evaluation engine to the
governed runner's external-call boundary. Every call that reaches the mediator
is evaluated against the loaded policy rules and fails closed when tool or
summary information is unavailable.
Constructing a `PolicyMediator`
Load a policy into a PolicyEngine, then pass it to PolicyMediator::new:
use zamburak_policy::PolicyEngine;
use zamburak_monty::PolicyMediator;
let policy_yaml = r#"
schema_version: 1
policy_name: production_policy
default_action: Deny
strict_mode: true
budgets:
max_values: 100000
max_parents_per_value: 64
max_closure_steps: 10000
max_witness_depth: 32
tools:
- tool: get_last_email
side_effect_class: ExternalRead
default_decision: Allow
- tool: send_email
side_effect_class: ExternalWrite
required_authority: [EmailSendCap]
arg_rules:
- arg: body
forbids_confidentiality: [AUTH_SECRET]
context_rules:
deny_if_pc_integrity_contains: [Untrusted]
default_decision: RequireConfirmation
"#;
let engine = PolicyEngine::from_yaml_str(policy_yaml)
.expect("valid policy");
let mediator = PolicyMediator::new(engine);
Integrating `PolicyMediator` with `GovernedRunner`
Pass the mediator to GovernedRunner::new as the production
ExternalCallMediator implementation:
use std::sync::{Arc, Mutex};
use monty::MontyRun;
use zamburak_monty::{
ExternalCallMediator, GovernedRunner, PolicyMediator,
};
use zamburak_policy::PolicyEngine;
let policy_yaml = "..."; // omitted for brevity
let engine = PolicyEngine::from_yaml_str(policy_yaml)
.expect("valid policy");
let mediator: Arc<Mutex<dyn ExternalCallMediator>> =
Arc::new(Mutex::new(PolicyMediator::new(engine)));
let monty_run = MontyRun::new(
"effect(\"x\")".to_owned(), "test.py", vec![],
).expect("parse failed");
let runner = GovernedRunner::new(monty_run, mediator);
The governed runner then evaluates every FunctionCall and OsCall yield
through the policy engine before returning a GovernedRunProgress state.
Policy evaluation flow
When PolicyMediator::mediate is called, the mediator translates the
CallContext into a policy-layer-owned ExternalCallPolicyInput and calls
PolicyEngine::evaluate_external_call. The evaluation follows a deterministic
decision order:
- Tool lookup — the tool name (from
CallContext.function_name) is matched againstToolPolicy.toolin the loaded policy. A missing tool entry fails closed with a deny decision. - Context rules —
deny_if_pc_integrity_containsrules are checked against the active control-context integrity. Unrecognized integrity label strings in the policy also fail closed. - Authority requirements — each
required_authoritycapability must be present in the caller's authority set. Missing capabilities are denied. - Positional argument rules —
requires_integrityandforbids_confidentialityrules are checked against each positional argument's dependency summary. - Keyword argument rules — the same argument rules are checked against keyword argument value summaries, preventing bypass by passing guarded parameters as keyword arguments.
- Default decision — when no earlier rule fires, the tool's
default_decisionis returned.RequireDraftis conservatively mapped toRequireConfirmationfor Task 1.6.4.
Policy evaluation types
The zamburak-policy crate exposes the following public runtime evaluation
types at the crate root, for example:
use zamburak_policy::{
ExternalCallKind, ExternalCallPolicyDecision, ExternalCallPolicyInput,
KeywordArgumentSummary, PolicyDecisionExplanation, PolicyDecisionReason,
};
These public crate-root exports are:
ExternalCallKind— external-call classification used for policy diagnostics:Function,Os, orMethod. This enum allows the policy layer to distinguish between different call types without depending on Monty runtime internals.ExternalCallPolicyInput— input data for external-call evaluation (tool name, call kind, dependency summaries, caller authority, and control context).KeywordArgumentSummary— per-keyword policy input entry carrying the keyword name plus the key and value dependency summaries, soarg_rulesmatch the correct keyword argument.ExternalCallPolicyDecision— decision outcome:Allow,Deny, orRequireConfirmation, each carrying aPolicyDecisionExplanation.PolicyDecisionExplanation— metadata attached to a decision with both a machine-parseablereasoncode (PolicyDecisionReason) and a human-readablesummaryfield.PolicyDecisionReason— machine-parseable reason code for policy decisions, enabling structured audit pipelines and programmatic handling of policy outcomes. Variants includeMissingToolPolicy,ContextRuleDeny,MissingAuthority,InvalidAuthorityInPolicy,ArgumentIntegrityRequirement,ArgumentConfidentialityForbidden,DefaultAllow,DefaultDeny,DefaultRequireConfirmation, andRequireDraftMappedToConfirmation.
Fail-closed behaviours
Library consumers should be aware of the following fail-closed behaviours:
- Missing tool policy — any external call to a tool name not present in the loaded policy is denied.
- Unrecognized label strings — if the policy YAML contains a misspelt integrity or confidentiality label, the evaluation treats it as a deny condition rather than silently skipping the rule.
- Missing caller authority — if the caller's
AuthoritySetdoes not contain a required capability, the call is denied. - Budget overflow — when the dependency graph's transitive summary
computation exceeds configured budgets, the summary becomes
DependencySummary::unknown_top(), which produces conservative policy decisions.