Operate
Set limits, watch health, upgrade, rotate keys and read the logs.
What to set, watch and do once Pistis runs for a team.
Limits and capacity
| Key | Meaning |
|---|---|
sandboxes.cpu, memory, pids | Limits for each sandbox: CPUs, memory such as 4g, and processes (default 1024) |
sandboxes.max | The most sandboxes at once. A new sandbox past it is refused; the session says why, and the log has sandbox.refused. Existing sandboxes stay reachable. |
sandboxes.warm | Spare sandboxes kept ready per server, so new sessions start at once. Must be below max. |
A test with a cap of two: box limits 1 CPU / 512 MiB / 256 pids; a third box refused at sandboxes.max 2 (said and logged).
Health and metrics
Set metricsPort to serve /metrics for Prometheus and /healthz. They listen on 127.0.0.1, or on metricsHost inside a private network. Scrape every Pistis server and watch the p95, not averages.
| Metric | Meaning |
|---|---|
pistis_tool_call_seconds | Each command, by tool and outcome: ok, error or unknown (cut off) |
pistis_sandbox_lease_seconds | Time to get a session's sandbox ready |
pistis_turn_seconds | A message, from accepted to finished |
pistis_tool_unknown_total | Commands cut off by a crash |
pistis_sessions_live | Sessions not ended |
pistis_warm_boxes | Spare sandboxes ready |
Alert when /healthz returns 503, which names the failing check. Also alert on a rising p95 of pistis_tool_call_seconds, any rise of pistis_tool_unknown_total, and pistis_warm_boxes at zero. See Speed for every timing.
Upgrade
Back up, change the version, and restart the servers one at a time: see Upgrade. A new version updates the database when it starts, in one transaction, and records each change in schema_migrations; upgrades go forward only.
Rotate the secret key
PISTIS_SECRET_KEY encrypts the secrets people enter in the web app.
- Stop every Pistis server.
- Run
pistis rotate-secretwith the current and the new key:
$ PISTIS_SECRET_KEY=<current> PISTIS_NEW_SECRET_KEY=<new> pistis rotate-secret --config pistis.json
re-sealed 3 secrets in 2 rows; start every brain with PISTIS_SECRET_KEY set to the new key- Start every server with
PISTIS_SECRET_KEYset to the new key.
If one secret doesn't open with the current key, nothing changes and the command names it. API tokens are unaffected. The built-in secret store (people's MCP credentials) and pending OAuth PKCE verifiers are re-encrypted too; a store from a plugin keeps its own keys. If rotation reports legacy MCP credentials, start the server once with the old key to adopt them, then stop every server before rotating.
Docker access
Access to the Docker socket is root on the host, so Pistis servers don't get it. pistis docker-proxy --namespace <ns> holds the socket and lets Pistis create, start, stop, pause and delete its own sandboxes and networks. It refuses privileged containers, host mounts, exec, image pulls and builds, and logs each refusal as docker-proxy.refused. The compose file runs it as the docker-proxy service.
Logs
Every decision off the normal path is one JSON line with its reason: sandbox.refused, sandbox.replaced, checkpoint.skipped, sync.blocked, tool.unknown, token.revoked, docker-proxy.refused and the policy's refusals. A step stopped after repeated crashes is logged as pi.report (task_parked) and waits for its owner or an admin to retry it (Sessions).
Several servers
Pistis servers that share one Postgres share every session. One server handles each session at a time. If a server stops answering, another cuts off its database connection and takes over its sessions. Sessions are split between servers in a fixed number of shards: set it once with fleet.shards and never change it.