--- # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 title: "Run Pi with OpenRouter" description: "Build a Pi coding-agent image, configure OpenRouter access, and run Pi inside an OpenShell sandbox." keywords: "Generative AI, Cybersecurity, Tutorial, Pi Coding Agent, OpenRouter, Sandbox, Provider Profile" --- 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 . ``` 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, skipping anything `.gitignore` excludes. You cannot combine `--upload` with a trailing command, so start Pi from the shell that opens. ```shell openshell sandbox create \ --name pi-project \ --from pi-agent:local \ --provider openrouter \ --upload .:/workspace ``` In the sandbox shell, run `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).