Skip to content

Releases: ncecere/pi-durable-platform

Pi Durable Platform v0.3.0

Choose a tag to compare

@github-actions github-actions released this 20 Oct 04:28
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]...
Read more

Pi Durable Platform v0.2.0

Choose a tag to compare

@github-actions github-actions released this 20 Oct 01:02
801a054

Images

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

ghcr.io/ncecere/pi-durable-platform:v0.2.0
ghcr.io/ncecere/pi-durable-platform@sha256:3f988afa89eadec2b4f38b132cc34016300e10d11e7c08c0258a6546b195222d

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

ghcr.io/ncecere/pi-durable-platform-runtime:v0.2.0
ghcr.io/ncecere/pi-durable-platform-runtime@sha256:11a098887c13377c985dae169d7dbbaedc854717b1d277e3cdf5aec45602ce8e

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

cosign verify ghcr.io/ncecere/pi-durable-platform@sha256:3f988afa89eadec2b4f38b132cc34016300e10d11e7c08c0258a6546b195222d \
  --certificate-identity https://github.com/ncecere/pi-durable-platform/.github/workflows/image.yml@refs/tags/v0.2.0 \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
cosign verify ghcr.io/ncecere/pi-durable-platform-runtime@sha256:11a098887c13377c985dae169d7dbbaedc854717b1d277e3cdf5aec45602ce8e \
  --certificate-identity https://github.com/ncecere/pi-durable-platform/.github/workflows/image.yml@refs/tags/v0.2.0 \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Release assets

  • <image>-v0.2.0-<os>-<arch>.sbom.spdx.json: each image's SBOM (SPDX JSON) for each platform, as attached to the image.
  • <image>-v0.2.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:3f988afa89eadec2b4f38b132cc34016300e10d11e7c08c0258a6546b195222d --format '{{ json .SBOM }}'.

Pi Durable Platform v0.2.0

"Run it anywhere, safely" (2026-10-19), on top of v0.1.2. The platform now ships deployment manifests, runs
as several replicas, exports metrics and traces, can re-encrypt its secrets, and closes the safety gaps the v0.1.0 notes
listed, plus the findings of an independent review of this release. It includes database migration 00017: read
Upgrading first.

What changed

