Monty
A minimal, secure Python sandbox written in Rust for code written by AI.
Monty avoids the latency, complexity and cost of a container based sandbox for running LLM generated code. It comes in two forms: OSS Monty, the MIT licensed Python 3.14 sandbox you install as a package, and Full Monty, the commercial server that runs the same sandbox behind a WebSocket as a service.
| Sandbox | New sandboxš | Agent run² | Combined³ | Execution envⴠ|
|---|---|---|---|---|
| OSS Monty | 0.80 ms | 0.40 ms | 1.20 ms | local |
| Full Monty (WebSocket) | 1.70 ms | 5.30 ms | 7.00 ms | remote |
| WASI / wasmtime | 16 ms | 180 ms | 200 ms | local |
| local Docker | 195 ms | 700 ms | 900 ms | local |
| Sandboxing service | 1500 ms | 400 ms | 1900 ms | remote |
| Pyodide in Deno | 2700 ms | 35 ms | 2700 ms | local |
- New sandbox: the time to get a fresh sandbox and run
1 + 1in it. For OSS Monty and Full Monty that is a checkout from a pool the application already created. The others have no pool, so each new sandbox starts from nothing. - Agent run: 10 commands run in a REPL against a sandbox that already exists, as you might expect from a simple agent with code mode. OSS Monty and Full Monty keep the session, so each command is one feed; the others have no persistent interpreter, so command n re-runs commands 1 to n.
- Combined: the time to create the sandbox and perform the agent run: the two columns added together.
- Execution env: OSS Monty, WASI, local Docker and Pyodide run the code on the same machine as the application calling them. Full Monty and sandboxing services run it remotely, which reduces the blast radius of an escape and lets the sandboxes scale independently of the hosts calling them.
Learn more in the comparison to alternatives.
- Latency in milliseconds, not seconds. A new sandbox plus ten REPL commands takes 1.2 ms vs. 1900 ms for a sandboxing service, because a sandbox is a checkout from a pool of worker subprocesses, a command is one message each way, and the session persists so nothing is re-run. See start latency.
- Simple to deploy at massive scale. Because youâre not provisioning a new VM or container for every sandbox you can run thousands of workers with minimal cost and complexity.
- Suspend and resume from bytes. Monty lets you dump the whole sandbox state to bytes at an external function call or at the end of a repl snippet. This makes long external function calls and human-in-the-loop not only possible but very cheap. It also makes extremely long running REPL sessions easy to implement. See snapshots.
- Strict resource limits maximum memory and execution time are enforced by the VM itself
so
'x' * 10**12raisesMemoryErrorbefore the allocation is attempted. See resource limits. - Local package for development, commercial option for scale. OSS Monty provides packages for Python, JS and Rust, making it trivial to get started with Monty. For greater security guards and larger scale deployments, Full Monty runs the same workers behind a WebSocket as a container image, adding OS-level isolation and horizontal scaling.
Installation
uv add pydantic-monty
npm install @pydantic/monty
cargo add monty-pool
The code string is what a model writes when asked how long a bar of chocolate could power a lightbulb.
It calls a tool it was given, does arithmetic it should not do in its head, and prints the answer:
from pydantic_monty import Monty
code = """
kcal = nutrition('chocolate bar')['kcal']
hours = kcal * 4184 / (bulb_watts * 3600)
print(f'a chocolate bar could power a {bulb_watts}W bulb for {hours:.1f} hours')
"""
with Monty() as pool:
with pool.checkout() as session:
session.feed_run(
code,
inputs={'bulb_watts': 10},
external_lookup={'nutrition': lambda food: {'kcal': 230}},
)
#> a chocolate bar could power a 10W bulb for 26.7 hours
Or in TypeScript:
import { Monty } from '@pydantic/monty'
const code = `
kcal = nutrition('chocolate bar')['kcal']
hours = kcal * 4184 / (bulb_watts * 3600)
print(f'a chocolate bar could power a {bulb_watts}W bulb for {hours:.1f} hours')
`
await using pool = await Monty.create()
await using session = await pool.checkout()
await session.feedRun(code, {
inputs: { bulb_watts: 10 },
externalLookup: { nutrition: (food: string) => ({ kcal: 230 }) },
})
// a chocolate bar could power a 10W bulb for 26.7 hours
nutrition ran on the host and the sandbox saw only its return value; the sandbox has no filesystem, environment or
network with which to reach anything else.
The Python, JavaScript and Rust quickstarts
take it from here.
Monty can do much more than this, see Examples.
LLMs are often faster, cheaper and more reliable when they write a short program that calls your tools, instead of making a sequence of individual tool calls: code mode from Cloudflare, programmatic tool calling and code execution with MCP from Anthropic, smolagents from Hugging Face. All of them need somewhere safe to run the generated code, and Monty is that place.
- Getting started with Python, JavaScript or Rust.
- Commercial support: Full Monty, the same workers behind a WebSocket as a container image.
- Security model for what âsecureâ does and does not mean here.
- Examples, including Code Mode in Pydantic AI.
- Limitations: the Python subset, and every known divergence from CPython.