Skip to content
Pi Durable Platform
v0.1.0First release · Open source, MIT

Pi Durable Platform

Deploy, test and manage your teams' durable AI agents

Pi Durable Platform (PDP) is a self-hosted console and API for agents built with Pi's durable package. Teams deploy agents from a reviewed template catalog and test them in the console; their apps reach them through the API. Each agent keeps its conversations in its own PostgreSQL schema, survives restarts, and sleeps when nobody is using it.

Self-hosted: one Go service with its web UI built in, PostgreSQL, and an agent runtime started on demand, as pods on Kubernetes or Agent Sandbox. An independent open-source project, not affiliated with or endorsed by Earendil, the makers of Pi.

Reach an agent from your app
PLATFORM=https://agents.example.com
KEY=pdp_sa_...   # a team service account's key

# One agent per user of your app: created the
# first time, returned after that
curl -X PUT \
  $PLATFORM/v1/teams/support/agents/by-key/user-42 \
  -H "Authorization: Bearer $KEY" \
  -d '{"name": "Assistant for user 42"}'

# Send a message; a hibernating agent wakes up
curl -X POST \
  $PLATFORM/v1/agents/$AGENT/threads/conv-1/messages \
  -H "Authorization: Bearer $KEY" \
  -d '{"text": "Where is my order?", "requestId": "m1"}'

# Follow the conversation as server-sent events
curl -N -H "Authorization: Bearer $KEY" \
  $PLATFORM/v1/agents/$AGENT/threads/conv-1/events

Apps talk only to the platform. It checks the caller, wakes the agent's runtime and relays the conversation; runtimes are never exposed.

The problem

Durable agents need somewhere to live

Once a company's teams build agents into their apps, someone has to run them, keep their state safe, give them models and tools, and answer for what they do.

  • Agents that forget

    An agent that lives in one process loses its conversation, or the reply it was writing, when that process restarts. Keeping thousands of them running all the time just to stay safe doesn't scale.

  • Keys and tools everywhere

    Every team wires its own agents to models and tools, with keys in each deployment. Nobody can say which agent uses which model or tool, or what it costs.

  • Nothing to govern with

    There is no reviewed list of what agents may be deployed, no separation between one team's conversations and another's, and no record of who changed what.

How it works

Apps in front, one runtime per active agent behind

Every request to an agent goes through the platform, which authenticates, authorizes and proxies it. Each agent is one Pi durable agent in its own runtime, with its state in PostgreSQL, so it can wake anywhere.

Apps and people

  • Your chat app
  • A support tool
  • The console's test chat
  • Scripts and services

to /v1/agents/<id>/threads/…

Pi Durable Platform

  1. 1Sign in. A browser session, a personal API key, a service account key or an app's OAuth Connect token.
  2. 2Allow. The caller's team role, who manages the agent, and whether the thread is private to someone else.
  3. 3Wake. If the agent is hibernating, start its runtime: a pod, a Sandbox, or a standby runtime from a warm pool.
  4. 4Relay. The message goes to the runtime with the agent's own token; the reply streams back as server-sent events.
  5. 5Record. The thread and its authors, model usage, MCP calls and every change, in the audit log.

Teams, templates, agents, threads and records in PostgreSQL · web console for teams and admins

One runtime per active agent

  • A Pi durable agent
  • Its own schema and database role
  • Hibernates when idle

Through the platform's gateway

  • Models from the catalog
  • MCP servers and their tools
An idle agent has no runtime and no database connection. The next request wakes it, and it carries on from what PostgreSQL holds, including a reply that was interrupted.

Features

From a reviewed template to an agent your app can call

PDP is one installation for one company. Agents belong to a team or to a person's personal workspace. Team owners and admins manage their team; platform admins review, configure and see everything; platform auditors see the same, read-only.

Templates and catalog

Templates are what you govern

Every agent comes from a template: its instructions, the model it runs on and the MCP tools it gets. Teams don't upload code; agents run the platform's own runtime.

  • A catalog of platform templates, curated by platform admins, and team templates published after a platform admin's review: approved into the catalog, or rejected with a note.
  • Versioned: a version never changes once made. New agents get the current one; an existing agent keeps its version until someone upgrades or rolls it back.
  • Team templates stay private to the team until it asks to publish them, and may use the team's own models and MCP servers, which keeps them out of the catalog.

