Overview
Constructive is pgpm, a package manager for versioned Postgres modules (schemas, row-level security policies, functions), plus a server that generates a GraphQL API from the database. This template deploys your pgpm modules from a git tag or your own image at boot, and serves them over an internal GraphQL API whose every request runs as a non-superuser role, so Postgres row-level security (RLS) decides which rows each user sees. It deploys only MIT-licensed code.This template does not create a GVC. It deploys into an existing GVC that you already have, and that GVC should have exactly one location, because the bundled database runs in one location.
What Gets Created
The API is reachable only from inside Control Plane. There is no public endpoint, because the
X-Actor-Id header that names the acting user is trusted as sent.
Prerequisites
1
Create the database credentials secret
PostgreSQL initializes its superuser and database from this dictionary secret, and the boot deploys modules with it:Set
postgres.config.credentialsSecretName to this name.2
Create the server login secret
The boot creates this login as Set
NOSUPERUSER NOBYPASSRLS, and the GraphQL server connects as it. Its username must differ from the database username:server.loginSecretName to this name. Secret names are org-wide, so give each release its own secrets.3
Choose a module source (optional)
A public git repository holding a pgpm workspace, or your own image built
FROM constructiveio/constructive:08e422d with the workspace copied in. See Deploying Modules.Installation
The required values are the names of the two secrets from Prerequisites:UI
Browse, install, and manage templates visually
CLI
Manage templates from your terminal
Terraform
Declare templates in your Terraform configurations
Pulumi
Declare templates in your Pulumi programs
Configuration
Server
Server login
pgpm modules
Deploying Modules
- Workspace layout: a pgpm workspace (
pgpm.jsonatmodules.path, packages under it).modules.packagestakes the pgpm package names (the%projectin eachpgpm.plan), deployed in the order listed. - Dependencies:
pgpm deployinstalls nothing. Commit everything your packages require to the workspace (thepgpm installoutput) or bake it into your image. Native extensions such aspgcryptoandpostgisneed nothing extra. - Grants: your modules grant
anonymousandauthenticatedwhat they may use. A table with no grant answerspermission denied. - New versions: set a new
modules.git.ref(or a newimagetag) and runcpln helm upgrade. A restart with the same settings skips the deploy, so a branch name never picks up new commits. - Failed deploy: the new replica stops with the pgpm error in its logs and never becomes ready, while the old replicas keep serving. Fix the module or revert the upgrade.
- Image source: build
FROM constructiveio/constructive:08e422d,COPYyour workspace to an absolute path, setimageto your image,modules.source: imageandmodules.pathto that path.
Access
Database
Connecting
Every request sends these headers:
Browsing the API in GraphiQL
The server ships GraphiQL at/graphiql, but the page itself needs X-Database-Id and X-Schemata, which a browser does not send, so a bare tab gets a 404.
- Run
cpln port-forward RELEASE_NAME-constructive 3000:3000 --gvc GVC_NAME. - Add
X-Database-Id: 00000000-0000-4000-8000-000000000c01andX-Schemata: YOUR_SCHEMASto every request tolocalhost:3000, with a browser extension that sets request headers or a local proxy that adds them. - Open
http://localhost:3000/graphiql(or the proxy’s address), and set{"X-Actor-Id": "USER_UUID"}in its Headers pane to query as that user; leave it empty to query asanonymous.
cpln port-forward.
Operations
Backing Up
Setpostgres.backup.enabled: true and fill the provider block under Database. The cloud account and bucket setup follows the postgres template.
Scaling and Availability
GraphQL server replicas hold no state, so they are interchangeable: setreplicas: 2 (up to 4) to keep the API up through restarts and upgrades. PostgreSQL is a single instance, so a database restart interrupts the API at any replica count.
Troubleshooting
The deployment never starts and cpln logs is empty
The deployment never starts and cpln logs is empty
A prerequisite secret does not exist. Read
status.versions[].message with cpln workload get-deployments RELEASE_NAME-constructive --gvc GVC_NAME -o yaml, create the named secret, then run cpln workload force-redeployment RELEASE_NAME-constructive --gvc GVC_NAME.After an upgrade the new replica never becomes ready
After an upgrade the new replica never becomes ready
The module deploy failed. Read the pgpm error with
cpln logs '{gvc="GVC_NAME", workload="RELEASE_NAME-constructive"}' --limit 50 --since 10m. The old replicas keep serving; fix the module or revert the upgrade.permission denied for table
permission denied for table
The module grants nothing on that table to
anonymous or authenticated. Add the grants to your module and deploy a new version.The server stops starting after the database secret was edited
The server stops starting after the database secret was edited
PostgreSQL keeps the credentials it first initialized with, and the boot authenticates with the secret. Restore the original values in the database credentials secret.
Important Notes
X-Actor-Idis trusted as sent, andX-Schematacan name any schema the server login can read. Allow only workloads you trust ininternalAccess; there is no public endpoint for this reason.- The server login is re-created on every start. To rotate its password, change
passwordwithcpln secret edit LOGIN_SECRET_NAME, then runcpln workload force-redeployment RELEASE_NAME-constructive --gvc GVC_NAME. - Do not edit the database credentials secret after install.
- Keep
postgres.imageon an image with PostGIS: Constructive’s base modules require it, and a backup restores only into such a server. - Jobs that modules enqueue are not processed: this template runs no job worker.
- The first
cpln helm upgradeafter an install can restart the API. - Use a single-location GVC. Uninstalling deletes the database volume.
External References
Constructive on GitHub
Source and documentation
Constructive quickstart
Getting started with pgpm and the server
GraphQL server reference
Headers, schemas and server behavior
pgpm modules
Reusable Postgres modules