Six service connections run as the postgres SUPERUSER across two databases on the
shared 100-connection server — the root of the "too many connections" peaks and a
standing least-privilege risk. Superuser sessions ignore per-role CONNECTION LIMIT
and can consume the superuser-reserved slots.
Drafts (apply as postgres; nothing applied here):
- scripts/app_roles_tracksolid_db.sql — webhook_app, ingest_app, worker_app,
dashboard_app. Capability groups (ts_app_read / ts_app_write), per-app NOSUPERUSER
login roles with hard CONNECTION LIMIT + bounded GUCs (statement_timeout,
idle_session_timeout, idle_in_transaction, lock_timeout).
- scripts/app_roles_fleet_platform.sql — gateway_app, cron_app (the apps on the
separate fleet_platform DB), fp_app_rw group over its schemas.
- scripts/MIGRATE_APPS_OFF_SUPERUSER.md — runbook: discovery (what each app actually
writes / whether it runs DDL), connection-budget table (sum ≈ 81 < 100), the
object-ownership step for migration-running apps (reassign app schemas to the
existing tracksolid_owner — scoped, never REASSIGN OWNED globally), one-at-a-time
cutover, and instant rollback (DATABASE_URL only).
Grants are best-effort by app function and explicitly call out where to verify before
cutover; all objects are postgres-owned, so row DML works but DDL needs the ownership
step. See the runbook.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>