feat(services): provision a managed Postgres database service (PLO-22)#56
Merged
Conversation
Create a basic, managed-by-agent Postgres database for an environment from the
dashboard, with its connection info surfaced through the env config flow and its
state visible like any other service.
A managed database is a `template` service provisioned from a small,
control-plane-owned catalogue (Postgres today): the control plane fixes the image
(postgres:16-alpine) and port (5432), forces PRIVATE visibility (a database must
never get a public Caddy route), generates the credentials, and stores them as the
service's env vars so the agent injects them when it starts the container. The
credentials, the service row, and the first deployment all commit in one
transaction — the env vars are written through a new consumer-defined EnvVarSetter
port (the same shape as the deploy Enqueuer), so services never imports envvars and
env_vars stays owned by its module.
- proto: ServiceService.CreateDatabaseService(environment, name, template_id,
server, deploy_now) -> {service, deployment_id, connection_uri}.
- services: a databaseTemplate catalogue + CreateDatabase; password generated with
crypto/rand; connection URI assembled as postgresql://user:pass@{slug}:5432/db.
- envvars: SetWithinTx writes a service's env vars inside the caller's tx (no auth
of its own — the caller authorized the service create).
- dashboard: a "PostgreSQL database" lane in Add a service, and a Connection panel
on the service detail that rebuilds host/port/user/db + the connection string
from the stored env vars, with copy controls.
Scope: data is NOT yet persisted across redeploys (the agent mounts no volumes — a
later slice; the UI flags the database as ephemeral), and the generated password
lives in the service's (plaintext) env vars rather than the sealed-secret path
because secret injection at deploy time isn't wired yet. Both are noted follow-ups
in deployment-engine.md.
Resolve the envvars→config refactor against PLO-22's managed Postgres. main's PLO-19 (#48) deleted internal/envvars and internal/secrets, unifying them into internal/config (ConfigService: variable|secret × service|environment). PLO-22 built on envvars, so re-target its credential-write path onto config: - config: add Service.SetWithinTx — writes service-scoped plaintext VARIABLES inside the caller's transaction (no authz/audit/logging; the services module authorizes and audits the service create). Managed DB credentials stay variables so the dashboard can read them back to build the connection URI. - services: rename the EnvVarSetter port to ConfigSetter and wire a.config.Service() in place of the deleted a.envvars.Service(). - web ServiceDetailPage: keep main's removal of the per-service env-var panel; re-add the managed-database Connection panel, ported from useEnvVars to useConfig (reads POSTGRES_* config variables). Regenerated proto + sqlc; go build/test/lint and web typecheck/lint/build green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Create a basic, managed-by-agent PostgreSQL database for an environment from the dashboard — connection info surfaced through the env-config flow, state visible like any other service.
How
A managed database is a
templateservice provisioned from a small, control-plane-owned catalogue (internal/services/templates.go— Postgres today; no template registry table). OnCreateDatabaseServicethe control plane:postgres:16-alpine) and port (5432) and forces PRIVATE visibility — a database must never get a public Caddy route; only its siblings reach it over the per-environment network at{slug}:5432.crypto/rand), and stores them as the service's env vars (POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB) so the agent injects them when it starts the container.EnvVarSetterport (satisfied by*envvars.Service, the same shape as the deployEnqueuer) — soservicesnever importsenvvarsandenv_varsstays owned by its module (modules.md Rule 1/2).postgresql://user:pass@{slug}:5432/db) once.No agent change is needed — the agent already runs a private image with env injection and sibling DNS.
Changes
ServiceService.CreateDatabaseService(environment_id, name, template_id, server_id, deploy_now)→{service, deployment_id, connection_uri}.databaseTemplatecatalogue +CreateDatabase(authorized as a service create, audited asservice.create_database); theEnvVarSetterport.SetWithinTxwrites a service's env vars inside the caller's tx (no auth of its own — the caller already authorized the create; nothing logged, as these may be credentials).deployment-engine.md.Acceptance criteria
Scope / deferred (noted in
deployment-engine.md)Testing
make verifygreen:go build ./...,go test ./...,golangci-lint(0 issues),buf lint, web lint + typecheck.pnpm build(vite) succeeds.internal/servicestests: provisions a private postgres template service with generated creds + deploy + audit + connection URI; provisions without deploy_now; rejects an unknown template; denied writes nothing.