Pistis

How it works

The parts of Pistis, and why a crash loses no work.

Pistis splits a coding agent in two. The part that thinks and holds your keys runs on your server. Only the part that runs commands goes into the sandbox. That's why the sandbox can die at any time without losing work.

Your serverBrain • Model + agent loop• Conversation• Tokens• Ledger• Approvals Relaypasses messagesreads none of them Hands • pistisd• runs commands, edits files• /workspace: your repo no tokensaccepts no connections Trust boundary · untrusted side connects dials out calls ⇄resultsbytes only Internet only what its provider allows
Both sides connect to the relay; nothing connects into the sandbox. The relay only passes messages. What else the sandbox may reach is its provider's setting: nothing by default.

The parts

Other pages say "your Pistis server" for the brain. Config keys, commands and service names use these names:

  • Brain. The Pistis server process. It runs the agent and the model, and keeps the conversation, approvals and keys in its database. The web app, Slack and the HTTP API all talk to it.
  • Relay. A small service next to the brain. The brain and the sandbox both connect out to it, and it passes their messages without reading them. It holds no keys.
  • pistisd. A small program inside the sandbox. It reads files, writes files and runs commands. Nothing else.
  • Ledger. The brain's record of every tool call, written before the call goes out and again when its result is saved.
  • Checkpoint. A saved copy of the sandbox's /workspace, kept on your server after each turn.

How a command travels

  1. The model asks to run a command, such as npm test.
  2. The brain writes started in the ledger.
  3. The command goes through the relay to pistisd, which runs it in /workspace.
  4. The output streams back the same way.
  5. The brain saves the result and writes done.

When something dies

BrainSandbox crash in here → agent told “unknown”, command not run again write: started call runs: npm test result write: done crash after this→ result already kept
The ledger is written before the call leaves and after the result is stored. If anything dies in between, the brain tells the model the outcome is unknown instead of running the command again.
What diesWhat happens
The sandboxOn the next command, the brain resumes, restarts or replaces it, restored from the last checkpoint.
The brainAnother brain takes over its sessions, or it reloads them when it restarts. The turn continues.
The relayRestart it. It keeps its state in one file, so sandboxes reconnect.
The sandbox is lost mid-step. The step reads Outcome unknown · not run again, a new sandbox comes up with the workspace, and the agent carries on.

In each case, a command that was running is reported to the model as an unknown outcome, and never run again by Pistis.

Your work is on your code host too

After each turn, Pistis pushes the session's repo to your code host as refs/pistis/sessions/<session>: every change the agent made, committed or not. It is a hidden ref, not a branch, so no CI runs and your branch list stays clean. The push comes from your server with the session's credential; the sandbox never holds it. The agent's own git state doesn't change.

So the work survives even when the sandbox, the server's disk and the session are all gone. To get it back:

git fetch origin refs/pistis/sessions/ses_8a2c…:recovered
git switch recovered

A change that looks like a credential (a cloud access key, a private key, a token) is not pushed. The server logs sync.blocked with what it found, never the value.

Why not retry?

A workflow engine retries a step after a crash. That is fine for ls and dangerous for git push --force, a migration or a deploy. Pistis never guesses. The model is told the command may have partly run, and it decides what to do.

Checkpoints

Only /workspace is saved, as of the last finished turn. Installed system packages and running processes are not. Copies over 256 MiB are skipped and the previous copy is kept. A new sandbox is restored before setup runs, so setup doesn't run twice.

How a sandbox joins

BrainRelaySandbox 1 · make a join token: one use, ≤ 10 min 2 · ask Docker to start the sandbox with the token 3 · pistisd dials out with it 4 · reply on that link: token used up, passes issued 5 · wait for it, send network rules, open a session 6 · signal on pistisd's link; it opens a new one out
The only secret your server hands a sandbox is a join token that works once and expires within ten minutes. After that it holds only relay passes, useless anywhere else. Arrows toward the sandbox are replies on connections pistisd opened.

The only secret a new sandbox gets is a join token. It works once, for one sandbox, and expires within ten minutes. A start-up script moves it out of the environment before any of your code runs. After joining, the sandbox holds only passes that work at the relay and nowhere else.

Under the hood

Pistis is built on two open-source libraries: pi-durable, which saves the agent's state so it survives crashes, and pi-env, which runs file operations and commands on a remote machine. pi-env's messages travel unchanged over a WebSocket through the relay.

On this page