Skip to content

Separate PostgresQL runtime and schema ownership - #1752

Draft
Jeremy Alvis (iplay88keys) wants to merge 10 commits into
agent-substrate:mainfrom
iplay88keys:iplay88keys/postgres-runtime-ddl-roles
Draft

Jeremy Alvis (iplay88keys) wants to merge 10 commits into
agent-substrate:mainfrom
iplay88keys:iplay88keys/postgres-runtime-ddl-roles

Conversation

@iplay88keys

@iplay88keys Jeremy Alvis (iplay88keys) commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Fixes #997

Summary

  • Give ateapi separate PostgreSQL owner and read/write connections. Migrations and owner maintenance use the owner pool; application reads and writes use the read/write pool. Each connection assumes its configured group role, so an operator can replace a login without changing object ownership.
  • Add optional first-install identity bootstrap using an administrator credential. Bootstrap creates the fixed development logins, group roles, schema, memberships, and default privileges from embedded SQL. The binary owns the fixed development usernames and passwords, checks the application connection strings against them, and does not reset existing passwords. Direct ateapi runs default to bootstrap disabled.
  • Accept @file:/absolute/path for both owner and read/write connection strings. Substrate rereads each projected Secret file when opening a new physical connection, so rotated credentials can take effect without restarting the process. The --postgres-max-conn-lifetime CLI flag sets the maximum connection lifetime, bounding how long old connections remain in use; endpoint or database changes still require a restart.
  • Wire the bundled PostgreSQL credentials and the two application connections through ate-setup and the installation manifests. The shell installer now delegates to ate-setup. Its previous single-connection environment variable still supplies both pools when used alone and remains the owner connection when a separate read/write DSN is added. Bundled PostgreSQL requires password authentication plus its existing client certificate check. Standalone Substrate keeps public as its default schema.
  • Update the Cloud SQL guide to provision the owner and read/write roles, IAM login membership, and default object grants before deployment; point the setup command at that guide.
  • Keep the identity SQL beside the migration sources so operators can run it directly with their own transaction-local login, schema, and role-name settings when provisioning users outside the application. The optional group role settings default to the bundled names.

Scope

This identity layout requires a fresh PostgreSQL database. The PR does not migrate existing users, grants, or tables into it.

The bundled-database installers enable bootstrap to provision Substrate's fixed identities. ateapi also accepts explicit administrator and application connections for a managed shared database; it has no way to infer which deployment owns PostgreSQL. Deployments using operator-managed users disable bootstrap and provide precreated roles, memberships, schema, and grants.

Cloud SQL intentionally uses one IAM database login by default. The owner connection defaults to the read/write connection, and each pool assumes its configured role. Separate logins remain supported through separate connection strings, but requiring two IAM logins would add proxy identity setup. Two logins limit exposure if only the read/write credential leaks; because ateapi holds both pools, they do not isolate a compromised ateapi pod. Stronger isolation would require moving owner operations out of that pod.

The bootstrap usernames and passwords are fixed, published development credentials. Do not enable fixed-identity bootstrap against a production database. For production, provision unique logins and permissions outside ateapi, supply their connection strings, and leave bootstrap disabled.

@iplay88keys
Jeremy Alvis (iplay88keys) marked this pull request as draft September 22, 2026 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Define least-privilege role design for Postgres

1 participant