{"record":{"id":"be48325390daf511","repo":"JuliusBrussee/caveman","slug":"postgres-production-database-url-requires-sslmode","errorCode":null,"errorMessage":"postgres: production DATABASE_URL requires sslmode=verify-full","messagePattern":"postgres: production DATABASE_URL requires sslmode=verify-full","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/postgresconfig/postgresconfig.go","lineNumber":37,"sourceCode":")\n\nconst (\n\tcaEnvironment     = \"CAVE_POSTGRES_CA_CERT\"\n\tcaFileEnvironment = \"CAVE_POSTGRES_CA_CERT_FILE\"\n)\n\n// ParsePoolConfig validates the connection string and applies the managed\n// database CA to every TLS path. Production rejects sslmode=require because it\n// encrypts without authenticating the server; verify-full is mandatory.\nfunc ParsePoolConfig(databaseURL string) (*pgxpool.Config, error) {\n\tproduction := runtimeenv.IsProduction()\n\tif production {\n\t\tparsed, err := url.Parse(databaseURL)\n\t\tif err != nil || (parsed.Scheme != \"postgres\" && parsed.Scheme != \"postgresql\") || parsed.Hostname() == \"\" {\n\t\t\treturn nil, errors.New(\"postgres: production DATABASE_URL must be a Postgres URL\")\n\t\t}\n\t\tif parsed.Query().Get(\"sslmode\") != \"verify-full\" {\n\t\t\treturn nil, errors.New(\"postgres: production DATABASE_URL requires sslmode=verify-full\")\n\t\t}\n\t}\n\n\tconfig, err := pgxpool.ParseConfig(databaseURL)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"postgres: parse DATABASE_URL: %w\", err)\n\t}\n\tcaPEM, err := caPEMFromEnvironment()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif production && caPEM == \"\" {\n\t\treturn nil, fmt.Errorf(\"postgres: %s or %s is required in production\", caEnvironment, caFileEnvironment)\n\t}\n\tif caPEM == \"\" {\n\t\treturn config, nil\n\t}\n\troots := x509.NewCertPool()","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/postgresconfig/postgresconfig.go#L19-L55","documentation":"In production, ParsePoolConfig requires the DATABASE_URL query string to contain sslmode=verify-full. Without verify-full, lib/pq/pgx will encrypt but not authenticate the server, enabling machine-in-the-middle attacks; the library treats anything weaker as an invalid production configuration.","triggerScenarios":"Production call to NewPool/ParsePoolConfig with sslmode=require, prefer, disable, verify-ca, or with sslmode missing entirely from the URL query.","commonSituations":"Copy-pasting a connection string from a cloud dashboard that defaults to sslmode=require; migrating from staging where weaker sslmode was accepted; stripping query params when building the URL programmatically.","solutions":["Append sslmode=verify-full to the DATABASE_URL query string","Also configure the CA certificate (the next check requires a pinned CA) so verify-full can validate the server chain","If the database lacks a verifiable TLS certificate, fix the server cert instead of downgrading sslmode"],"exampleFix":"// before\nDATABASE_URL=\"postgres://app:secret@db.internal:5432/prod?sslmode=require\"\n// after\nDATABASE_URL=\"postgres://app:secret@db.internal:5432/prod?sslmode=verify-full&sslrootcert=/etc/certs/ca.pem\"","handlingStrategy":"validation","validationCode":"u, _ := url.Parse(databaseURL)\nif u.Query().Get(\"sslmode\") != \"verify-full\" {\n    return fmt.Errorf(\"production DATABASE_URL must set sslmode=verify-full\")\n}","typeGuard":"func hasVerifyFull(raw string) bool {\n    u, err := url.Parse(raw)\n    return err == nil && u.Query().Get(\"sslmode\") == \"verify-full\"\n}","tryCatchPattern":"cfg, err := postgresconfig.ParsePoolConfig(databaseURL)\nif err != nil && strings.Contains(err.Error(), \"sslmode=verify-full\") {\n    log.Fatal(\"production database requires sslmode=verify-full; refusing to start\")\n}","preventionTips":["Standardize production connection strings on ?sslmode=verify-full","Never copy staging connection strings into production manifests unreviewed","Add a CI/lint check that production secret templates include sslmode=verify-full","Provision server certificates that pass full verification so verify-full is viable"],"tags":["postgres","tls","production","sslmode"],"backgroundTag":"invalid-config-value","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}