Durable agents

State in PostgreSQL, runtimes on demand

Each agent is one Pi durable agent in its own runtime. Its conversations live in its own PostgreSQL schema, under its own database role, in a database separate from the platform's.

  • Survives restarts: a restarted agent keeps its conversations, deduplicates retried requests, and finishes a reply that was cut off.
  • One owner at a time: a runtime holds a lock on the agent's only database connection, so a stale runtime can't write once it loses it.
  • Isolated: an agent's role can't read another agent's schema, and a runtime answers only requests that carry its agent's own token, which the platform adds.
  • Hibernates when idle (after 10 minutes by default; no pod, no connection), and wakes on the next request.

Pi's own storage runs unchanged on PostgreSQL: the durable package's storage conformance suite (24 cases) passes against it.

Teams and workspaces

Agents belong to a team, or to one person

Ownership, data and cost follow the team. People sign in with your identity provider over OpenID Connect, optionally limited to some email domains or groups.

  • Team roles: owners and admins manage members, service accounts and the team's own models and MCP servers; editors deploy agents and write templates; members use the team's agents.
  • Service accounts for the team's apps: a member or editor role, keys that always expire, and up to two active keys so they rotate without downtime.
  • Service-managed agents: an app creates one per user by key. Only the team's service accounts talk to them; owners and admins see and delete them, never read them.
  • Personal workspaces: where platform admins turn them on, everyone gets one, deploying catalog templates, with limits on agents and on agents running at once.

Models

A model catalog, with keys kept in the platform

Platform admins add providers and publish their models to every team or to chosen teams. A template version picks a model; a team may choose another of its models for one agent.

  • Providers: OpenAI, Anthropic, and servers compatible with OpenAI chat completions, the OpenAI Responses API or the Anthropic Messages API. Discover models lists what a provider offers.
  • Proxy mode (the default): agents call the platform's gateway, which checks the model, adds the key and counts the tokens. Agents never see the key. Direct mode hands it to them.
  • Team providers: owners and admins can add their own, for their team alone, proxy only, at addresses the platform's guard allows.
  • Keys and extra headers are encrypted at rest and never returned.

MCP servers

Tools from a catalog, down to the tool

Platform admins add remote MCP servers and publish them to teams; the platform reads and keeps each server's tool list. A template version gives its agents a server's tools, all of them or only some.

  • Through the gateway by default: it adds the server's headers, refuses tools the version left out, and logs every call.
  • Team servers: owners and admins add MCP servers for their team only, reached through the gateway and the same address guard.
  • Apps' own servers, where a template allows it: a chat app can give one thread the MCP servers its user enabled, on hosts the operator allows. The platform keeps their headers.

Threads

Private or shared conversations, with authors

A conversation is a thread. Its first message makes it shared, seen by everyone who may use the agent, or private to the person or service account that started it.

  • Private means private: not the team's owners or admins either. Someone else's private thread reads as not found on every route.
  • Authors set by the platform: on shared team threads the agent is told who is speaking, and threads list who took part.
  • Reasoned reads only: platform admins can open a conversation by stating a reason; the read is in the team's audit log, where its owners see it.
  • Listing threads doesn't wake the agent; one event stream covers every agent a caller may use.

Usage and audit

Know what agents use, and who changed what

Model usage is counted for every request, through the proxy or reported by the runtime for models the proxy doesn't see, so nothing counts twice.

  • Usage reports for each team and for the platform: requests and tokens per day, model, agent or team, with a cost estimate from the models' prices.
  • An append-only audit log, written in the same transaction as each change, for the platform (with CSV export) and for each team.
  • Rate limits counted in PostgreSQL; keys and tokens stored only as peppered digests; secrets sealed with AES-256-GCM; the encryption key and the key pepper can be rotated, with the previous one still accepted.

For app builders

Reach agents from your app, as the app or as the person

The console's chat is for testing. People use agents through your apps, and your apps call the platform API. It is described in OpenAPI; replies stream as server-sent events, and a requestId makes a retried message safe.

