> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mirage.strukto.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Route Runtime

> Route commands across runtimes, free from the limits of a local terminal or a single kernel.

Mirage’s virtual terminal is not tied to a local terminal or a single kernel.
Your agent uses one shell while Mirage routes commands to in-process,
containerized or remote runtimes.

Use `route_policy` to choose a runtime for each command line with a script
or a function. Here, `monty` runs `python3` in-process, while `docker` runs
it in a container with your packages. Start the [Docker container](/home/setup/runtime/sandbox/docker)
`my-box` with `/jobs/train.py` and its dependencies before using this excerpt:

```yaml theme={null}
mode: exec
runtimes:
  - monty
  - name: docker
    captures: ["python3"]
    config:
      container: my-box
  - workspace
route_policy: {script: ./route.py, runtime: monty}
```

```python route.py theme={null}
import json
import os

ctx = json.loads(os.environ["MIRAGE_POLICY_CTX"])
paths = [p for c in ctx["commands"] for p in c["paths"]]
if any(p.startswith("/jobs/") for p in paths):
    print(json.dumps({"kind": "route", "runtime": "docker"}))
```

`python3 /jobs/train.py` runs in the container; `cat data.csv | python3
-c ...` stays on monty.

## Input and answers

A runtime routing script is a [policy program](/home/policy/scripts) shared
by every session. It reads the same `ctx` a program gets at `pre_execute`:
the line, each of its commands with its words and resolved paths, and
the session. It prints its answer:

| Prints | Effect |
| - | - |
| `{"kind": "route", "runtime": "docker"}` | that runtime serves every command it captures |
| `{"kind": "route", "runtime": "workspace"}` | the line runs in Mirage: no whole-line runtime (a `*` capture) takes it |
| nothing | the first capturer in `runtimes` order serves each command |
| `{"kind": "deny", "reason": "..."}` | the line is refused, as by any `pre_execute` program |

A program that exits non-zero, runs past 10 seconds, or prints anything
else fails the line with a routing error. It runs on its own runtime,
monty unless the block names one, as
[a profile's programs do](/home/policy/scripts#runtimes).

In code, `route_policy=` takes the same block with a `ScriptSource`, or
a plain function, sync or async, returning a runtime name or `None`:

```python theme={null}
from mirage.policy import RouteContext

def policy(ctx: RouteContext) -> str | None:
    paths = [p.virtual for c in ctx.commands for p in c.paths]
    return "docker" if any(p.startswith("/jobs/") for p in paths) else None

ws = Workspace(mounts, runtimes=runtimes, route_policy=policy)
```

## Admit, then route

Runtime selection happens at
[`pre_execute`](/home/policy/policies#lifecycle), before commands run.
Mirage checks `route_policy` first, then any policies defined in code.
These policies can also choose a runtime or refuse a command line:

```python theme={null}
from mirage.policy import Deny, Policy, Route, RouteContext

class JobsOnDocker(Policy):
    async def pre_execute(self, ctx: RouteContext) -> Route | Deny | None:
        if "/prod/" in ctx.line:
            return Deny("the prod tree is read only")
        return Route("docker") if "/jobs/" in ctx.line else None
```

A line the rules refuse is never placed. A Deny beats any Route, and two
policies naming different runtimes refuse the line. Nested lines (`$( )`,
`eval`, `source`, `xargs`) keep the outer line's placement.
`ws.explain(line)` shows each placement answer and each command's
runtime. The caller's `runtime=` argument places the line itself.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.