ccppccpp home

Profiles

PostgreSQL 18.6

PostgreSQL 18.6 schemas, migrations, queries, roles, configuration and operations read from SQL text, parsed commands, configuration files and postgres-language-server. Rule IDs PG-V/S/M/Q/A/C/O/P; NEED TO blocks or asks, SHOULD warns.

Set this profile

ccpp cpo profile set postgres-18

Inside Claude Code, prefix the command with ! to run it in the session. Downloading a profile needs a Builder or Team plan; listing them does not.

Policies

125 policies, grouped by what they guard.

Secrets & private data 4

Credentials, keys and personal data stay out of files, logs, commands and the conversation.

  • pg-a08-trust-env

    PG-A08 · The user confirms container setups with POSTGRES_HOST_AUTH_METHOD=trust (every published port then accepts any login)

  • pg-a10-command-passwords

    PG-A10 · Commands carry no literal database password (URLs, PGPASSWORD=…, password= in arguments)

  • pg-a10-pgpass-mode

    PG-A10 · A written .pgpass file is made owner-only (libpq ignores it otherwise)

  • pg-a10-sql-passwords

    PG-A10 · Tracked SQL holds no literal password (CREATE/ALTER ROLE … PASSWORD, user mappings, connection strings)

Authentication & authorization 12

Who may do what, from roles, privileges and permissions to CSRF, CORS and verified tokens.

  • pg-a01-role-attributes-command

    PG-A01 · The user confirms commands and database MCP calls giving roles SUPERUSER, BYPASSRLS, REPLICATION, CREATEROLE or server-file privileges

  • pg-a01-role-attributes

    PG-A01 · The user confirms roles given SUPERUSER, BYPASSRLS, REPLICATION, CREATEROLE or server-file privileges in SQL files

  • pg-a03-public-privileges

    PG-A03 · PUBLIC gets no CREATE on a schema and no blanket ALL privileges: a writable schema on a search path is an attack path

  • pg-a04-definer-execute

    PG-A04 · New SECURITY DEFINER functions revoke EXECUTE from PUBLIC in the same file

  • pg-a04-security-definer

    PG-A04 · SECURITY DEFINER routines pin a trusted search_path (no public, no $user, pg_temp last)

  • pg-a06-rls-force

    PG-A06 · Tables getting row-level security are checked under the role that really runs the queries (owners bypass RLS unless FORCE)

  • pg-a08-hba-trust

    PG-A08 · pg_hba.conf never trusts network connections beyond the local host (trust over TCP needs no password at all)

  • pg-a11-extensions-command

    PG-A11 · The user confirms commands and database MCP calls creating extensions outside allowedExtensions or loading libraries

  • pg-a11-extensions

    PG-A11 · The user confirms new extensions outside allowedExtensions, native C functions, LOAD and preload libraries

  • pg-v09-md5-authentication

    PG-V09 · New authentication setup uses SCRAM, not MD5 (deprecated in PostgreSQL 18)

  • pg-v17-ignore-cipher-failure-command

    PG-V17 · The user confirms a command or MCP call using pgcrypto's ignore-cipher-failure option (recovery and re-encryption only)

  • pg-v17-ignore-cipher-failure

    PG-V17 · The user confirms pgcrypto's ignore-cipher-failure option in a file: it only strips the broken pre-18.6 PGP wrapper for re-encryption

Untrusted input 4

Requests, downloads and tool output are validated before they reach SQL, a shell, eval or a deserializer.

  • pg-a12-restore-sources

    PG-A12 · The user confirms restoring a dump or piping downloaded SQL into psql: a dump is executable input

  • pg-a13-server-copy-command

    PG-A13 · The user confirms commands and database MCP calls running server-side COPY to/from a program or a server file

  • pg-a13-server-copy

    PG-A13 · The user confirms server-side COPY to/from a program or a server file in SQL files (client-side \copy needs no confirmation)

  • pg-q01-dynamic-sql-concat

    PG-Q01 · Dynamic SQL (PL/pgSQL EXECUTE) binds values with USING and quotes identifiers with format(%I)/quote_ident, never by concatenation

Network & TLS 1