As the app: service account keys

  • A team service account's key (pdp_sa_…) reaches the team's agents. With the editor role, it can also create them.
  • PUT …/agents/by-key/<your user ID> gives each user of your app an agent of their own, created the first time and returned after that.
  • Personal API keys (pdp_…) work too, acting as their person, for scripts.

As the person: OAuth Connect

  • The platform is an OAuth 2.1 authorization server: PKCE is required, and an app identifies itself with a Client ID Metadata Document or Dynamic Client Registration.
  • The person signs in with the platform's usual sign-in and allows the app once. Platform admins can mark apps as trusted, which skips the consent page, never the sign-in.
  • The app's token reaches the person's personal workspace and their teams' agents, without their platform role: an app acting for an admin gets no admin powers.
  • Refresh tokens rotate on every use; a stolen one, used again, ends the whole connection. People disconnect apps under Account › Connected apps.
OAuth Connect: act as the person who connected your app
PLATFORM=https://agents.example.com

# 1. Discover the authorization server
curl $PLATFORM/.well-known/oauth-authorization-server

# 2. Send the person to sign in and allow your app
$PLATFORM/oauth/authorize?response_type=code
  &client_id=https://chat.example.com/oauth/client-metadata.json
  &redirect_uri=https://chat.example.com/connect/callback
  &code_challenge=...&code_challenge_method=S256
  &state=...&scope=agents

# 3. Exchange the code at POST /oauth/token for a pdpat_...
#    access token (1 hour) and a pdprt_... refresh token

# 4. Call the platform as that person
curl -H "Authorization: Bearer pdpat_..." \
  $PLATFORM/v1/agents

Tokens are stored only as digests, every request re-checks the person (a suspended person is refused), and the audit log records what an app does as the person, through the app. OAuth Connect in the design notes

The web console

One console for teams and platform admins

Teams deploy and test their agents, write templates, and manage their models, MCP servers, service accounts and usage. Platform admins review templates and configure models, MCP servers and settings; auditors see the same pages, read-only.

A team's agents, deployed from templates. Agents its apps create for their users are marked as managed by a service.
Test an agent in the console before an app uses it. The conversation is kept in the agent's own storage.
Deploy from the catalog or the team's own templates. The agent is pinned to the template's current version.
Template versions don't change once made. New agents get the latest; existing agents move when someone upgrades or rolls them back.
Platform admins add providers and publish their models to every team or chosen ones. Keys are sealed and never shown again.
A team's owners and admins can add MCP servers for that team alone; agents reach them only through the platform.
Usage is counted for every model, proxied or not, with a cost estimate from the models' prices.
Apps reach a person's agents with OAuth Connect: the person allows the app once and can disconnect it under Account › Connected apps.

Self-host

Run it on your own infrastructure

Pi Durable Platform is one Go binary with the web UI built in, in a distroless image that runs as non-root with a read-only root filesystem. Agents run the runtime image, one per active agent, started by the launcher you choose.

With Agent Sandbox's warm pool, standby runtimes are already started: waking an agent claims one and activates it as that agent, skipping scheduling, image pull and start-up.

Try Pi Durable Platform on your machine
git clone https://github.com/ncecere/pi-durable-platform.git
cd pi-durable-platform
make deps-up                   # PostgreSQL 17, local only
(cd runtime && pnpm install)   # the agent runtime
make web                       # build the web UI
make run                       # http://localhost:8098
  1. Needs Go 1.26, Node 22.19, pnpm 11 and Docker. Sign in as Dev Platform Admin and create a team under Admin › Teams.
  2. Open the team's Agents, deploy one and use its Test tab. Agents run as local processes, on a scripted demo model until you add a real one.

Development sign-in and the example keys work only on a loopback address; the platform refuses to start with them anywhere else. Never expose them.

The v0.1.0 release images: the platform and the agent runtime
docker pull ghcr.io/ncecere/pi-durable-platform@sha256:15d1335db6c44d4499cef90097675320e8349a78bfc8b21b3d33e7a0588ef059
docker pull ghcr.io/ncecere/pi-durable-platform-runtime@sha256:2621a07f708fcdab089bba4384da08932d278e350fc55a985d178a498df55b35

