Skip to main content

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 NOSUPERUSER NOBYPASSRLS, and the GraphQL server connects as it. Its username must differ from the database username:
Set 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.
Both secrets must exist before you install. If either does not, the deployment waits silently: cpln logs returns nothing, and the only place the missing secret is named is status.versions[].message in cpln workload get-deployments RELEASE_NAME-constructive --gvc GVC_NAME -o yaml. Create the secret and the deployment recovers on its own after several minutes, or run cpln workload force-redeployment RELEASE_NAME-constructive --gvc GVC_NAME to skip the wait.
Optional database backups need a cloud account and bucket, as described in the postgres template.

Installation

The required values are the names of the two secrets from Prerequisites:
To install, follow the instructions for your preferred method:

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.json at modules.path, packages under it). modules.packages takes the pgpm package names (the %project in each pgpm.plan), deployed in the order listed.
  • Dependencies: pgpm deploy installs nothing. Commit everything your packages require to the workspace (the pgpm install output) or bake it into your image. Native extensions such as pgcrypto and postgis need nothing extra.
  • Grants: your modules grant anonymous and authenticated what they may use. A table with no grant answers permission denied.
  • New versions: set a new modules.git.ref (or a new image tag) and run cpln 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, COPY your workspace to an absolute path, set image to your image, modules.source: image and modules.path to 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.
  1. Run cpln port-forward RELEASE_NAME-constructive 3000:3000 --gvc GVC_NAME.
  2. Add X-Database-Id: 00000000-0000-4000-8000-000000000c01 and X-Schemata: YOUR_SCHEMAS to every request to localhost:3000, with a browser extension that sets request headers or a local proxy that adds them.
  3. 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 as anonymous.
The Docs pane lists the types and mutations generated from your modules. If the page stops loading after a while, restart the cpln port-forward.

Operations

Backing Up

Set postgres.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: set replicas: 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

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.
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.
The module grants nothing on that table to anonymous or authenticated. Add the grants to your module and deploy a new version.
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-Id is trusted as sent, and X-Schemata can name any schema the server login can read. Allow only workloads you trust in internalAccess; there is no public endpoint for this reason.
  • The server login is re-created on every start. To rotate its password, change password with cpln secret edit LOGIN_SECRET_NAME, then run cpln workload force-redeployment RELEASE_NAME-constructive --gvc GVC_NAME.
  • Do not edit the database credentials secret after install.
  • Keep postgres.image on 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 upgrade after 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