Deploying

  • A Helm chart and a Kustomize base (deploy/) with RBAC, NetworkPolicies (and a Cilium flavour), Pod Security
    restricted, the Kubernetes and Agent Sandbox launchers, an Ingress, a PodDisruptionBudget and a ResourceQuota. The
    chart is the source of truth: the Kustomize base is generated from it, and CI checks that the two render the same
    objects, with Helm 3 and Helm 4. The chart is published, signed with cosign, to
    oci://ghcr.io/ncecere/charts/pi-durable-platform on release tags. CI installs it into a kind cluster with two
    replicas and checks the isolation (an agent reaches only its database, the gateway and DNS), using the images built for
    the commit under test (#1).
  • Several platform replicas (#2). Set replicaCount: 2 (the Kustomize component components/replicas; the chart
    adds rolling updates that leave none missing, spreading over nodes and zones and, if you turn it on, a
    PodDisruptionBudget). The runtime reports its open streams and its work, the reaper believes that report, only one
    replica reaps at a time and starting an agent is serialised across replicas, so no replica can hibernate an agent whose
    stream is open on another. Sessions, sign-in and OAuth state, rate limits and thread events were already shared through
    PostgreSQL. The design and the inventory of what is per replica are in docs/replicas.md.
  • Metrics and tracing (#3). /metrics on its own listener (METRICS_ADDR; the chart enables it on port 9090 and
    allows nobody to reach it until you say who may), with requests by route pattern, agent wakes (cold, warm pool or
    already running), hibernations, model and MCP proxies, OAuth, sign-ins, rate limits, background loops, group re-checks
    and database pools. Labels are values the platform defines; no id, email, team name or URL is ever a label. Optional
    traces over OTLP/HTTP (OTEL_EXPORTER_OTLP_ENDPOINT). A Grafana dashboard and example alerts are in deploy/grafana/,
    and the chart can create a ServiceMonitor, the NetworkPolicy for your scraper and the dashboard ConfigMap.

Operating

  • pi-durable-platform rekey (#4) re-seals every stored secret (agent credentials, model providers, MCP servers
    and the stored identity provider tokens) under the current ENCRYPTION_KEY; --dry-run shows what it would do and
    doctor counts the values still under the previous key. Once it reports none, ENCRYPTION_KEY_PREVIOUS can go. A
    test fails for any sealed column it doesn't cover.
  • A configuration reference (#23). .env.example lists every setting, and a test keeps it complete.
  • The agents database is harder to interfere with (#7). An agent's ownership is a row in its own schema, taken over
    only when the owning database session is gone, so another agent's role can't block it. New commands prune-roles and
    prune-agent-storage. doctor reports agent roles that belong to no agent, and warns when PUBLIC may connect to the
    platform database.
  • Vulnerability checks in CI: govulncheck and audits of the web app's and the runtime's production dependencies,
    weekly and whenever a dependency file changes.

For apps and people

  • Platform-chosen thread IDs (#5). POST /v1/agents/{id}/threads returns a new random thread ID. MCP servers are set
    only on a thread that exists or that the same PUT creates (the caller becomes its creator; send visibility on the
    PUT for a private thread). Use the POST and your threads can't be pre-seeded.
  • IdP group re-checks (#6). With OIDC_ALLOWED_GROUPS, the platform asks the identity provider every 15
    minutes (OIDC_GROUP_RECHECK_INTERVAL) whether each recently active person is still in an allowed group. When not,
    their sessions, API keys and connected apps stop working, open event streams close, and Admin › People shows "Group
    access ended"; signing in again restores access. If the provider can't answer, access lasts a grace period
    (OIDC_GROUP_RECHECK_GRACE, 24 hours). It needs the refresh token of the person's sign-in, so the offline_access
    scope. A person without a stored token is never re-checked: this is logged, counted and shown by doctor.

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

  • The reaper reads a runtime's /status through a 64 KiB limit; an agent could otherwise send a reply large enough to
    exhaust the platform's memory.
  • Agents whose database role created large objects or stray tables can be deleted; prune-roles removes such leftovers.
  • An agent's role can no longer block platform start-up by holding the agents database's prepare lock.
  • A client's traceparent no longer forces a request to be traced.
  • Open event streams re-check access every 30 seconds, so a person who is suspended, denied or removed from a team stops
    receiving events.
  • MCP servers set on a reserved thread ID don't reach another person's first message.
  • A stored identity provider token that can't be opened ends access after the grace period instead of exempting the
    person; a rotated refresh token survives a restart in the middle of a check; people in more than 200 groups are matched
    on the full list.
  • A status report from an agent's runtime that arrived after a thread was deleted brought the thread back, as a shared
    record carrying the deleted thread's title, even for a private thread (in v0.1.x too). A single report now only
    updates a thread the platform has; the runtime's list at start-up still adopts threads the platform doesn't know.
  • A reserved thread (no message yet) can be deleted; the runtime has no conversation for it, so the delete returned 404.
  • golang.org/x/net 0.60.0 fixes four HTTP/2 advisories (GO-2026-6603, -6611, -6612, -6617) that govulncheck reports
    as reachable. v0.1.2 and earlier are affected.

Upgrading from v0.1.2

  1. Back up the platform database (the migration is forward-only in practice: see rolling back).

  2. Replace both images (the platform and the runtime, by digest) and restart. The platform applies migration 00017
    when it starts.

    • It deletes MCP servers that were set on a thread nobody had started (earlier releases accepted them, and a later
      private thread with that ID would have inherited them) and makes a thread's servers belong to its record from then on.
    • It adds the tables for group re-checks. Nobody is re-checked until they sign in again, so nobody is locked out by the
      upgrade.
    • With several replicas, a rolling upgrade runs both versions for a while: until every replica is upgraded, a request
      to set MCP servers on an unrecorded thread can fail against the new reference. Apps that use
      POST /v1/agents/{id}/threads are unaffected.
  3. Agents upgrade as they wake. An agent that was already running keeps its old runtime until it hibernates; until
    then it doesn't report openStreams, so the reaper judges it by the streams of the replica it is on. An agent database
    from v0.1.x opens with the new runtime without any step.

  4. Keep agents' roles out of the platform database. Run pi-durable-platform doctor. If it warns that PUBLIC may
    connect, run on the server (as the platform database's owner):

    REVOKE CONNECT ON DATABASE <platform database> FROM PUBLIC;
    GRANT CONNECT ON DATABASE <platform database> TO <the platform's role>;

    and the same REVOKE for the other databases on that server that agents must not reach, or limit agent roles to the
    agents database in pg_hba.conf (a rule is in [`deploy/examples/cloudnative-pg/...

Read more

Pi Durable Platform v0.1.2

Choose a tag to compare

@github-actions github-actions released this 19 Oct 21:55

Images

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

ghcr.io/ncecere/pi-durable-platform:v0.1.2
ghcr.io/ncecere/pi-durable-platform@sha256:c20916ab720cc5353e17d8ad5d7c12a50fd637b9cbedc1e453f1a99ca6db6deb

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

ghcr.io/ncecere/pi-durable-platform-runtime:v0.1.2
ghcr.io/ncecere/pi-durable-platform-runtime@sha256:7b61a2af7b1c4aafec78ec0c5af4e9ae8e6978c1bfd375d9fea9f0b52e32c983

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

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

Release assets

  • <image>-v0.1.2-<os>-<arch>.sbom.spdx.json: each image's SBOM (SPDX JSON) for each platform, as attached to the image.
  • <image>-v0.1.2.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:c20916ab720cc5353e17d8ad5d7c12a50fd637b9cbedc1e453f1a99ca6db6deb --format '{{ json .SBOM }}'.

Pi Durable Platform v0.1.2

A fix release (2026-10-19) on top of v0.1.1: two console fixes. No database migrations (still 00016), no
configuration changes.

What changed

Fixed

  • A connecting app's logo shows. An app that connects with OAuth Connect can publish a logo. The approval screen,
    Account › Connected apps and a person's admin page showed a broken image, because the console's
    Content-Security-Policy allows images only from the platform itself. The platform now fetches the logo (public
    addresses only, https only, no redirects, at most 256 KiB, PNG, JPEG, GIF or WebP verified by their bytes, never SVG),
    caches it for an hour and serves it from its own origin at GET /v1/oauth/client-logo?client_id=… (signed-in people
    only, rate limited). The request never carries a URL, so it can't be used to fetch arbitrary addresses (#25).
  • The agent page's status badge follows the agent. After a message woke a hibernated agent from another session or an
    app, the badge kept saying "Hibernating" until the next poll or a reload. The page now follows the agent's events
    (#27).

Upgrading from v0.1.1

Replace both images (the platform and the runtime, by digest) and restart. There are no migrations and no new settings.

Known limitations

Unchanged from v0.1.0. The roadmap plans each one, and each has a GitHub
issue with the roadmap label.

Images

Image 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)

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

Verifying the images

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.1.2 \
  --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.1.2 \
  --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.1.2 (<commit>);
the runtime image is labelled org.opencontainers.image.version=0.1.2.

Reporting problems

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

Pi Durable Platform v0.1.1

Choose a tag to compare

@github-actions github-actions released this 18 Oct 23:20

Images

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

ghcr.io/ncecere/pi-durable-platform:v0.1.1
ghcr.io/ncecere/pi-durable-platform@sha256:ef046ad6534aa056adf0f10b1a9e41d5e93793e17e911b8cefab750dd909b147

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

ghcr.io/ncecere/pi-durable-platform-runtime:v0.1.1
ghcr.io/ncecere/pi-durable-platform-runtime@sha256:a7adb0c5f7913ff2f2ffd7783301f81d23caeeceb8d68abd48838e178d9d1168

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

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

Release assets

  • <image>-v0.1.1-<os>-<arch>.sbom.spdx.json: each image's SBOM (SPDX JSON) for each platform, as attached to the image.
  • <image>-v0.1.1.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:ef046ad6534aa056adf0f10b1a9e41d5e93793e17e911b8cefab750dd909b147 --format '{{ json .SBOM }}'.

Pi Durable Platform v0.1.1

A fix release (2026-10-18) on top of v0.1.0: small console fixes found while preparing the documentation
and screenshots, doctor checking the agents database, a read-only console path for platform staff to any team's
agents, and the product's logo. No database migrations (still 00016), no configuration changes.

What changed

Added

  • doctor checks the agents database. It connects to AGENTS_DATABASE_URL and checks that its role can provision
    agents (CREATEROLE, and CREATE on the database, on PostgreSQL 16 or later). Skipped without an agents database;
    reported as info when AGENTS_CREATE_DATABASE will create it.
  • Platform staff read any team's agents. Platform admins and auditors see every team's agents read-only
    (GET /v1/teams/{team}/agents, GET /v1/agents/{id}), not only personal workspaces'. Admin › Teams › a team has an
    Agents tab linking to each agent's page: Overview and Settings, Conversations for platform admins (reading one still
    takes a reason), and no Test.
  • The product's logo, "Wake". The sidebar, the sign-in page and the session-unavailable page show Pi Durable
    Platform's mark when the instance has no logo of its own (UI_LOGO_URL still replaces it); the favicon is the mark,
    with a favicon.ico and an apple-touch-icon.png.

Changed

  • Staff can't hibernate other teams' agents. In v0.1.0 platform staff could hibernate a personal workspace's agent
    (POST /v1/agents/{id}/hibernate); now only the agent's team can, and staff get 403. Staff access to agents is
    read-only everywhere.

Fixed

  • Sign-in: the "Help signing in" link uses the app's link colour.
  • Home: the Agents card no longer shows an empty strip (it links to All agents and the Catalog).
  • Admin › Models and MCP servers: the Owner filter's chevron no longer overlaps its text (every small select).
  • Admin › Templates: the review queue and the full list each have a heading, and the queue's table a name.
  • Admin › Settings › Private networks: the example placeholder shows only while no network is listed.
  • Audit log: every action the platform records has a label, with areas in the filter and target type names.
  • Platform admins' conversation list says "Title hidden" for a withheld title instead of "Private conversation";
    Visibility says who sees the thread.
  • Agent Test chat: the header's Shared icon stays on the line with its word.

Upgrading from v0.1.0

Replace both images (the platform and the runtime, by digest) and restart. There are no migrations and no new
settings. Run pi-durable-platform doctor afterwards: it now also checks the agents database.

Known limitations

Unchanged from v0.1.0. The roadmap plans each one, and each has a GitHub
issue with the roadmap label.

Images

Image 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)

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

Verifying the images

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.1.1 \
  --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.1.1 \
  --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.1.1 (<commit>);
the runtime image is labelled org.opencontainers.image.version=0.1.1.

Reporting problems

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

Pi Durable Platform v0.1.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 21:32

Images

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

ghcr.io/ncecere/pi-durable-platform:v0.1.0
ghcr.io/ncecere/pi-durable-platform@sha256:15d1335db6c44d4499cef90097675320e8349a78bfc8b21b3d33e7a0588ef059

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

ghcr.io/ncecere/pi-durable-platform-runtime:v0.1.0
ghcr.io/ncecere/pi-durable-platform-runtime@sha256:2621a07f708fcdab089bba4384da08932d278e350fc55a985d178a498df55b35

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

cosign verify ghcr.io/ncecere/pi-durable-platform@sha256:15d1335db6c44d4499cef90097675320e8349a78bfc8b21b3d33e7a0588ef059 \
  --certificate-identity https://github.com/ncecere/pi-durable-platform/.github/workflows/image.yml@refs/tags/v0.1.0 \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
cosign verify ghcr.io/ncecere/pi-durable-platform-runtime@sha256:2621a07f708fcdab089bba4384da08932d278e350fc55a985d178a498df55b35 \
  --certificate-identity https://github.com/ncecere/pi-durable-platform/.github/workflows/image.yml@refs/tags/v0.1.0 \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Release assets

  • <image>-v0.1.0-<os>-<arch>.sbom.spdx.json: each image's SBOM (SPDX JSON) for each platform, as attached to the image.
  • <image>-v0.1.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:15d1335db6c44d4499cef90097675320e8349a78bfc8b21b3d33e7a0588ef059 --format '{{ json .SBOM }}'.

Pi Durable Platform v0.1.0

This is Pi Durable Platform's first release (2026-10-18). Pi Durable Platform is a self-hosted console and API where teams deploy, test and manage agents built with Pi and its durable package. Each agent runs in its own runtime, keeps its state in its own PostgreSQL schema, and hibernates when idle. People don't chat with agents in the console: a team's own apps (a chat app, a support tool) reach its agents through the platform API. The full list of changes is in the changelog, and the design in DESIGN.md.

Pi Durable Platform is an independent project. It is not affiliated with or endorsed by Earendil.

v0.1.0 ships two images: the platform (one Go binary with the React web UI embedded, backed by PostgreSQL 17) and the agent runtime (Node, one agent per container). Migrations 00001 to 00016.

What's in it

  • Teams and roles. Platform roles platform_admin and platform_auditor; team roles owner, admin, editor and member. Platform admins create teams and assign owners; owners and admins manage members and invites. The authorization rules are pure functions (internal/authz), and a test classifies every operation in api/openapi.yaml for every kind of caller.
  • Personal workspaces. While platform admins have them turned on, each person who signs in gets a team of kind personal with themselves as its only member. Personal workspaces deploy catalog templates only. Platform admins set defaults (max agents, max running, a monthly budget) and override them per workspace, and choose whether personal workspaces may add their own model providers and MCP servers (off by default). Platform admins and auditors see a workspace, its agents, usage and audit log, but not its conversations.
  • Templates and the reviewed catalog. Templates are versioned; each agent is pinned to a version and can move to another (upgrade or roll back), applied at its next start. Platform admins curate platform templates. Teams write their own, and submit them to the catalog; a platform admin approves or rejects the request with a note.
  • Agents. Team editors deploy agents from templates and test them in the console. Each agent gets its own schema and database role in a separate agents database (AGENTS_DATABASE_URL), and a session advisory lock on its only connection makes sure only one runtime writes for it. Service accounts create service-managed agents by external key (PUT /v1/teams/{team}/agents/by-key/{key}), for example one per user of an app. Only the team's service accounts use those; owners and admins see and delete them but don't read them. prune-agent-storage removes storage no agent owns.
  • Launchers (AGENTS_LAUNCHER). local: child processes of the platform, for development. kubernetes: one pod per agent (no service-account token, non-root, read-only root filesystem, all capabilities dropped, optional AGENTS_RUNTIME_CLASS such as gVisor), with its secrets in a per-agent Secret. agent-sandbox: a Kubernetes Agent Sandbox per agent, suspended when idle; or, with AGENTS_SANDBOX_WARM_POOL, a standby runtime claimed from a SandboxWarmPool and activated as the agent (POST /activate with the pool's activation token), so a wake skips scheduling, image pull and Node start-up.
  • Hibernation and wake. Agents with no work in progress, no open event streams and no recent requests hibernate after AGENTS_IDLE_TIMEOUT (default 10 minutes): no pod, no database connection. The next request wakes them, and work in flight resumes from storage. Team members can hibernate an agent by hand (POST /v1/agents/{id}/hibernate).
  • Model catalog. Platform admins add providers: OpenAI (Responses API), Anthropic (Messages API), and servers compatible with OpenAI chat completions (LiteLLM, vLLM, Ollama, OpenRouter and others), the OpenAI Responses API or the Anthropic Messages API. "Discover models" lists what a provider offers. Models are published to every team, to chosen teams, and optionally to personal workspaces. Each provider is either proxy (default: agents call the platform's model gateway with their own credential, and the gateway checks the model, adds the key and records usage) or direct (agents get the URL and key). Team owners and admins add the team's own providers and models (proxy only, through the address guard, for that team alone). Models carry input and output prices. Usage reports (GET /v1/teams/{team}/usage, GET /v1/admin/usage) sum requests and tokens per day, model, agent or team, with a cost estimate from current prices. The runtime reports usage for models the gateway doesn't see (direct providers, the default model).
  • MCP catalog. Platform admins add remote MCP servers (Streamable HTTP) with sealed headers and publish them. The platform reads and keeps each server's tool list. A template version gives its agents servers and all or some of their tools; tools are named mcp__<slug>__<tool>, and more than 20 are deferred behind tool_search. Calls go through the gateway's MCP proxy by default, which adds the headers, refuses tools the version left out and logs each call. Teams add their own servers (proxy only, through the address guard). Apps may give an agent their own MCP servers for one thread (mcpServers with a message, or PUT .../threads/{thread}/mcp-servers), for hosts in AGENTS_CLIENT_MCP_HOSTS (none by default), with headers sealed until they expire.
  • Threads. The platform keeps a record of each thread (agent_threads), so listing doesn't wake the agent: title, created and updated times, busy, last reply and last error, creator and participants. GET /v1/agents/events streams thread changes for every agent the caller may use; GET .../threads/{thread}/events streams one conversation's view. A thread is shared or private (chosen by its first message; private threads are visible only to the person or service account that started it, and read as not found to everyone else). The platform sets each message's author, and on shared threads of shared teams the model is told who is speaking. Messages carry their time, the model, token usage, stop reason and tool timings. Platform admins can list any agent's threads and read one conversation with a stated reason (10 to 500 characters), recorded in the team's audit log before the conversation is returned.
  • Service accounts and personal API keys. Team service accounts with a team role and expiring keys (pdp_sa_…, 1 to 365 days, two active at a time for rotation). Personal API keys (pdp_…) act as their person.
  • OAuth Connect. The platform is a small OAuth 2.1 authorization server, so an app can act as the person who connects it: discovery (RFC 8414 and RFC 9728), PKCE (S256, required), Client ID Metadata Documents or Dynamic Client Registration, a consent page, 1-hour access tokens and rotating 30-day refresh tokens with reuse detection, revocation, and trusted apps that skip consent. App tokens act as the person without their platform role, on the agent routes and a few read routes.
  • Audit log. Append-only, written in the same transaction as each change. Team audit logs for editors and above, and a platform log for admins and auditors.
  • Sign-in. OIDC (any provider), optionally limited to email domains or IdP groups (OIDC_ALLOWED_GROUPS), with a bootstrap admin subject. Development personas (DEV_AUTH=true: admin, auditor, user, alex, blair and casey), refused unless APP_URL is a loopback address.
  • Operations. pi-durable-platform doctor checks the configuration, the production safety rules, PostgreSQL, the API key pepper and the migration state. migrate applies migrations under an advisory lock (MIGRATE_ON_START does it at start-up). /healthz and /readyz. Rate limits are counted in PostgreSQL.

Security model

  • One enforcement point. Clients reach agents only through the platform API, which authenticates, ...
Read more