# Verify each signature (cosign v3.0 or newer; keyless)
REPO=https://github.com/ncecere/pi-durable-platform
cosign verify ghcr.io/ncecere/pi-durable-platform@sha256:15d1335db6c44d4499cef90097675320e8349a78bfc8b21b3d33e7a0588ef059 \
  --certificate-identity \
    "$REPO/.github/workflows/image.yml@refs/tags/v0.1.0" \
  --certificate-oidc-issuer \
    https://token.actions.githubusercontent.com

Both are built for linux/amd64 and linux/arm64, with SBOMs, and signed at the release tag. The release notes list their digests: pin those in your deployment.

Agent Sandbox with a warm pool (excerpt of the platform's settings)
AGENTS_LAUNCHER=agent-sandbox
AGENTS_SANDBOX_WARM_POOL=agent-runtimes     # your SandboxWarmPool
AGENTS_SANDBOX_ACTIVATION_SECRET=agent-activation
AGENTS_GATEWAY_URL=http://<platform service>:<gateway port>
AGENTS_IDLE_TIMEOUT=10m
Launchers (AGENTS_LAUNCHER): where an active agent runs, and what hibernating does
LauncherRuntime per active agentHibernate
kubernetesA pod per agentDelete the pod
agent-sandboxA Sandbox per agent around the same podSuspend the Sandbox
agent-sandbox + warm poolA standby runtime claimed from a warm poolRelease the claim
localA child process (development)Stop the process

What you need

  • PostgreSQL 17: one database for the platform, and one for agents' state, reached by a role that may create roles
  • An OpenID Connect identity provider for sign-in
  • Kubernetes for agent runtimes, with Agent Sandbox for the sandbox and warm-pool launchers
  • TLS in front of the platform, on the host in APP_URL
  • Two 32-byte secrets, ENCRYPTION_KEY and API_KEY_PEPPER, kept with your backups

On Kubernetes

Agent pods get no service account token, a restricted security context and a read-only root filesystem, and can use a sandboxing RuntimeClass such as gVisor. Secrets reach them through a Secret per agent, never the pod spec. The platform needs to manage pods and Secrets (or sandboxes and claims) in the agents' namespace only; /healthz and /readyz serve the probes.

The repository doesn't ship Kubernetes manifests, NetworkPolicies or a Helm chart yet. Write NetworkPolicies so agent pods take traffic from the platform only and reach only its gateway, their database and the providers you allow. The settings are described in .env.example and the launchers in the design notes.

Open source and project status

Open source

MIT licensed, built in the open

Pi Durable Platform is released under the MIT licence. Run it for as many people as you like, and change the code if you need to.

The platform is written in Go with PostgreSQL and a React web UI built into the binary; the agent runtime is TypeScript. The API is described in OpenAPI, and a test fails if the served routes and the description differ. Integration tests run against real PostgreSQL, and an authorization matrix covers every operation.

It is an independent project. Agents are built with Pi's durable package, which has its own authors and licence; PDP isn't affiliated with or endorsed by Earendil, which makes Pi, and uses none of Pi's logos.

Project status

v0.1.0, the first release

Everything on this page is built and tested, but it is a first release, before 1.0, and hasn't run in production beyond its maintainer's own installation: expect changes between releases, and read the release notes before you upgrade. The durable package is experimental itself, so the runtime pins one version of it and its conformance suite guards every upgrade.

Planned, not built yet:

  • Waking a hibernating agent for scheduled work.
  • Files and artifacts, on object storage with a prefix per agent.
  • Enforcing personal workspaces' monthly cost budget, which is recorded today.
  • Approving an agent's tool calls in the conversation, once templates have tools that need it.
  • Incremental event streams: today each event carries the whole conversation view.
  • Teams uploading agents of their own, which waits for hostile-code isolation (gVisor or Kata).

Known limits: run one platform replica, since several haven't been tested; a schema per agent suits thousands of agents, not millions; any role in the agents database can take another agent's lock, which is acceptable for templates the platform runs, not for uploaded code; and thread IDs are chosen by apps, which should make them random.

Read the v0.1.0 release notes and the known limits and open items in the design notes.