Skip to content

Pi Durable Platform v0.3.0

Choose a tag to compare

@github-actions github-actions released this 20 Oct 04:28
· 90 commits to main since this release
3e390b6

Images

The platform (the Go server with the web UI):

ghcr.io/ncecere/pi-durable-platform:v0.3.0
ghcr.io/ncecere/pi-durable-platform@sha256:01972d4f8cf6dfa2e79f2834f2643590d2aca43ba705a347aa0d39040d198a36

The agent runtime (one agent per container; the platform starts it):

ghcr.io/ncecere/pi-durable-platform-runtime:v0.3.0
ghcr.io/ncecere/pi-durable-platform-runtime@sha256:c4e967f0ff40df422971303e72bddc3f2225876f55029716f4300ca3126da4a5

Deploy by digest. Verify the signatures with cosign v3 or newer (keyless, GitHub Actions OIDC):

cosign verify ghcr.io/ncecere/pi-durable-platform@sha256:01972d4f8cf6dfa2e79f2834f2643590d2aca43ba705a347aa0d39040d198a36 \
  --certificate-identity https://github.com/ncecere/pi-durable-platform/.github/workflows/image.yml@refs/tags/v0.3.0 \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
cosign verify ghcr.io/ncecere/pi-durable-platform-runtime@sha256:c4e967f0ff40df422971303e72bddc3f2225876f55029716f4300ca3126da4a5 \
  --certificate-identity https://github.com/ncecere/pi-durable-platform/.github/workflows/image.yml@refs/tags/v0.3.0 \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Release assets

  • <image>-v0.3.0-<os>-<arch>.sbom.spdx.json: each image's SBOM (SPDX JSON) for each platform, as attached to the image.
  • <image>-v0.3.0.digest.txt: each image's reference by digest.
  • checksums.txt: SHA-256 of the files above (sha256sum -c checksums.txt).

The SBOM and build provenance are also attached to the images as attestations:
docker buildx imagetools inspect ghcr.io/ncecere/pi-durable-platform@sha256:01972d4f8cf6dfa2e79f2834f2643590d2aca43ba705a347aa0d39040d198a36 --format '{{ json .SBOM }}'.

Pi Durable Platform v0.3.0

"Apps at scale" (2026-10-20), on top of v0.2.0. Apps built on the platform can page and search long lists,
follow conversations with small event streams instead of whole views, steer a reply in progress, create agents safely
on retries, move agents between teams, and serve many people through one connection. It includes database migrations
00018 to 00020
: read Upgrading first.

What changed