Outbound data, verified certificates, destinations taken from requests and services exposed to others.

  • pg-a09-sslmode

    PG-A09 · Connections over untrusted networks verify the server (sslmode=verify-full), not just encrypt or allow plaintext

Destructive actions 5

Deletions, overwrites and irreversible commands wait for the user or a scoped target.

  • pg-m17-destructive-commands

    PG-M17 · The user confirms destructive SQL run directly (psql, database MCP tools) or dropdb, outside disposable databases

  • pg-m17-destructive-ddl

    PG-M17 · The user confirms destructive DDL in migrations (DROP TABLE/COLUMN/SCHEMA, TRUNCATE, CASCADE) on objects this file did not create

  • pg-o04-live-pgdata-copy

    PG-O04 · The user confirms file-level copies of a data directory: a live PGDATA copy is not a backup

  • pg-o06-wal-deletion

    PG-O06 · Nobody deletes files in pg_wal (or the WAL archive) by hand

  • pg-s20-unlogged

    PG-S20 · The user confirms an UNLOGGED table: its data is emptied after a crash and never reaches physical replicas

Weakened checks 2

Suppressions, disabled lint or CI steps and relaxed strictness need a reason or the user's approval.

  • pg-p06-trigger-bypass-command

    PG-P06 · The user confirms commands and database MCP calls disabling triggers or setting session_replication_role = replica

  • pg-p06-trigger-bypass

    PG-P06 · The user confirms disabling or dropping triggers and session_replication_role = replica in SQL files

Error handling 1

Failures are propagated or handled, never swallowed or turned into fake success.

  • pg-p08-swallowed-exceptions

    PG-P08 · PL/pgSQL handlers do not swallow errors (WHEN OTHERS THEN NULL / a success-shaped value)

Performance & resource bounds 6

Timeouts, bounded retries and fan-out, reused clients, closed resources, no N+1 queries.

  • pg-q05-large-offset

    PG-Q05 · Deep OFFSET pagination could become keyset pagination

  • pg-q06-row-loops

    PG-Q06 · PL/pgSQL loops that write once per selected row become one set-based statement

  • pg-q13-explain-analyze-dml

    PG-Q13 · The user confirms EXPLAIN ANALYZE of a data-changing statement: it executes it

  • pg-q14-cte-materialization

    PG-Q14 · CTE MATERIALIZED / NOT MATERIALIZED directives carry a measured reason

  • pg-q15-redundant-index

    PG-Q15/V13 · New B-tree indexes do not duplicate a key or a longer index on the same table (18 skip scan may already serve later columns)

  • pg-q16-wide-include

    PG-Q16 · Wide INCLUDE payloads (four or more columns) are backed by a measured index-only scan

Databases & queries 21

Schemas, queries, transactions and the ORM used as intended.

  • pg-p05-trigger-generated

    PG-P05 · Trigger functions that only derive NEW columns from other NEW columns become generated columns

  • pg-p07-plpgsql-single-query

    PG-P07 · Functions whose body is one query are LANGUAGE SQL, not PL/pgSQL

  • pg-q02-null-comparison

    PG-Q02 · NULL is tested with IS [NOT] NULL or IS [NOT] DISTINCT FROM, never with = or <> (always unknown)

  • pg-q03-select-star-view

    PG-Q03 · Views list their result columns instead of SELECT *

  • pg-q04-limit-without-order

    PG-Q04 · Top-N queries (LIMIT/FETCH beyond one row) have an ORDER BY, unless any rows will do

  • pg-q04-offset-without-order

    PG-Q04 · Paginated queries (OFFSET) have an ORDER BY: without it pages overlap and skip rows

  • pg-q11-skip-locked

    PG-Q11 · SKIP LOCKED is used for queue-like claims (bounded with LIMIT), not for ordinary results

  • pg-q17-immutable-functions

    PG-Q17 · Functions declared IMMUTABLE do not read the clock, randomness, sequences, settings or session state

  • pg-q18-recursive-termination

    PG-Q18 · Recursive CTEs show how they terminate: a CYCLE clause or a bound in the recursive term

  • pg-s01-table-key

    PG-S01 · New durable tables declare a primary or candidate key

  • pg-s04-check-row-local

    PG-S04 · CHECK constraints are row-local and stable: no clock, randomness, session state, sequences or subqueries

  • pg-s07-composite-fk-match

    PG-S07 · Composite foreign keys choose MATCH FULL or MATCH SIMPLE: by default a single NULL column skips the check

  • pg-s08-fk-index

    PG-S08 · Foreign keys created in a SQL file have an index led by their referencing column in the same file, or a stated reason

  • pg-s09-serial

    PG-S09 · New sequence-generated columns use GENERATED … AS IDENTITY, not serial types

  • pg-s10-identity-uniqueness

    PG-S10 · A new table's identity column is backed by a primary key or unique constraint: identity alone permits duplicates

  • pg-s12-blank-padded-char

    PG-S12 · New text columns are text or varchar, not blank-padded char(n) by accident

  • pg-s13-timestamp-semantics

    PG-S13 · New timestamp columns state their semantics: timestamptz for instants, timestamp only for local wall-clock values

  • pg-s15-exact-quantities

    PG-S15 · Money and exact quantities use numeric or scaled integers, not money or floating point

  • pg-s16-ad-hoc-types

    PG-S16/S17 · Native uuid and boolean types instead of text encodings; references are not hidden in id arrays

  • pg-s19-partition-keys

    PG-S19 · Primary keys and unique constraints of a partitioned table include every partition key column

  • pg-s20-unlogged-partitioned

    PG-S20 · No UNLOGGED partitioned table: PostgreSQL 18 rejects it

