pydantic-monty-runtime
The compiled `monty` CLI and worker binary that gives pydantic-monty and @pydantic/monty crash-isolated Python sandbox execution.
Repository Health
Technical Analysis
pydantic-monty-runtime packages the compiled monty binary for PyPI, the same way uv and ruff distribute their Rust binaries: installing the wheel drops monty straight into the environment’s scripts directory, with no compiler or manual build step required. The binary is built from the monty-runtime Rust crate inside the pydantic/monty workspace, and it is the execution engine underneath Monty, a minimal, secure Python interpreter written in Rust for sandboxing LLM-generated code.
Most users never install this package directly — the pydantic-monty metapackage pulls it in automatically alongside the pydantic_monty Python bindings, which spawn monty as a crash-isolated worker subprocess to run untrusted, agent-generated code with microsecond startup and strict controls over filesystem, network, and resource usage. Installed on its own, pydantic-monty-runtime gives you just the monty CLI: an interactive REPL, monty file.py execution, monty -c "<code>" one-liners, and a monty subprocess worker mode that speaks a framed protobuf protocol over stdio for embedding in a parent process’s worker pool.
What You Get
- Compiled
montybinary - a maturin-built,bindings = "bin"wheel that places the Rust-compiledmontyexecutable directly in the environment’s scripts directory on install. - Standalone CLI -
montyfor an interactive REPL,monty file.pyto run a script, andmonty -c "<code>"for one-off snippets, mirroringpython’s own invocation flags. - Subprocess worker mode -
monty subprocessruns the binary as a wire-protocol child, reading framed protobuf requests from stdin and streaming events back on stdout, so a parent pool (likepydantic-montyormonty-pool) gets crash isolation for free. - Type checking on demand -
-t/--type-checkruns the bundledty-powered type checker before executing, with multiple diagnostic output formats (full,concise,json,github, and more). - Sandboxed filesystem mounts -
-m/--mountmaps a host directory into the sandbox in read-only, read-write, or in-memory overlay mode, with an optional write-size limit. - Resource limit flags -
--max-memory,--max-duration,--max-recursion-depth, and--gc-intervalbound a session’s heap, wall-clock time, call-stack depth, and GC cadence.
Common Use Cases
- Standalone tool install -
uv tool install pydantic-monty-runtimeto get a globalmontycommand for exploring the sandbox interactively, independent of any Python project. - Supplying the worker binary - installing
pydantic-monty-runtimealongside an existingpydantic-monty-clientinstall when the binary needs to come from a separate build or pinned version. - Crash-isolated agent code execution - as the worker binary spawned by
pydantic-monty’sAsyncMonty/Montypools or the JavaScript@pydantic/montypackage, so a stack overflow or allocator abort in agent-written code kills only the worker subprocess, never the host process. - CI and local debugging of the sandbox itself - running
monty -csnippets directly to check what a given piece of Python does under Monty’s supported subset before wiring it into an agent.
Under The Hood
Architecture
The monty-runtime crate (main.rs, run.rs at ~700 lines, subprocess.rs at ~135 lines) builds one binary with two distinct execution paths behind a shared Cli argument struct: a standalone path (run.rs) that drives MontyRepl/MontyRun from the core monty crate for REPL, file, and -c execution, wired to monty-fs for sandboxed mounts and monty-type-checking for optional type checking; and a subprocess path (subprocess.rs) that is a thin stdio shell around monty_proto::worker::Child, a transport-agnostic state machine reading framed protobuf requests and emitting framed events, explicitly designed so a parent pool can distinguish a graceful FatalError from an uncommunicative crash. A process-wide monty_alloc::LimitedAllocator global allocator enforces soft and hard memory ceilings across both paths, and a standalone Cargo feature can strip the REPL/terminal stack entirely to build a subprocess-only worker binary.
Tech Stack
Pure Rust, built with maturin using the bindings = "bin" mode (the same PyPI packaging pattern uv and ruff use) so the compiled binary lands in the wheel’s scripts directory with no Python glue code. Dependencies include clap for argument parsing, rustyline plus anstream/anstyle for the interactive REPL and terminal styling, monty-proto for the protobuf worker protocol, monty-type-checking (backed by Astral’s ty), monty-fs for mount handling, and an optional logfire/tracing pairing gated behind a telemetry feature that instruments only the standalone CLI path, never the subprocess worker.
Code Quality
The crate carries substantial integration test coverage relative to its size — a 1,329-line tests/subprocess.rs exercising the worker protocol and a tests/mounts.rs covering filesystem mount modes. Error handling is explicit and structured: the subprocess loop matches every FrameError variant and exits with dedicated BSD sysexits.h-style codes so a parent process can classify failures programmatically, and a custom panic hook guards the worker loop. The wider workspace runs CI (ci.yml) and continuous performance tracking (codspeed.yml), with dense, precise doc comments throughout explaining non-obvious design decisions (why telemetry lives only in the standalone path, why memory limiting must be declared in the binary crate).
API Design
The binary deliberately offers two ergonomic surfaces for two very different audiences: a CLI that mirrors python’s own flags (-c, -i, file execution) for a developer poking at the sandbox by hand, and a strict framed-protocol subprocess mode for a parent process embedding Monty programmatically. Packaging the compiled binary itself as a plain PyPI/npm-installable artifact (rather than requiring a Rust toolchain or a separate download step) removes the most common friction point in shipping native tooling to Python and JS developers.