Feature Specification: Component postgresql
Created: 2026-10-07
Status: Implemented
Input: Reconstructed from the code at 093e68b6f (core/components/hosting-provider/postgresql/default.nix, core/components/hosting-provider/pgweb.nix, core/setups/hosts-common.nix, core/resources/oidc/forward-auth-domain.nix, core/checks/nix-unit-tests.nix, core/checks/default.nix).
User Scenarios & Testing (mandatory)
User Story 1 - Run the shared database node (Priority: P1)
The apps of the hosting provider and of the operator deployments need
PostgreSQL databases. The postgresql component marks the node that runs
the shared PostgreSQL server. It declares provides = [ "postgresql" ].
The setups find the node by this capability. They add the server config,
the databases and roles of the consumers and the roster to it (see
postgresql and
005).
Why this priority: every app with a postgresql request needs this
node.
Independent Test: nix-unit-setups-postgresql-roster holds the two
halves of one roster: what the node creates and what the consumers expect.
nix-unit-setups-postgresql-render-gate and contracts-postgresql cover
the provider. No test boots this component as a whole.
Acceptance Scenarios:
- Given a node whose app
providespostgresql, When the setups evaluate, Then the node importspostgresql.mainConfigand thepostgresqlsupporting requests of each node in the group. - Given that node in the
hosting-providergroup, When the setups evaluate, Then the node also gets the operator requests, the roster frompostgresqlRoster.nodeConfigandappSubnetswithsamenetadded. - Given a deployment that serves PKI, When the
sslresource renders the leaf ofpostgresql.<domain>, Then the key ispostgres:postgreswith mode0600, and a new leaf restartspostgresql.serviceandpgweb.service.
User Story 2 - Read the databases in a web UI (Priority: P2)
An administrator wants to look at the tables without a shell. The
component requests a forward-auth vhost pgweb.<apex> in front of pgweb
on 127.0.0.1:8082. Authelia checks the session first.
Why this priority: it is a convenience for operations. The databases work without it.
Independent Test: nix-unit-setups-served-domains and
nix-unit-setups-apex-cohesion include pgweb in the served names. No
test opens the UI.
Acceptance Scenarios:
- Given the policy sets
deployAdminUIs, When theoidcresource applies thepgweb-uirequest, Then the vhostpgweb.<apex>asks forone_factorand proxies to127.0.0.1:8082. - Given a running UI, When an administrator runs a write statement, Then
pgwebrefuses it, because it runs with--readonly. - Given the UI, When an administrator picks an app database in the sidebar, Then
pgwebswitches to it, because it runs without--lock-session.
Edge Cases
pgwebconnects aspostgresover127.0.0.1withsslmode=disable. It needs thepg_hbarulehost 127.0.0.1/32 trustfrom thepostgresqlresource. Without that rule, the UI cannot connect.pgwebstarts afterpostgresql.servicewithWants=. A boot where PostgreSQL waits for its leaf can exhaust the start limit ofpgweb. The leaf restart ofpgweb.servicerecovers it.- With
deployAdminUIsunset, the resource emits no vhost and nopgwebunit. - The node has no backup of the full cluster. Each consumer backs up its own database with a
streamingBackupto restic, over its own credential. A database whose consumer adds no backup has no offsite copy.
Requirements (mandatory)
Functional Requirements
- FR-001: The component MUST declare
components.postgresqlwithpackage = pkgs.postgresql, the icon fromsources.postgresql-pgwebandprovides = [ "postgresql" ]. - FR-002: The implementation MUST request
spiffe.enableunder the namespiffe, so that the node can get an OpenBao token. - FR-003: The implementation MUST request
oidc.forwardAuthunder the namepgweb-uiwithdomain = "pgweb.<apex>",upstream = "127.0.0.1:8082",authorizationPolicy = "one_factor"andallowedGroups = [ ], and importpgweb.nixthrough itsnixos-modulerequest.pgweb.nixMUST set thepgwebunit only whenfediversity.forwardAuth.domainshas an entry for127.0.0.1:8082. - FR-004:
pgweb.nixMUST runsystemd.services.pgwebas aDynamicUserwith--bind=127.0.0.1 --listen=8082 --readonlyandRestart=on-failure. - FR-005: The component MUST NOT carry the server config, the databases or the roles. The setups MUST add them to the node whose app
providespostgresql. - FR-006: On a node that serves PKI, the setups MUST request the leaf key as
postgres:postgres 0600, and restartpostgresql.serviceandpgweb.servicewhen the leaf renders again. - FR-007: On the node of the central group (the one group whose roster provides
varsBackend), the setups MUST add the requests of the operator groups, the full roster andsamenetinappSubnets.
Key Entities
pgweb-uirequest: the forward-auth vhost of the read-only UI.- PostgreSQL roster: the databases and roles that the node creates for its consumers.
Success Criteria (mandatory)
Measurable Outcomes
- SC-001:
nix-unit-setups-postgresql-roster,nix-unit-setups-postgresql-render-gateandcontracts-postgresqlpass. - SC-002:
nix-unit-setups-served-domainsandnix-unit-setups-apex-cohesionpass. - SC-003: On a live node,
https://pgweb.<apex>/sends a browser without a session to the Authelia portal.
Assumptions
- Authelia runs in the same deployment (see
authelia). - The entry point passes the apex as the
apexmodule argument.core/apex.nixreads it fromDNS_DOMAIN.
Known Deviations
None.