Docs
Framework Deep Dives
Runtime Policy

Runtime Policy

The Runtime Policy is the safety layer between the agent's intent and actual system effects. When the agent calls a tool, the policy decides where it runs (host, WASM sandbox, microVM), what it can access (filesystem, network, processes), and how much resources it gets (CPU time, memory, output size).

Why a policy layer

Without policy, every tool runs with the same permissions. A web search and a shell command have the same access to your filesystem. That's fine for a trusted local setup, but not for:

  • Running community-built plugins you haven't audited
  • Giving the agent code execution capabilities
  • Deploying an agent in a shared environment

The policy makes these decisions explicit and configurable — without changing tool code.

The RuntimePolicy trait


#[async_trait]
pub trait RuntimePolicy: Send + Sync {
/// Check if a tool is allowed to execute.
/// Returns an ExecutionGrant (how to run it) or a denial.
async fn check_permission(
&self,
tool_name: &str,
args: &Value,
context: &PolicyContext,
) -> Result<ExecutionGrant, PolicyDenied>;
/// Get the sandbox mode for a tool.
fn sandbox_for(&self, tool_name: &str) -> SandboxMode;
/// Get resource limits for a tool.
fn resources_for(&self, tool_name: &str) -> ResourceLimits;
}

The agent core calls check_permission() before every tool execution. The policy returns an ExecutionGrant that tells the executor exactly how to run the tool — or denies the call entirely.

Sandbox modes

Each tool invocation runs in one of these modes, determined by the policy:


┌─────────────────────────────────────────────────────────────┐
│ Sandbox Modes │
│ │
│ ┌─────────┐ ┌─────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Host │ │ WASM │ │ MicroVM │ │ Disabled │ │
│ │ │ │ │ │ │ │ │ │
│ │ Direct │ │ Process │ │ Full VM │ │ Tool │ │
│ │ exec on │ │ + WASM │ │ isolation│ │ cannot │ │
│ │ host OS │ │ sandbox │ │ via µsb │ │ be used │ │
│ └─────────┘ └─────────┘ └──────────┘ └──────────┘ │
│ Trusted Untrusted Code exec Prohibited │
│ tools plugins persistent by policy │
└─────────────────────────────────────────────────────────────┘

ModeIsolationFirst citizenUse case
HostNone — direct executionRobot tools, shell commandsTrusted, latency-sensitive
WASMProcess + WASM fuel limitsweb.search, http.fetchUntrusted, stateless
MicroVMFull VM via microsandboxcode.exec (Python, Bash)Persistent state, full OS
DisabledN/APer-policyBlock tools in certain contexts

These are the existing execution backends in Jan Agent. The policy layer formalizes routing to them — no new sandbox implementations.

Resource limits

Each tool execution is bounded:


pub struct ResourceLimits {
pub max_instructions: u64, // WASM fuel (1B default)
pub max_wall_time: Duration, // 30s default
pub max_memory_bytes: u64, // 256 MB default
pub max_output_bytes: u64, // 64 KB default
pub max_log_lines: u32, // 1000 default
}

Current hardcoded values (1 billion WASM fuel, 1800s timeout, 4KB log limit, 256KB output) become configurable defaults. No behavior change for existing users.

Permissions

Plugins declare what they need. Policy grants or restricts:

PermissionLevelsExample
Filesystemnone, read-only, read-writecode.exec needs read-write; web.search needs none
Networknone, outbound, fullweb.search needs outbound; robot tools need outbound
Processnone, spawncode.exec needs spawn; most tools need none

Policy configuration

Policies are defined in runtime-policy.yaml:


version: 1
defaults:
sandbox: wasm
permissions:
filesystem: none
network: none
process: none
resources:
max_wall_time_ms: 30000
max_memory_mb: 256
plugins:
code.exec:
sandbox: microvm
permissions:
filesystem: read-write
network: outbound
process: spawn
resources:
max_wall_time_ms: 300000
web.search:
sandbox: wasm
permissions:
network: outbound
resources:
max_wall_time_ms: 10000
robot.move_forward:
sandbox: host
permissions:
network: outbound

Resolution order

When determining policy for a tool:

  1. Plugin-specificplugins.<tool> in the policy file
  2. Global defaultsdefaults in the policy file
  3. Built-in defaults — hardcoded in the framework

More specific overrides less specific. No merging.

Built-in profiles

The framework ships two profiles for common setups:

host — for trusted, local development


defaults:
sandbox: host
permissions:
filesystem: read-write
network: full
process: spawn

Everything runs directly on the host. No sandboxing. This is the profile for the first-citizen ReAct agent — you trust the tools, you want low latency, and you're running locally.

standard — for mixed trust


defaults:
sandbox: wasm
permissions:
filesystem: none
network: none
plugins:
code.exec:
sandbox: microvm
permissions:
filesystem: read-write
network: outbound
process: spawn

Untrusted tools run in WASM. Code execution runs in a microVM. Built-in tools get the permissions they need. This is the profile for when you're using community plugins or giving the agent more autonomy.

Enforcement flow


Agent Core Policy Executor
│ │ │
│ dispatch("code.exec", │ │
│ args) │ │
│─────────────────────────▶│ │
│ │ │
│ check_permission() │
│ → sandbox: microvm │
│ → permissions: rw + net │
│ → limits: 5min, 1GB │
│ │ │
│ ExecutionGrant │ │
│◀─────────────────────────│ │
│ │ │
│──────────────────────────┼────────────────────────▶│
│ │ execute(grant) │
│ │ enforce limits │
│ │ │
│◀─────────────────────────┼─────────────────────────│
│ result │

The policy check happens before every tool call. The executor receives the grant and enforces it. Tools themselves are unaware of the policy — they just run. The framework handles the rest.

The policy layer adds zero overhead when using the host profile — it's a simple match that returns "allow everything". The cost only appears when sandbox routing decisions are needed.