> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/openshell/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/openshell/_mcp/server.

# Run Pi with OpenRouter

> Build a Pi coding-agent image, configure OpenRouter access, and run Pi inside an OpenShell sandbox.

In this tutorial, we'll run [Pi](https://pi.dev), a terminal coding agent,
inside an OpenShell sandbox. Pi works with many model providers out of the box,
including [OpenRouter](https://openrouter.ai), which we use here.

We'll build a Pi image, give the sandbox access to OpenRouter, and start Pi.
The sandbox lets Pi reach OpenRouter but nothing else on the network, and Pi
never sees your real API key.

## Prerequisites

You'll need:

* A working OpenShell installation with an active gateway. Refer to
  [Installation](/about/installation).
* Docker or Podman to build the image.
* An OpenRouter API key.

## Build the Pi Image

Pi doesn't publish a container image, so we'll build one from its npm package,
based on Pi's [container guide](https://pi.dev/docs/latest/containerization).
Save this Dockerfile as `Dockerfile.pi`:

```dockerfile
FROM node:24-bookworm-slim

ARG PI_VERSION=latest

RUN apt-get update \
    && apt-get install -y --no-install-recommends bash ca-certificates fd-find git ripgrep \
    && ln -s /usr/bin/fdfind /usr/local/bin/fd \
    && rm -rf /var/lib/apt/lists/*

RUN npm install -g --ignore-scripts "@earendil-works/pi-coding-agent@${PI_VERSION}"

RUN mkdir -p /workspace && chown node:node /workspace
USER node
WORKDIR /workspace

ENV PI_CODING_AGENT_DIR=/tmp/pi-agent
```

We add two things to Pi's guide because the sandbox limits what Pi can reach:

* We install `fd`. Pi uses `fd` and `ripgrep` to search files, and if they're
  missing, it downloads them from GitHub on first use. The sandbox blocks that
  download, so the image includes both.
* We set `PI_CODING_AGENT_DIR` so Pi keeps its settings and sessions in `/tmp`,
  one of the few places the sandbox lets Pi write.

Now build the image:

```shell
docker build -t pi-agent:local -f Dockerfile.pi .
```

> **Note**
>
> If your gateway uses Podman, build with
> `podman build -t localhost/pi-agent:local -f Dockerfile.pi .` and use
> `localhost/pi-agent:local` in the commands below. If your gateway runs on
> another machine, push the image to a registry the gateway can pull from, and
> use that image reference with `--from`.

## Give Pi Access to OpenRouter

A sandbox has no network access until you grant it. To let Pi reach
OpenRouter, we'll create a provider, which stores your API key and opens access
to the service. Pi only ever sees a placeholder for the key. OpenShell swaps in
the real key only when Pi calls OpenRouter.

A provider is built from a provider profile, which describes the service. Save
this one as `provider-openrouter.yaml`:

```yaml
id: openrouter
display_name: OpenRouter
description: Direct OpenRouter inference access
category: inference
inference_capable: true

credentials:
  - name: api_key
    description: OpenRouter API key
    env_vars:
      - OPENROUTER_API_KEY
    required: true
    auth_style: bearer
    header_name: authorization

discovery:
  credentials:
    - api_key

endpoints:
  - host: openrouter.ai
    port: 443
    protocol: rest
    access: read-write
    enforcement: enforce

binaries:
  - /usr/local/bin/node
```

Here's what the main sections do:

* `credentials` declares the API key. Pi finds the placeholder in
  `OPENROUTER_API_KEY`.
* `endpoints` allows only `openrouter.ai`, and OpenShell sends the key nowhere
  else.
* `binaries` allows only `node` to use this access. Pi runs on Node.js, so
  that includes Pi.

Check the profile, import it, and create a provider named `openrouter`:

```shell
openshell profile lint -f provider-openrouter.yaml
openshell profile import -f provider-openrouter.yaml

OPENROUTER_API_KEY=your-key \
  openshell provider create \
    --name openrouter \
    --type openrouter \
    --from-existing
```

Replace `your-key` with your OpenRouter API key, or drop that prefix if
`OPENROUTER_API_KEY` is already set in your shell. `--from-existing` tells
OpenShell to read the key from that variable.

## Start Pi in a Sandbox

Next, create a sandbox from the image, attach the provider, and start Pi:

```shell
openshell sandbox create \
  --name pi \
  --from pi-agent:local \
  --provider openrouter \
  -- pi
```

Everything after `--` runs inside the sandbox, so Pi and any commands it runs
stay contained.

Pi starts with its default OpenRouter model. To pick a different one, type
`/model` and choose any OpenRouter model from the list. OpenRouter usually has
some free models, marked with a `:free` suffix, which are handy for trying this
out without spending credits.

Then give Pi a try:

```text
Explain what files are available in this workspace.
```

The workspace starts empty, but we'll give Pi your own code in a later step.

## Inspect the Sandbox

While Pi is running, open another terminal and take a look at the sandbox's
status, access, providers, and logs:

```shell
openshell sandbox list
openshell policy get pi --full
openshell sandbox provider list pi
openshell logs pi --tail
```

`openshell logs` streams the sandbox's activity, including any connections it
blocks. When you're done, type `/quit` in Pi to exit.

## Work on a Local Project

To point Pi at your own code, run this from your project directory. `--upload`
copies the directory into the sandbox before Pi starts, skipping anything
`.gitignore` excludes.

```shell
openshell sandbox create \
  --name pi-project \
  --from pi-agent:local \
  --provider openrouter \
  --upload .:/workspace \
  -- pi
```

Pi's changes stay inside the sandbox. To copy them back, run
`openshell sandbox download` from another terminal while Pi is still running.
Refer to [Transfer Files](/how-it-works/sandboxes/overview#transfer-files).

## Clean Up

When you're finished, delete both sandboxes:

```shell
openshell sandbox delete pi pi-project
```

## Troubleshooting

* If the gateway can't find `pi-agent:local`, make sure you built the image
  with the same container engine your gateway uses, or pushed it to a registry
  the gateway can reach.
* If Pi's requests to OpenRouter are denied, make sure the `binaries` path in
  the profile matches where `node` is installed in your image. In the image
  above, that's `/usr/local/bin/node`.

## Next Steps

* To give Pi more tools, add them to the image. To let Pi reach package
  registries, source hosts, or other services, grant access with a
  [sandbox policy](/how-it-works/policies/overview).
* To use a different model service, write a provider profile for it. The
  [`providers/`](https://github.com/NVIDIA/OpenShell/tree/main/providers)
  directory has examples, and [Profiles](/how-it-works/providers/profiles)
  describes each field.
* To learn about images, resources, file transfer, and the sandbox lifecycle,
  refer to [Sandboxes](/how-it-works/sandboxes/overview).