Skip to content
PaloNexus
Request access Request

Persistence

By default the control-plane registry and the agent-idp store (agents, delegations, revocations / StatusList) are in-process maps — a pod restart wipes every registration, delegation, and revocation. The in-memory default is also per-replica, so any multi-replica/HA deployment needs a shared backend. The opt-in persistence layer makes them durable behind the same storage interface, selected entirely by environment variables. This durability is what makes it provable which agent acted on whose still-valid authority across restarts — the identity and credential design it backs lives in Agent identity & credentials.

Canonical design: docs/persistence-and-identity.md in the platform repo.

Status: shipped and verified live on a managed Kubernetes cluster (DigitalOcean Kubernetes, DOKS, example) with REGISTRY_BACKEND=postgres / IDP_STORE_BACKEND=postgres (Postgres via CloudNativePG). Registry and agent-idp store survive pod restarts.

Both components keep their storage interface and gain a backend factory chosen by env. Records are stored as a JSON document keyed by primary key, so one SQL implementation serves Postgres / MySQL / SQLite (dialect-aware placeholders + upsert) and a separate document implementation serves MongoDB. Memory stays the zero-config default.

Control-plane (Go)agent-idp (Python)
Interfaceregistry.Store (Upsert/Get/List)app.store.Store (agents/delegations/revocations)
Backendsmemory · postgres · mysql · sqlite · mongodbsame set
Select viaREGISTRY_BACKEND + REGISTRY_DB_URLIDP_STORE_BACKEND + IDP_DB_URL
SQL driversmodernc.org/sqlite (pure-Go — keeps the static distroless build), lib/pq, go-sql-driver/mysqlstdlib sqlite3, psycopg (pg), PyMySQL
Mongo drivergo.mongodb.org/mongo-driverpymongo
Tested in CISQL path against SQLite (no server needed)SQL path against SQLite

The control-plane also accepts REGISTRY_DB_TABLE and REGISTRY_DB_DATABASE to override the table/collection and database names.

Terminal window
# Control-plane registry
REGISTRY_BACKEND=postgres REGISTRY_DB_URL=postgres://palonexus:pw@pg-rw.palonexus.svc:5432/palonexus?sslmode=disable
REGISTRY_BACKEND=sqlite REGISTRY_DB_URL=/var/lib/palonexus/registry.db
REGISTRY_BACKEND=mysql REGISTRY_DB_URL='palonexus:pw@tcp(mysql.palonexus.svc:3306)/palonexus'
REGISTRY_BACKEND=mongodb REGISTRY_DB_URL=mongodb://mongo.palonexus.svc:27017/palonexus
# agent-idp store
IDP_STORE_BACKEND=postgres IDP_DB_URL=postgresql://palonexus:pw@pg-rw.agent-idp.svc:5432/agentidp

SQLite (a PVC-backed file) is the zero-operator single-node option. Mongo uses the document implementation; the rest share the SQL implementation.

Production Postgres is provisioned by CloudNativePG (CNPG). The deploy/kustomize/components/postgres/ component creates a Cluster custom resource (CR) per component (palonexus, agent-idp), each giving a managed primary+replica with a *-rw Service the apps point their *_DB_URL at, and patches the data source name (DSN) in from the generated *-app secret.

Enable it from the selfhost overlay (install the CNPG operator first):

deploy/kustomize/overlays/selfhost/kustomization.yaml
components:
- ../../components/postgres
Terminal window
# Install the CloudNativePG operator before applying the component
# https://cloudnative-pg.io

The component does not add a NetworkPolicy for Postgres today. When later locking down control-plane / agent-idp egress, allow TCP 5432 to pods labelled cnpg.io/cluster=<palonexus-pg|agentidp-pg>.

If a durable backend is misconfigured or unreachable at startup, the process exits rather than silently falling back to memory — a typo in a DSN must not quietly drop the deployment to a store that loses every registration. This mirrors the control plane’s deny-by-default posture: see the control-plane invariants.

Once durable, registrations, delegations, and revocations survive restarts — which is what makes the live-revocation flow (vc mode) reliable rather than dependent on an in-memory set. See Credential-safe action enforcement (ops) for AGENT_IDENTITY_MODE=vc.