Central PostgreSQL
French version: ../postgresql.md.
Connection
EAI_DATABASE_URL:postgresql://user@host:5432/eai?sslmode=verify-full&sslrootcert=/etc/eai/pg-ca.pem. In prod the startup refuses a URL withoutsslmode=require,verify-caorverify-full.psycopg_poolpool (EAI_DB_POOL_MIN,EAI_DB_POOL_MAX), every connection is checked before use (replaced after a restart or failover),application_name=eai-control-planeinpg_stat_activity.- Isolation: every tenant transaction switches to the
eai_approle (forced RLS,FORCE ROW LEVEL SECURITY). - Separate runtime login (ADR-0156):
EAI_RUNTIME_DATABASE_URLis the login of the API and the worker, member of theeai_runtimerole (V165: member ofeai_app, no CREATE right, owner of nothing), created by the operator:CREATE ROLE eai_api LOGIN PASSWORD '...' IN ROLE eai_runtime;. The migrations keepEAI_DATABASE_URL(administrator login, owner of the schema). Empty: the API usesEAI_DATABASE_URL. In prod the same TLS requirement applies to both URLs.
Migrations
python -m eai.migrateappliessql/postgres/V###__name.sqlunder an advisory lock;--statuslistsappliedorpending. An applied migration that was modified is refused (checksum).
High availability
- CloudNativePG (
deploy/data/postgres/cluster.yaml): 3 instances spread across zones, automatic failover, encrypted WAL archive to S3 and a daily base backup; PITR through a newClusterinbootstrap.recovery.
Logical backup and restore (verified commands)
The connection only goes through the libpq environment (PGHOST, PGPORT, PGUSER, PGDATABASE, PGSSLMODE, PGPASSFILE with mode 0600): never a password or URL in arguments, the scripts refuse it.
cd services/control-plane
export PGHOST=<pg-host> PGUSER=eai_backup PGSSLMODE=verify-full PGPASSFILE=/run/secrets/pgpass
PGDATABASE=eai scripts/pg_backup.sh /backup/eai-$(date +%F).dump # pg_dump --format=custom, file 0600, checked by pg_restore --list
createdb eai_drill
PGDATABASE=eai_drill EAI_PYTHON=.venv/bin/python \
EAI_DATABASE_URL='postgresql://eai_backup@<pg-host>/eai_drill?sslmode=verify-full' \
scripts/pg_restore.sh /backup/eai-2026-10-30.dump # single transaction, fails on a pending migration
Roles (eai_app, owner) are cluster wide and must exist on the target. tests/test_core_pg_backup.py runs both scripts against a real PostgreSQL 16: tenant and data found, no pending migration, same forced RLS tables and same policies, eai_app sees nothing without a tenant scope.