Skip to main content
Shell lines like python3 script.py need an interpreter. Mirage calls that interpreter a runtime, and you pick it per workspace. The TypeScript packages ship two:

Pyodide (default)

Pyodide is full CPython in WebAssembly: real stdlib, sys.argv, stdin, and auto-loaded scientific packages. Mirage mirrors the workspace mounts into Pyodide’s in-memory filesystem, so open() and os.listdir work on mounted paths.

Monty

Monty runs each execution in a crash-isolated worker with microsecond startup and no host filesystem, environment, or network access. pathlib I/O routes through the workspace mounts, and the run’s env is readable either way Python spells it (os.getenv or os.environ):
Monty requires the optional @pydantic/monty package:
Without it, selecting monty makes python3 exit with code 127 and an install hint.

Differences from CPython

  • Command-line arguments are the argv global; sys.argv does not exist.
  • The builtin open() is not bridged yet in the JS binding; use pathlib (Path(...).read_text(), .write_text(), .iterdir()).
  • No sys.stdin and no third-party imports.
  • os.environ reflects the session env only.

Selecting in YAML

Server workspace config files take a top-level runtimes list:
Every runtime entry takes the same options (captures, config, script); the knobs that differ per runtime live in config. The home config key locates the runtime’s interpreter or distribution, in the spirit of JAVA_HOME. For pyodide that is where the distribution loads from: it defaults to the installed package in Node and the pinned CDN in the browser; point it at self-hosted assets to pin or air-gap the runtime (falls back to the MIRAGE_PYODIDE_HOME environment variable). monty embeds its interpreter and has no config keys yet. Python-only names (wasi, local) fail loud with a cross-language hint. In application code the entries are the runtimes workspace option, instances carrying their own options:

Resource limits

python3 is a command like any other: the same command_limits blocks that guard cat or grep guard it, enforced at the same central point. A run that exceeds timeout_seconds answers with exit 124 and python3: timed out after Ns on stderr, exactly like any other command; max_bytes and max_lines cap its output the same way. There is no python3-specific limit surface. The deadline stops the interpreter, not just the answer. Monty’s worker process is SIGKILLed when its run trips the deadline (or a background job is killed), so a runaway loop never keeps burning. Pyodide shares the event loop with the workspace, so the runtime arms its interrupt buffer from a watchdog thread: the guest gets a KeyboardInterrupt at the deadline and the run answers 124 even for a busy while True loop. The watchdog needs SharedArrayBuffer (always present in Node; in a browser only on cross-origin isolated pages) — without it, a busy pyodide loop blocks the event loop until it finishes, so prefer monty for untrusted code there.