Governed execution with `zamburak-monty`

Updated Jul 27, 2026

The zamburak-monty crate provides a governed execution path around the vendored full-monty interpreter. A GovernedRunner wraps a compiled MontyRun with a Zamburak observer and mediates every external-function call through a deterministic ExternalCallMediator hook.

Constructing a `GovernedRunner`

use std::sync::{Arc, Mutex};
use zamburak_monty::{
    AllowAllMediator, ExternalCallMediator, GovernedRunner,
};

let monty_run = monty::MontyRun::new(
    "x = 1 + 2\nx".to_owned(), "test.py", vec![],
).expect("parse failed");

let mediator: Arc<Mutex<dyn ExternalCallMediator>> =
    Arc::new(Mutex::new(AllowAllMediator));
let runner = GovernedRunner::new(monty_run, mediator);

Selecting an `ExternalCallMediator`

The mediator trait defines the deterministic hook invoked at each external-call boundary. Implementations receive a CallContext and return a MediationDecision:

  • MediationDecision::Allow — proceed with the external call,
  • MediationDecision::Deny { reason } — block the call with an explanation,
  • MediationDecision::RequireConfirmation { request } — yield to the host for interactive approval.

Built-in mediators:

  • AllowAllMediator — unconditionally allows every call (testing and permissive mode),
  • DenyAllMediator — unconditionally denies every call (deny-path testing),
  • PolicyMediator — evaluates calls against loaded policy rules (production use; see Policy-backed mediation below).

Each CallContext now includes an ifc payload describing the observer-driven information-flow state at that boundary:

  • propagation_modeNormal or Strict,
  • aggregate_summary — dependency summary for the whole call,
  • control_context — the active program-counter summary,
  • arg_summaries — per-positional-argument provenance,
  • kwarg_summaries — per-keyword (key, value) provenance in the same order as CallContext::kwarg_names,
  • kwarg_names — resolved keyword identifiers used to align policy rules with the corresponding keyword value summary.

For tests or embedder-controlled execution, GovernedRunner::with_ifc_config can override the default IFC configuration before execution starts. This is useful when strict mode must be forced or value seed labels must be customized. GovernedIfcConfig::strict_with_boundary_seeds and IfcValueSeedConfig::boundary_defaults provide the shared strict/boundary defaults used by the governed IFC tests.

`GovernedRunProgress` yield states

After execution, the governed runner returns a GovernedRunProgress enum:

  • Complete(MontyObject) — execution finished with a final value,
  • ExternalCallPending { context, suspended } — an external call was allowed and execution paused so the host can provide the actual result via the SuspendedCall,
  • Denied { reason, function_name, call_id } — an external call was denied by the mediator,
  • AwaitConfirmation { context, suspended } — execution paused pending host confirmation; the SuspendedCall can be resumed after approval,
  • NameLookup { name, inner } — execution paused for an unresolved name lookup,
  • ResolveFutures(...) — execution paused waiting for async futures.

Minimal governed run example

use std::sync::{Arc, Mutex};
use monty::{MontyObject, MontyRun, NoLimitTracker, PrintWriter};
use zamburak_monty::{
    AllowAllMediator, ExternalCallMediator, GovernedRunProgress,
    GovernedRunner,
};

let monty_run = MontyRun::new(
    "x = 1 + 2\nx".to_owned(), "test.py", vec![],
).expect("parse failed");

let mediator: Arc<Mutex<dyn ExternalCallMediator>> =
    Arc::new(Mutex::new(AllowAllMediator));
let runner = GovernedRunner::new(monty_run, mediator);

match runner.run_no_limits(vec![]) {
    Ok(GovernedRunProgress::Complete(value)) => {
        assert_eq!(value, MontyObject::Int(3));
    }
    other => panic!("unexpected result: {other:?}"),
}

Inspecting IFC at an external-call boundary

use std::sync::{Arc, Mutex};
use monty::{MontyRun, NoLimitTracker, PrintWriter};
use zamburak_monty::{
    AllowAllMediator, ExternalCallMediator, GovernedRunProgress,
    GovernedRunner,
};

let monty_run = MontyRun::new("effect(\"x\")".to_owned(), "test.py", vec![])
    .expect("parse failed");
let mediator: Arc<Mutex<dyn ExternalCallMediator>> =
    Arc::new(Mutex::new(AllowAllMediator));
let runner = GovernedRunner::new(monty_run, mediator);

match runner.run_no_limits(vec![]) {
    Ok(GovernedRunProgress::ExternalCallPending { context, .. }) => {
        assert_eq!(context.function_name, "effect");
        assert!(!context.ifc.arg_summaries.is_empty());
    }
    other => panic!("unexpected result: {other:?}"),
}