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 and os.getenv route through the workspace mounts:
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:
Options live flat on the runtime entry. The home option 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 options 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_safeguards 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. One caveat: the guard bounds how long a run may answer, not the interpreter itself. A run that ignores the deadline keeps its interpreter busy in the background until it finishes (monty runs on a crash-isolated worker; pyodide shares the event loop, so prefer monty for untrusted code). Give untrusted code a finite workload, or run it behind a sandboxed deployment.