Schema migrations 21

Migrations come from the tool, applied history stays immutable and risky changes are rolled out safely.

  • pg-m01-direct-ddl

    PG-M01 · The user confirms schema changes run directly through psql or a database MCP tool instead of the project's migrations

  • pg-m02-migration-generator

    PG-M02 · New migration files come from the project's migration tool (Prisma, Supabase, Atlas, Sqitch) when it owns their names, hashes or ledgers

  • pg-m03-applied-history

    PG-M03 · The user confirms changing a committed migration: applied history is fixed by a follow-up migration

  • pg-m03-migration-removal

    PG-M03 · The user confirms deleting or renaming committed migration files

  • pg-m04-if-not-exists-index

    PG-M04 · CREATE INDEX IF NOT EXISTS in a migration comes with a check of the index's validity and definition

  • pg-m05-lock-timeout

    PG-M05 · Migrations that lock existing tables set a lock_timeout (and no zero/infinite timeout without a decision)

  • pg-m06-rewrite-additions

    PG-M06 · Columns added to existing tables avoid full rewrites: no volatile default, identity, serial or STORED generation in one step

  • pg-m07-unique-not-valid

    PG-M07 · No UNIQUE, PRIMARY KEY or EXCLUDE constraint declared NOT VALID: PostgreSQL rejects it

  • pg-m07-validate-separately

    PG-M07 · CHECK, FOREIGN KEY and NOT NULL constraints on existing tables are added NOT VALID, then validated in a separate step

  • pg-m08-pending-validation

    PG-M08 · At the end of a turn, constraints this session added NOT VALID are listed until a VALIDATE CONSTRAINT follows them

  • pg-m09-concurrent-index

    PG-M09 · Indexes on existing tables in migrations are built CONCURRENTLY (new and temporary tables are exempt)

  • pg-m10-concurrent-in-transaction

    PG-M10 · Concurrent index operations stay outside explicit transactions and runner-injected ones (dbmate, goose)

  • pg-m10-concurrent-index-alone

    PG-M10 · A migration with CREATE INDEX CONCURRENTLY holds nothing else: runners that send a file as one query wrap it in a transaction

  • pg-m10-single-transaction-script

    PG-M10 · psql --single-transaction does not run a script holding CREATE INDEX CONCURRENTLY

  • pg-m11-invalid-index-retry

    PG-M11 · After a failed concurrent index build, the invalid index is inspected or dropped before a retry

  • pg-m12-constraint-using-index

    PG-M12 · Primary keys and unique constraints on existing tables attach a unique index built concurrently

  • pg-m13-partitioned-concurrent-index

    PG-M13 · No CREATE INDEX CONCURRENTLY on a partitioned parent: indexes are built per partition and attached

  • pg-m14-expand-contract

    PG-M14 · Renames of existing tables and columns go through expand/contract while old code still runs

  • pg-m15-unbounded-backfill

    PG-M15 · Backfills of existing tables in migrations are bounded (batches) rather than one statement over every row

  • pg-m16-type-change

    PG-M16 · The user confirms column type and stored expression changes on existing tables (rewrite, locks, dependents)

  • pg-p16-introspection

    PG-P16 · Before the first new migration of a session, Claude reads the existing schema from the catalog (once per session)

Database operations 18

Backups, upgrades, replication and maintenance done with the server's own tools.

  • pg-o01-insert-dumps

    PG-O01 · Data exports are made with pg_dump, not hand-generated INSERT files (dump/backup/export files with hundreds of INSERTs)

  • pg-o01-insert-generation

    PG-O01 · Exports are not produced by SQL that prints INSERT statements (use pg_dump)

  • pg-o03-script-errors

    PG-O03 · Scripted psql stops on the first error (ON_ERROR_STOP) and pg_restore exits on errors

  • pg-o05-verify-backup

    PG-O05 · After a successful pg_basebackup, Claude verifies it with pg_verifybackup and plans a restore test

  • pg-o06-wal-archive

    PG-O06 · archive_command refuses to overwrite and does not fake success; the WAL chain is not cut by hand

  • pg-o08-logical-replication

    PG-O08 · New publications and subscriptions come with a plan for DDL, sequences and replica identity

  • pg-o09-slot-retention

    PG-O09 · Replication slots come with a WAL retention budget (max_slot_wal_keep_size, idle_replication_slot_timeout)

  • pg-o10-upgrade-check

    PG-O10 · pg_upgrade runs for real only after a successful pg_upgrade --check this session

  • pg-o10-upgrade-mode

    PG-O10 · The user confirms pg_upgrade --link, --swap or --clone: the old cluster cannot be started again afterwards

  • pg-o11-post-upgrade

    PG-O11 · After a successful pg_upgrade, Claude restores missing statistics and runs the generated follow-ups

  • pg-o12-rewriting-maintenance-command

    PG-O12 · The user confirms commands running VACUUM FULL, CLUSTER, REINDEX without CONCURRENTLY or vacuumdb --full

  • pg-o12-rewriting-maintenance

    PG-O12 · The user confirms maintenance that rewrites or locks live tables (VACUUM FULL, CLUSTER, REINDEX without CONCURRENTLY, SET TABLESPACE) in SQL files

  • pg-o13-bulk-inserts

    PG-O13 · Bulk loads use COPY rather than thousands of single-row INSERTs or a psql call per row

  • pg-o14-copy-reject-limit

    PG-O14 · COPY … ON_ERROR IGNORE sets a REJECT_LIMIT (and reports rejected rows)

  • pg-v08-checksums

    PG-V08 · Durable clusters keep data checksums (initdb's 18 default); turning them off is a stated decision

  • pg-v10-removed-stats-columns

    PG-V10 · Monitoring queries use PostgreSQL 18 statistics columns (pg_stat_io.op_bytes, pg_stat_wal timings, memory-context parent are gone)

  • pg-v11-maintenance-scope

    PG-V11 · VACUUM/ANALYZE of a partitioned or inheritance parent states its scope: PostgreSQL 18 also processes the children unless ONLY

  • pg-v16-output-plugins

    PG-V16 · A third-party logical decoding plugin is listed in output_plugin_libraries (18.6 only admits pgoutput and test_decoding by default)

Versions & deprecated APIs 11

Code matches the installed version, without removed, renamed or deprecated APIs.

  • pg-v01-version-gate

    PG-V01/V02/O02 · Utilities that apply, dump, restore, back up or upgrade run once the session has seen the server and utility versions, at or above the 18.6 floor

  • pg-v02-newer-patch

    PG-V02 · A pin on an 18 patch newer than the 18.6 contract asks for the contract to be updated

  • pg-v02-other-major

    PG-V02 · The user confirms a new pin on a PostgreSQL major other than 18 (a compatibility target is a decision, not a default)

  • pg-v02-patch-floor

    PG-V02 · No new PostgreSQL 18 pin below the 18.6 floor (images, packages, CI, tool versions) and no reference to 18.5, which was never released

  • pg-v03-versioned-docs

    PG-V03 · PostgreSQL documentation links point at /docs/18 and REL_18_STABLE, not /current, /devel, older majors or master

  • pg-v04-generated-storage

    PG-V04 · New generated columns say STORED or VIRTUAL: PostgreSQL 18 defaults to VIRTUAL where 16/17 only had STORED

  • pg-v05-virtual-user-functions

    PG-V05 · VIRTUAL generated columns call no user-defined function (schema-qualified outside pg_catalog, or created in the file)

  • pg-v07-uuid-extension

    PG-V07 · UUID v4/v7 come from built-ins (gen_random_uuid, uuidv4, uuidv7), not from an extension added for them

  • pg-v12-copy-terminator

    PG-V12 · Inline COPY … FROM STDIN data in a psql script ends with a standalone \. line

  • pg-v15-not-enforced

    PG-V15 · The user confirms a NOT ENFORCED constraint: PostgreSQL 18 then never checks it, unlike NOT VALID

  • pg-v18-upgrade-notes

    PG-V18/V19/V20 · Moving a server pin to 18.6 or later from an older release comes with the 18.2-18.6 remediation checklist

Configuration 11

Settings read once at a boundary, validated, with units and without unsafe defaults.

  • pg-c01-durability-off

    PG-C01 · Durable clusters keep fsync and full_page_writes on

  • pg-c02-synchronous-commit

    PG-C02 · The user confirms reducing synchronous_commit: recent commits can be lost on a crash or failover

  • pg-c04-max-connections

    PG-C04 · max_connections grows only with a resource budget (memory per backend, a pooler), not as a fix for 'too many clients'

  • pg-c05-effective-cache-size

    PG-C05 · effective_cache_size is described as a planner estimate, never as memory PostgreSQL allocates

  • pg-c06-global-timeouts

    PG-C06 · statement, transaction and lock timeouts are scoped to roles, databases or sessions, not set server-wide

  • pg-c07-autovacuum-off

    PG-C07 · Autovacuum stays on (server-wide and per table) unless a replacement maintenance plan is stated

  • pg-c08-autovacuum-worker-slots

    PG-C08 · autovacuum_max_workers stays within autovacuum_worker_slots (PostgreSQL 18)

  • pg-c10-partition-analyze

    PG-C10 · New partitioned tables come with a plan to ANALYZE the parent (autovacuum only analyzes the partitions)

  • pg-c11-planner-switches

    PG-C11 · Planner features (enable_*) stay on server-wide and per role/database; disabling them is a scoped experiment

  • pg-c12-io-method

    PG-C12 · io_method and I/O concurrency changes are measured on the target platform (PostgreSQL 18 asynchronous I/O)

  • pg-c14-units

    PG-C14 · Memory and time settings carry explicit units (each parameter has its own default unit)

Generated files 5

Build outputs, generated code and tool-owned files are regenerated, never edited by hand.

  • pg-c13-auto-conf

    PG-C13 · postgresql.auto.conf is written by ALTER SYSTEM only, never edited by hand

  • pg-p11-migration-ledger-command

    PG-P11 · Commands and database MCP calls do not write migration ledgers by hand

  • pg-p11-migration-ledger

    PG-P11 · Migration ledgers (schema_migrations, flyway_schema_history, _prisma_migrations…) are written by their tool only

  • pg-p11-tool-owned-files

    PG-P11 · Files a tool generates (dumped schemas, migration locks, atlas.sum) are not edited with file tools

  • pg-p15-pgdata-files

    PG-P15 · Files inside a PostgreSQL data directory are never written by hand (configuration files and *.signal excepted)

Completion evidence 3

A turn ends with its checks run and passing; claims of success need proof after the last edit.

  • pg-evidence-ledger

    Records, per session, the PostgreSQL versions, introspection, migration runs, pg_upgrade checks and failed concurrent index builds that commands showed

  • pg-m18-migration-test

    PG-M18 · A turn that changed migrations ends only after a migration run succeeded since the last change (once per stop)

  • pg-p13-sql-syntax

    PG-P13 · No new PostgreSQL syntax error in a SQL file (postgres-language-server), except where its pre-18 parser cannot judge