Share PostgreSQL with Substrate - #2708
iplay88keys wants to merge 13 commits into
Conversation
Configure embedded Substrate to use Kagent's selected PostgreSQL database through a shared Secret and a separate schema. Support bundled, external, shared existing, and independently configured Substrate databases, with examples for each installation mode. Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io>
Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io>
…tgres-with-substrate Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io>
|
Three gaps after this PR. --postgres-schema defaults to public, and atepg.go assigns RuntimeParams["search_path"] unconditionally after parsing the DSN. In the shared-Secret setup this PR documents, a connection string carrying search_path=agents is therefore overridden to public on the ate-api-server side, silently, unless the flag is set to match. Migrations cannot use a separate identity. RunUp runs in-process on the same connection string before the pool opens, so the role serving traffic must also hold DDL rights on its own schema, and every migrated object ends up owned by it. A schema owner distinct from its runtime consumer is not expressible. Rotation has no input left. POSTGRES_DATABASE_URL is an env var, frozen for the life of the process. urlFile was the only input a rotation system could write to, and passfile is no substitute because ParseConfig resolves that once too. Is rotation meant to work another way? |
|
@smeegoan, thanks for taking a look! Just updating this PR and reviewing your comment. Just a heads up that Codex helped put together this response, though I reviewed it.
This PR defaults
Kagent already supports this operationally. An operator can create the schema/grants and run On the Substrate side, even if we added a way to pre-apply and skip startup migrations, that would not provide complete DDL/DML separation. Operators can configure Substrate with a separate connection Secret and role, even when it points to the same PostgreSQL server and database. This keeps Substrate’s DDL privileges off Kagent’s runtime role, but Substrate currently has no supported mechanism for separating its own migration/DDL and runtime/DML identities. Additional Substrate work would be required for that stricter separation.
So the PR does not remove an existing Kagent credential-separation or live-rotation capability. The real follow-up is adding equivalent migration-role separation to Substrate if that is required for shared production databases. |
…tgres-with-substrate Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io>
…t's role Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io>
Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io>
Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io>
…tgres-with-substrate Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io>
|
@smeegoan, I see now how As such, pgx's So the updated design preserves a live Kubernetes Secret input and supports credential rotation without a pod restart, while replacing the arbitrary file-path |
|
Thank you @iplay88keys, the @file: plus BeforeConnect design closes the rotation gap. One thing stops us adopting it as is. sameConnectionIdentity treats user as part of connection identity, so a rotation that changes the username fails every new acquire with database connection identity changed; restart required, while existing connections keep serving until maxConnLifetime expires. Our rotation changes the username by design: each cycle creates a new user with the new password and leaves the previous user able to log in until the following cycle, so that two credentials authenticate at once and no consumer fails while the secret store promotes the new version. That is a property of the database's rotation, not something a single consumer opts into. New dials should authenticate as the incoming user while older connections finish on the outgoing one, which is what the pool already does. Proposal: move User out of the identity comparison and into the set the refresh adopts, next to Password. Host, port, database and fallback host and port stay fenced, since those are the pool's endpoint and changing them mid-life does need a restart. That removes the user comparison rather than adding anything. |
Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io>
Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io>
|
@smeegoan, thanks for taking another look. I'm glad this approach overall will work for you. I have updated both sides of this (Substrate/Kagent) to allow for rotating the user to allow for the flow you laid out. |
Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io>
…tgres-with-substrate Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io>
Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io>
Important
This PR depends on agent-substrate/substrate#1752 and kagent-dev/substrate#32. After both merge and release, this branch still needs its Substrate image/chart dependency bumped and locked.
Summary
kagent_userlogin,kagent_ownergroup role, and schema with embedded identity SQL before table migrations. The bundled install uses a separate administrator Secret and a Kagent application connection Secret. The binary checks the fixed development credentials in that connection Secret and never resets an existing login's password.kagentdatabase while using separate owner and read/write connections in thesubstrateschema. The chart renders those connection Secrets with Substrate-owned Helm helpers and validates the shared database and administrator Secret references.public.@file:connection strings when opening new physical PostgreSQL connections and assume a stable group role.DB_MAX_CONN_LIFETIME(Helmdatabase.postgres.pool.maxConnLifetime) bounds Kagent connection lifetime; Helmsubstrate.postgres.pool.maxConnLifetimesets the corresponding Substrate flag. These settings bound how long old credentials remain in use. The migration CLI also accepts a role.public). Bootstrap installs the extension there; external databases must preinstall it there. Migrations check the installed location before writing, and both migrations and runtime qualify pgvector references by schema while keeping only Kagent's table schema on the normal search path. Multiple installs in one database can share the extension schema, even if it contains other application objects.USAGEthere. The runtime checks that its table schema is accessible before querying.kagent_owner. Bundled bootstrap keeps its fixed role names, which the binary passes explicitly.Scope and dependencies
This identity layout is for a fresh database. Database upgrades into this layout are not supported by this PR. Operators managing their own users must create the users, roles, schema, and grants, supply application connection Secrets, and disable bootstrap.
The bundled chart's default administrator and application passwords are fixed, published values. Fixed-identity bootstrap is for development and evaluation, not production. Production deployments should provision unique credentials and database permissions outside the application, use existing connection Secrets, and disable bootstrap.
Embedded Substrate requires the runtime in agent-substrate/substrate#1752 and the chart helpers in kagent-dev/substrate#32. This branch still pins an older Substrate module, CI version, and Helm dependency; those pins need a released companion version before integration or release. Local Helm validation used a packaged copy of the companion chart.
Multiple installs sharing one database should use distinct table schemas, group roles, and logins, with each login granted only its own install's roles.
Validation
go/,go test ./core/internal/database ./core/pkg/migrations -count=1passed;make -C go lintalso passed.helm unittest helm/kagentpassed (336 tests). A local umbrella-chart render verified custom Kagent and Substrate role names.