Monty, a Rust Python sandbox for running code a model writes
A minimal, secure Python interpreter written in Rust for use by AI
At a glance
- What is it?
- Monty is Pydantic's MIT licensed Python sandbox written in Rust, aimed at one job: running LLM generated code without giving it a filesystem, network or environment variables. The trade is a Python 3.14 subset and an explicit channel to the host in place of ambient access.
- Who is it for?
- Monty fits a team whose agent writes short Python that computes, transforms or queries through functions the host supplies, and who cannot afford a container per execution. It does not fit code that needs real files, sockets or arbitrary libraries, because the sandbox has none of those and the host channel is something you design by hand.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- pydantic/monty8,924 ★ · +670
| Repository | 2026-09-22 | 2026-11-14 |
|---|---|---|
| pydantic/monty | 8.3k | 8.9k |
670 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
No filesystem, no environment variables, no network
The security model is an absence list, and it is short enough to remember. Inside a Monty sandbox there is no filesystem, no environment variables and no network. What the sandboxed code can reach on the host is exactly the set of functions and mounts the caller passed in, and nothing else.
That is a different bargain from a container, which gives the process a filesystem and a network namespace and then subtracts from there. Monty starts from nothing, so there is no ambient authority to argue about. The cost lands on the calling side instead: every capability model-written code wants has to be declared by the program that launches the sandbox, which is more work up front and easier to audit afterwards.
The sub-millisecond number assumes a pool is already running
The README puts a new OSS Monty sandbox at under 1ms taken from a running pool, Full Monty at about 2ms, and a sandbox service at around 1500ms, with a comparison page linked for the measurements. Read that first figure carefully, because the pool is doing the work. Starting cold is a different cost from the one quoted.
The architecture that follows from it is a pool plus a checkout. You construct a pool once, then take a session out of it per unit of work, which is why the first-run example nests two context managers rather than calling a single function. If your workload is one script per user request and nothing else, the pool never gets warm and the headline number is not the number you will see.
uv add pydantic-monty, then checkout and feed_run
Monty ships as a package for all three of its supported languages, and the install commands are the whole first step:
uv add pydantic-monty # Python
npm install @pydantic/monty # JavaScript / TypeScript
cargo add monty # RustThe first Python run takes a code string, a dict of inputs, and a mapping of host callables. This is the example from the repository, with its own comment showing the output:
from pydantic_monty import Monty
code = """
kcal = nutrition('chocolate bar')['kcal']
hours = kcal * 4184 / (bulb_watts * 3600)
print(f'a chocolate bar powers 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 powers a 10 W bulb for 26.7 hoursThe lambda passed as `external_lookup` executed on the host. The sandbox saw only the dictionary it returned, and the code string itself never touched a socket or a file.
inputs and external_lookup are the only route to the host
The example uses two channels and they are not interchangeable. `inputs` is a plain dict of values handed to the sandbox up front. `external_lookup` maps names to callables that stay on the host and are called from inside the run, with only the return value crossing back. Read together, they are a typed boundary you write by hand instead of one the runtime infers from the code.
The practical consequence is that prompts and code have to be written together. If a model writes `open('data.csv')` or `requests.get(...)`, there is nothing inside the sandbox to resolve that name, so the generator has to be told to emit a call against a declared lookup instead. Teams migrating existing agent prompts hit this rewrite first, before any performance question, and it is worth budgeting for.
Monty implements a Python 3.14 subset, not CPython
This is the limitation to read before adopting. The README calls OSS Monty a Python 3.14 sandbox, and the documentation links to a page devoted to the Python subset. Monty is an interpreter for a subset of the language, not CPython with a policy layer on top, and the repository carries a top-level `limitations` file alongside the crates.
The example directory reflects the supported shape rather than arbitrary Python: spreadsheet, expense analysis, SQL playground, web scraper, julia.py and julia_image.py. Budget for testing the generated code against the subset, not only against your logic.
OSS Monty and Full Monty ship the same workers two ways
There are two products here and the difference is deployment, not behaviour. OSS Monty is the MIT licensed package you install yourself and run in your own process. Full Monty is the commercial server that runs the same sandbox behind a WebSocket as a service, described as running the same workers as a container image.
That split decides who carries which cost. With the MIT package you own the pool, the host functions and the latency profile, and nothing leaves your process. With the commercial server the execution sits behind a WebSocket boundary instead, at roughly the 2ms figure the README quotes. Teams with a policy against running model-written code in their own address space have only the second option, and it is not the one the licence covers.
Docker, Pyodide and WASI are the alternatives on its own comparison page
The project publishes a comparison against four alternatives: Docker, Pyodide, WASI, and sandboxing services. They fail in different directions, which is why the choice is not a ranking exercise.
Docker is the heavyweight baseline: a real filesystem, a real network namespace and a real process, at the cost the README calls latency, complexity and price. Pyodide and WASI are the WebAssembly-based options on the same comparison page. Sandbox services sit at the other end, keeping code off your host entirely and paying for it in round-trip time.
Eighteen workspace members, and cargo builds one by default
The repository is a Rust workspace of eighteen members plus a fuzz crate, with `default-members` set to `crates/monty-runtime`. A bare `cargo build` at the root therefore compiles that one crate rather than the whole workspace, so anyone expecting to build everything needs the explicit workspace command that the Makefile's `install` target runs as `cargo check --workspace`. The workspace requires Rust edition 2024 and rust-version 1.96, and the Python packages are built through maturin rather than setuptools.
Maintenance is brisk. The last push to the default branch was on 2026-11-02, 1.4.0 was published on 2026-10-26 after v1.3.0 and v1.2.0 a few days earlier, and the workspace manifest reads 1.4.0, matching the newest release. Everything is MIT licensed, which covers the OSS package and the repository but not the Full Monty server.
Editorial conclusion
Monty fits a team whose agent writes short Python that computes, transforms or queries through functions the host supplies, and who cannot afford a container per execution. It does not fit code that needs real files, sockets or arbitrary libraries, because the sandbox has none of those and the host channel is something you design by hand. Check the Python subset page against your model's typical output before adopting it, then measure the pool checkout path rather than the sandbox startup number.
Frequently asked questions
What is Monty and what problem does it solve?
Monty is a secure Python sandbox written in Rust, published by Pydantic for running code that an LLM generated. It exists to avoid the latency, complexity and cost of a container based sandbox for that workload, and it comes in an MIT licensed package and a commercial server edition.
How do I install Monty for Python, JavaScript or Rust?
Use `uv add pydantic-monty` for Python, `npm install @pydantic/monty` for JavaScript and TypeScript, or `cargo add monty` for Rust. The quickstart guides for each of the three languages are linked from the project documentation.
Can sandboxed code in Monty reach files, the network or environment variables?
No. Filesystem, environment variables and network do not exist inside the sandbox. It reaches the host only through the functions and mounts you pass in, which the Python example shows by passing a host lambda as `external_lookup` and letting the sandbox see only its return value.
What is the difference between OSS Monty and Full Monty?
OSS Monty is the MIT licensed Python sandbox you install as a package and run in your own process. Full Monty is the commercial server that runs the same sandbox behind a WebSocket as a service, running the same workers as a container image.
Which Python version does Monty support?
The README describes OSS Monty as a Python 3.14 sandbox rather than a full CPython. The documentation links to a page covering the Python subset, and the repository keeps a top-level `limitations` file alongside the crates.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pydantic-monty)Star history chart for your README
[](https://hysenlabs.com/projects/pydantic-monty)Was this useful?