Skip to content

feat(services): provision a managed Postgres database service (PLO-22)#56

Merged
ismatBabirli merged 2 commits into
mainfrom
feature/plo-22
Jun 27, 2026
Merged

feat(services): provision a managed Postgres database service (PLO-22)#56
ismatBabirli merged 2 commits into
mainfrom
feature/plo-22

Conversation

@ismatBabirli

Copy link
Copy Markdown
Contributor

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 template service provisioned from a small, control-plane-owned catalogue (internal/services/templates.go — Postgres today; no template registry table). On CreateDatabaseService the control plane:

  1. Fixes the image (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.
  2. Generates the credentials (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.
  3. Commits the credentials + the service row + the first deployment in one transaction. The env vars are written through a new consumer-defined EnvVarSetter port (satisfied by *envvars.Service, the same shape as the deploy Enqueuer) — so services never imports envvars and env_vars stays owned by its module (modules.md Rule 1/2).
  4. Returns the assembled connection URI (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

  • proto: ServiceService.CreateDatabaseService(environment_id, name, template_id, server_id, deploy_now){service, deployment_id, connection_uri}.
  • services: a databaseTemplate catalogue + CreateDatabase (authorized as a service create, audited as service.create_database); the EnvVarSetter port.
  • envvars: SetWithinTx writes 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).
  • dashboard: a "PostgreSQL database" lane in Add a service, and a Connection panel on the service detail that rebuilds host/port/user/db + the full connection string from the stored env vars, each copyable, with a prominent ephemeral-data warning.
  • docs: a "Managed database services" section in deployment-engine.md.

Acceptance criteria

  • ✅ A Postgres resource can be created and associated with a project/environment — the Add a service "PostgreSQL database" lane provisions a private template service in the chosen environment.
  • ✅ Connection information is made available through the env-config flow — credentials are the service's env vars; the connection URI is returned at create time and rebuilt on the service detail's Connection panel.
  • ✅ Resource state is visible in the dashboard — a database is a normal service, so its deployment status shows in the service list + detail.

Scope / deferred (noted in deployment-engine.md)

  • No persistence yet: the agent mounts no volumes, so a redeploy starts a fresh database. The UI flags the database as ephemeral. Persistent volumes are a later slice (related to PLO-58).
  • Password storage: the generated password lives in the service's (plaintext, readable) env vars rather than the sealed-secret path, because secret injection at deploy time isn't wired yet. Moving managed credentials onto that path is a follow-up.

Testing

  • make verify green: go build ./..., go test ./..., golangci-lint (0 issues), buf lint, web lint + typecheck. pnpm build (vite) succeeds.
  • New internal/services tests: provisions a private postgres template service with generated creds + deploy + audit + connection URI; provisions without deploy_now; rejects an unknown template; denied writes nothing.

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.
@linear-code

linear-code Bot commented Jun 16, 2026

Copy link
Copy Markdown

PLO-22

PLO-58

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.
@ismatBabirli ismatBabirli merged commit 5290428 into main Jun 27, 2026
7 checks passed
@ismatBabirli ismatBabirli deleted the feature/plo-22 branch June 27, 2026 14:48
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 27, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant