Skip to content

How it works

The explanation of the system is under Concepts: how it fits together, isolation, and workers. This page is a short map.

  OpenAI SDK / your app
           |
           |  bearer key
           v
      FastAPI gateway             never in the guest
      store (SQLite or Postgres)
           |
           |  in-process or worker lease
           v
      Pi  (+ stdio MCP)           none | microvm
           |
           +-- local files        next to Pi
           +-- or remote env      self_hosted runner
           +-- HTTP MCP           e.g. Tavily
What it controls
Run mode Where Pi (and stdio MCP) run
Environment Where file/shell tools run

Guest images are prebuilt files. A worker pulls them from an s3://, https://, or file:// source before it starts. The session picks an image id. Placement uses only a worker that has that image. The store format is ADR 0012.

HTTP routes do not spawn Pi themselves. They call SessionService, which sits above the session execution adapter. Extenders use the same service in-process (gateway.sessions), including stream() for catch-up from the store plus live EventHub events (and a store poll, the same as SSE). Combined apipi serve uses the in-process adapter. apipi serve --api-only leases a worker. Firecracker stays on the worker, or on combined serve as an embedded worker. EventHub is per API process; stream() is not a multi-replica bus.

To run the Agents API and your own routes in one process, build a Gateway with Gateway.create, call startup and shutdown from your FastAPI lifespan, call configure so app.state, middleware, and exception handlers are installed, then include_router for each gateway.routers.* you want. apipi serve does that wiring for standalone. startup attaches the store and starts usage and worker-lease expiry loops. Combined serve also starts idle-Pi and hosted workspace reap. In a split deploy those two loops run on apipi worker; the API copies are no-ops. Pass extend_settings(...) so host DATABASE_URL and OPENAI_* values do not leak in. Pass your Store if you own the engine; Gateway does not dispose an injected store. Pass authenticate= to inject the auth callback without APIPI_AUTH. Mounting create_app() under a path does not run its lifespan; call startup on the host app. If the Agents API is not at the domain root, APIPI_API_URL for workers must include that prefix. The full page is Extending ApiPi.

The durable store holds tenants, agents, sessions, turns, items, the event log, usage (never prompt text), and artifact metadata. Artifact bytes sit in the configured object store (local or S3). Hosted file and skill bytes use the same backends. Pi JSONL is a cache in that same blob store; the session row keeps a pointer, not the file. The event log is the transcript. Cross-tenant IDs return 404, not 403. SQLite is one process. Postgres is shared.

Auth is a callback on the bearer. See auth. Tools, MCP, and skills are in tools. Environments are in environments. Operator install is in install.