Sandboxes
Where sandboxes run, what happens to them over time, and what they can reach.
A sandbox is the isolated machine where the agent's commands run. The session lives on your Pistis server, so a sandbox is disposable: Pistis pauses it, restarts it or replaces it, and the agent carries on.
Where sandboxes run
- Docker. Containers on your server, each on its own private network. See Run it for a team.
- Kubernetes. Pods in their own namespace, fenced by a NetworkPolicy. See Run it on Kubernetes.
- Daytona. Daytona's cloud or your own Daytona install. Set
sandboxes.providertodaytona(below). - Your own machines. See Use your own machines.
- Anything else. Add a provider with a plugin.
Daytona
For Daytona's cloud, leave apiUrl out. For a Daytona you run yourself, point apiUrl at its API and use an API key from its dashboard:
"sandboxes": {
"provider": "daytona",
"apiUrl": "https://daytona.internal.example.com/api",
"apiKey": "${DAYTONA_API_KEY}",
"snapshot": "pistis-hands",
"relayPort": 8080,
"publicRelayUrl": "wss://relay.example.com"
}- The sandboxes dial out to Pistis at
publicRelayUrl, so that address must be reachable from your Daytona runners (behind a TLS proxy in front ofrelayPort). Nothing needs to reach into the sandboxes. - Use a snapshot that already holds
pistisd, the program that runs commands for Pistis, so a sandbox starts without uploading it. Leavesnapshotout and Pistis uploads it to a stock image instead. - Pistis decides when to pause, stop and delete. It turns Daytona's own auto-stop off, so a sandbox is never stopped in the middle of a command.
targetpicks a region when your install has several.
A sandbox's life
When a session sits idle, Pistis pauses its sandbox after 2 minutes, stops it after 30 minutes and deletes it after 7 days. The clock starts when a turn ends and never runs out during a command. On the next command:
- a paused sandbox resumes with its memory and files;
- a stopped sandbox restarts with its files;
- a deleted or lost sandbox is replaced, with your files restored from the end of the last turn.
Change the times with lifecycle in pistis.json.
Use your own image
The default image is Debian with git. Build your own on the base you already use, and copy Pistis's program in:
FROM node:24-alpine
COPY --from=pistis/pistisd:dev /usr/local/bin/pistisd /usr/local/bin/pistisd-musl /usr/local/bin/pi-env /usr/local/bin/pistisd-entry /usr/local/bin/
RUN mkdir -p /workspace
WORKDIR /workspace
ENTRYPOINT ["/usr/local/bin/pistisd-entry"]Any Linux base works, glibc or musl. Use the image with pnpm pistis dev --image my-sandbox or sandboxes.image in pistis.json.
Network access
Your Pistis server clones the repository and copies it in, so the sandbox needs no network for git. Everything else it reaches, such as package registries, is up to the sandbox provider. With Docker you choose one of three settings:
| Setting | The sandbox reaches |
|---|---|
none (default) | Nothing |
| An allow list | Only the hosts you list, through a proxy that logs each request. Use --allow-registry npm with pistis dev, or sandboxes.allow. |
open | The internet, but not other sandboxes. Use --internet, or sandboxes.internet. |
An allow-list entry is a host name or host:port, with no wildcards. Shorthands: npm, pypi, crates and go cover those registries.
Keep keys out of the sandbox
Don't give the sandbox an API key or a database password. Anything that needs a key, or that changes something real like a deploy, belongs in a tool on your Pistis server: an MCP server or a plugin tool. There it keeps the key, its calls are recorded, and it can wait for approval. A database the tests need can run inside the sandbox.