For apps

  • Paging and title search (#8). GET /v1/agents/{id}/threads takes limit, an opaque cursor (the reply's
    nextCursor) and q, a case-insensitive title substring; newest activity first. Without limit the reply is exactly
    what it was. A conversation pages backwards with GET …/threads/{thread}?limit=&before= (the reply's
    earlierCursor).
  • Incremental event streams (#9). GET …/threads/{thread}/events?format=delta&v=1 sends a snapshot, then
    numbered changes: message.added and message.updated, partial text (partial.set, partial.appended,
    partial.cleared), tools (tool.started, tool.updated with elapsed time, tool.finished, tool.removed,
    tools.set) and the run's state (run). After a reconnect, Last-Event-ID resumes, or a new snapshot comes. Traffic
    follows the new tokens, not the conversation's length. Without format the stream is unchanged.
  • Steering (#10). whenBusy: "steer" on POST …/messages joins the reply in progress (after its current tool
    round); followUp, the default, queues behind it.
  • Safe agent creation (#11). An Idempotency-Key header on POST /v1/teams/{team}/agents: the same key and request
    within 24 hours return the first agent (Idempotent-Replayed: true), a different request with the key is 422
    idempotency_key_reused, and concurrent retries create one agent. PUT …/agents/by-key/{key} now also works for a
    person in their own personal workspace (with a session, a personal API key or an app they connected), so an app can
    keep one agent per purpose for each person; those agents are the person's, not service-managed. In shared teams it
    stays for service accounts.
  • OAuth Connect for apps with many people (#12).
    • prompt=none reconnects without a page, or returns login_required, consent_required or
      interaction_required. login_hint names the account the app expects: a different signed-in account is never
      connected silently.
    • A read-only scope, agents:read (reads only; anything else is 403 insufficient_scope).
    • App secrets (pdpcs_…): a platform admin issues one to a trusted app (Admin › Settings › Trusted apps; shown
      once, at most two). With it, the app follows everyone who connected it on one stream, GET /v1/app/events, and
      reads their usage, GET /v1/app/usage. People appear there only as the app's own subject for them (returned in
      the token response and in /v1/me), never by user ID, email or name.
  • Usage for several agents (#24). A repeatable agentId (up to 100) and groupBy=agent,day on usage reports.

In the console

  • The test chat pages and searches its threads, loads earlier messages, follows the conversation with the delta stream,
    offers Steer while a reply runs, and Enter queues a follow-up.
  • An agent's Settings has Move, with the dry run's summary before confirming (#13): what the target team must offer,
    how many shared threads become visible to whom, and what stops applying. Service-managed agents don't move.
  • Team › Usage groups by agent and day; Admin › Usage filters by connected app; Admin › Settings issues and deletes app
    secrets.

MCP

  • Tools an MCP server's app keeps for its own interface (_meta.ui.visibility without "model", the MCP Apps
    extension) are never offered to the model. Tool lists stored before this release are read again in the background when
    the platform starts. Showing apps' interfaces is roadmap item #51.

Fixed and hardened (the result of an independent review of this release; none was known to be exploited)

  • A v0.2 replica serving requests during a rolling upgrade could create keyed agents that weren't service-managed, so
    the team's people could read an app's users' conversations. The database now enforces that a keyed agent in a team
    that isn't a personal workspace is service-managed (migration 00020, which also fixes such rows).
  • A runtime start always uses the agent's current configuration: a request in flight during a move can't restart the
    agent with the old team's model key or MCP servers, and an agent turned off isn't started by a request authorized while
    it was on. Thread MCP servers set during a move don't survive it.
  • prompt=none no longer tells an app whether the browser is signed in as the login_hint account, unless the person
    connected the app or it is trusted.
  • The app-level event stream names people only by the app's subjects.
  • Removing an app from the trusted list deletes its app secrets (audited); trusting it again needs new ones.
  • q with NUL or invalid UTF-8 is 400 invalid_query instead of 500.

Upgrading from v0.2.0

  1. Back up the platform database.
  2. Replace both images (the platform and the runtime, by digest) and restart. The platform applies migrations 00018
    (agents' service_managed flag, idempotency keys, agent moves), 00019 (OAuth scopes, app secrets, per-app
    subjects, usage attributed to apps) and 00020 (the database enforces that keyed agents in shared teams are
    service-managed) when it starts.
    • Existing keyed agents (all made by service accounts in shared teams) are marked service-managed: nothing changes for
      them.
    • With several replicas, a rolling upgrade is safe: a v0.2 replica that creates a keyed agent after the migrations
      still gets a service-managed one (the database sets it).
  3. Agents upgrade as they wake. Conversation paging, delta streams and steering are served by the agent's runtime.
    An agent that was running when you upgraded keeps its old runtime until it hibernates; until then it ignores limit
    and before (the whole conversation comes back), format=delta (whole-view events) and whenBusy (a follow-up).
    Thread list paging and search work at once (the platform serves them).
  4. MCP tool lists are read again once, in the background, for every server registered before this release; a server
    that can't be reached keeps its list until the next start.
  5. Apps using OAuth Connect keep working unchanged. To use the new features: store the subject from the token
    response, use prompt=none with login_hint for silent reconnects, and ask a platform admin for an app secret to
    use the app-level stream and usage.
  6. No new settings.

Rolling back. Going back to v0.2.0's images works with the schema at 00020: v0.2 ignores the new columns and
tables (idempotency keys, app secrets, per-app subjects), treats every keyed agent as service-managed again (so a person's
keyed agents in their personal workspace become service-managed for them: they keep them but can't read them until you
upgrade again), and its by-key inserts get the trigger's protection.

Known limitations

Unchanged from v0.2.0, except these, which this release resolves: no paging, whole-view
events, messages always queue, by-key agents for service accounts only, no idempotency on agent creation, OAuth
Connect without login_hint and prompt=none, and no agent transfer. New or remaining:

  • Usage of a shared team agent used through two apps at once is attributed to neither app (usage counts for an app
    only when it's unambiguous which app's person the agent is answering).
  • During a move, the running-agents limit is checked against the old team for a start already under way.
  • Delta streams keep a copy of the view per open stream, and the number of open streams per person isn't limited.
  • Conversation paging needs the agent's runtime to be v0.3 (see Upgrading).
  • The roadmap plans the rest; v0.4 is about agents that run on their own.

Images and chart

Artifact What
ghcr.io/ncecere/pi-durable-platform The platform (distroless, UID 65532, read-only root filesystem)
ghcr.io/ncecere/pi-durable-platform-runtime The agent runtime (Node 22, UID 10001, read-only root filesystem with a writable /tmp)
oci://ghcr.io/ncecere/charts/pi-durable-platform The Helm chart, version 0.3.0

Both images are tagged v0.3.0 and latest, for linux/amd64 and linux/arm64. Deploy by digest. The digests are in the
GitHub Release, its <image>-v0.3.0.digest.txt assets and the workflow's job summary.

ghcr.io/ncecere/pi-durable-platform@sha256:01972d4f8cf6dfa2e79f2834f2643590d2aca43ba705a347aa0d39040d198a36
ghcr.io/ncecere/pi-durable-platform-runtime@sha256:c4e967f0ff40df422971303e72bddc3f2225876f55029716f4300ca3126da4a5
ghcr.io/ncecere/charts/pi-durable-platform:0.3.0 (sha256:7a0848ea1f57cbb41aa0d27d32f6725b6173af218487b947cdf2663965fc42c1)

Verifying the images and the chart

Verify the signatures with cosign v3.0 or newer
(keyless, GitHub Actions OIDC):

cosign verify ghcr.io/ncecere/pi-durable-platform@sha256:<digest> \
  --certificate-identity https://github.com/ncecere/pi-durable-platform/.github/workflows/image.yml@refs/tags/v0.3.0 \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
cosign verify ghcr.io/ncecere/pi-durable-platform-runtime@sha256:<digest> \
  --certificate-identity https://github.com/ncecere/pi-durable-platform/.github/workflows/image.yml@refs/tags/v0.3.0 \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
cosign verify ghcr.io/ncecere/charts/pi-durable-platform:0.3.0 \
  --certificate-identity https://github.com/ncecere/pi-durable-platform/.github/workflows/chart.yml@refs/tags/v0.3.0 \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

The SBOM (SPDX) and build provenance are attached to each image as attestations, and the Release carries each image's
SBOM per platform with checksums.txt. pi-durable-platform version prints pi-durable-platform 0.3.0 (<commit>); the
runtime image is labelled org.opencontainers.image.version=0.3.0.

Reporting problems

Open an issue at https://github.com/ncecere/pi-durable-platform/issues. Report security problems privately as
SECURITY.md describes.