Production Secrets & Environment Configuration
Production Secrets & Environment Configuration
Bloqr Compiler — Cloudflare Workers + Neon PostgreSQL + Better Auth
This document is the single source of truth for every secret, credential, and environment variable required to deploy and operate the bloqr-backend in production.
Table of Contents
- 1. Overview
- 2. Cloudflare Worker Secrets (REQUIRED before deploy)
- 3. Cloudflare Hyperdrive Binding
- 4. GitHub Actions Secrets
- 5. wrangler.toml [vars] (Non-Sensitive)
- 6. Local Development Environment Files
- 7. Secret Rotation Policy
- 8. Security Checklist (Pre-Deploy)
- 9. Troubleshooting
1. Overview
The bloqr-backend separates sensitive credentials from non-sensitive configuration across four distinct scopes:
flowchart TD
subgraph Production["Production Environment"]
WS["Cloudflare Worker Secrets\n(wrangler secret put)"]
WV["wrangler.toml [vars]\n(committed — non-sensitive)"]
HY["Hyperdrive Binding\n(wrangler.toml — connection ID only)"]
end
subgraph CI["CI/CD"]
GH["GitHub Actions Secrets\n(Settings → Secrets)"]
end
subgraph Local["Local Development"]
DV[".dev.vars\n(Worker runtime — gitignored)"]
EL[".env.local\n(Prisma CLI / shell — gitignored)"]
NB["Neon dev branch\n(personal, isolated)"]
end
subgraph External["External Services"]
NE["Neon PostgreSQL"]
CL["Clerk (deprecated)"]
CF["Cloudflare Dashboard"]
SE["Sentry"]
end
WS -->|"runtime env"| Worker["Cloudflare Worker"]
WV -->|"runtime env"| Worker
HY -->|"connection pool"| NE
GH -->|"CI env"| Actions["GitHub Actions"]
Actions -->|"migrations"| NE
DV -->|"wrangler dev"| LocalWorker["Local Worker"]
EL -->|"prisma cli"| NB
DV -->|"CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE"| NB
style WS fill:#dc2626,color:#fff,stroke:#991b1b
style GH fill:#dc2626,color:#fff,stroke:#991b1b
style DV fill:#f59e0b,color:#000,stroke:#d97706
style EL fill:#f59e0b,color:#000,stroke:#d97706
style WV fill:#22c55e,color:#000,stroke:#16a34a
style HY fill:#22c55e,color:#000,stroke:#16a34a
Key principles:
- Worker Secrets (
wrangler secret put) — encrypted at rest, injected at runtime, never visible in dashboards after creation. Used for all sensitive values. - wrangler.toml
[vars]— committed to source control. Only non-sensitive values (public keys, site keys, feature flags). - GitHub Actions Secrets — encrypted repository secrets for CI/CD pipelines (migrations, deployments, Docker publishing).
- Local env files —
.dev.vars(Worker runtime viawrangler dev) and.env.local(Prisma CLI, Deno tasks). Both are gitignored.
⚠️ Zero Trust rule: Secrets MUST NEVER appear in
wrangler.toml [vars], committed.envfiles, or CI workflow logs. Every secret is set viawrangler secret putor GitHub repository settings.
2. Cloudflare Worker Secrets (REQUIRED before deploy)
Every secret below must be set before the first production deployment. The Worker will fail to start or will reject requests if any required secret is missing.
Core Authentication Secrets
| Secret | Command | Source | Required |
|---|---|---|---|
BETTER_AUTH_SECRET | wrangler secret put BETTER_AUTH_SECRET | openssl rand -base64 32 | ✅ Yes |
TURNSTILE_SECRET_KEY | wrangler secret put TURNSTILE_SECRET_KEY | Cloudflare Dashboard → Turnstile → Site → Settings | ✅ Yes |
ADMIN_KEY | wrangler secret put ADMIN_KEY | openssl rand -base64 48 | ✅ Yes |
Clerk Secrets (Transition Period)
| Secret | Command | Source | Required |
|---|---|---|---|
CLERK_SECRET_KEY | wrangler secret put CLERK_SECRET_KEY | Clerk Dashboard → API Keys → Secret keys | ✅ Yes (until Clerk fully removed) |
CLERK_WEBHOOK_SECRET | wrangler secret put CLERK_WEBHOOK_SECRET | Clerk Dashboard → Webhooks → Signing Secret | ✅ Yes (until Clerk fully removed) |
Note: Clerk is deprecated and being replaced by Better Auth. Once the migration is complete, set
DISABLE_CLERK_FALLBACK=truein[vars]and remove Clerk secrets.
Cloudflare Access Secrets
| Secret | Command | Source | Required |
|---|---|---|---|
CF_ACCESS_AUD | wrangler secret put CF_ACCESS_AUD | Cloudflare Zero Trust → Access → Applications → Application Audience (AUD) Tag | ✅ Yes (if CF Access protects /admin/*) |
CF_ACCESS_CLIENT_ID | wrangler secret put CF_ACCESS_CLIENT_ID | Cloudflare Zero Trust → Access → Service Tokens | ⚠️ Conditional |
CF_ACCESS_CLIENT_SECRET | wrangler secret put CF_ACCESS_CLIENT_SECRET | Cloudflare Zero Trust → Access → Service Tokens | ⚠️ Conditional |
CORS Configuration
| Secret | Command | Source | Required |
|---|---|---|---|
CORS_ALLOWED_ORIGINS | wrangler secret put CORS_ALLOWED_ORIGINS | Comma-separated origin allowlist | ✅ Yes |
Why is CORS a secret? The production origin allowlist may include internal domains that should not be committed to source control. In development, set this in
.dev.varsinstead.
Observability Secrets
| Secret | Command | Source | Required |
|---|---|---|---|
SENTRY_DSN | wrangler secret put SENTRY_DSN | Sentry → Project Settings → Client Keys (DSN) | ⚠️ Recommended |
ANALYTICS_ACCOUNT_ID | wrangler secret put ANALYTICS_ACCOUNT_ID | Cloudflare Dashboard → Account ID | ⚠️ Recommended |
ANALYTICS_API_TOKEN | wrangler secret put ANALYTICS_API_TOKEN | Cloudflare Dashboard → API Tokens | ⚠️ Recommended |
OTEL_EXPORTER_OTLP_ENDPOINT | wrangler secret put OTEL_EXPORTER_OTLP_ENDPOINT | OpenTelemetry collector endpoint URL | ❌ Optional |
Integration Secrets
| Secret | Command | Source | Required |
|---|---|---|---|
CONTAINER_SECRET | wrangler secret put CONTAINER_SECRET | Shared secret for container service auth | ⚠️ If using containers |
WEBHOOK_URL | wrangler secret put WEBHOOK_URL | External webhook endpoint | ❌ Optional |
DATADOG_API_KEY | wrangler secret put DATADOG_API_KEY | Datadog → Organization Settings → API Keys | ❌ Optional |
NEON_API_KEY | wrangler secret put NEON_API_KEY | Neon Console → Account Settings → API Keys | ❌ Optional (runtime admin) |
Bulk Setup Script
#!/usr/bin/env bash# deploy-secrets.sh — Run once before first production deploy# ⚠️ DO NOT commit this script with values filled in
set -euo pipefail
echo "Setting Cloudflare Worker secrets for bloqr-backend..."
# Core authecho "<value>" | wrangler secret put BETTER_AUTH_SECRETecho "<value>" | wrangler secret put TURNSTILE_SECRET_KEYecho "<value>" | wrangler secret put ADMIN_KEY
# Clerk (transition period)echo "<value>" | wrangler secret put CLERK_SECRET_KEYecho "<value>" | wrangler secret put CLERK_WEBHOOK_SECRET
# Cloudflare Accessecho "<value>" | wrangler secret put CF_ACCESS_AUD
# CORSecho "https://your-domain.com,https://app.your-domain.com" | wrangler secret put CORS_ALLOWED_ORIGINS
# Observabilityecho "<value>" | wrangler secret put SENTRY_DSN
echo "✅ All secrets set. Deploy with: wrangler deploy"3. Cloudflare Hyperdrive Binding
Hyperdrive accelerates PostgreSQL connections by pooling and caching at the Cloudflare edge. It is configured as a binding in wrangler.toml, not as a secret.
Production Configuration (wrangler.toml)
[[hyperdrive]]binding = "HYPERDRIVE"id = "800f7e2edc86488ab24e8621982e9ad7"How It Works
sequenceDiagram
participant Client
participant Worker as Cloudflare Worker
participant HD as Hyperdrive<br/>(Connection Pool)
participant Neon as Neon PostgreSQL<br/>(Pooler Endpoint)
Client->>Worker: POST /compile
Worker->>Worker: Authenticate request
Worker->>HD: SQL query via env.HYPERDRIVE
HD->>HD: Check connection pool
alt Connection available
HD->>Neon: Reuse pooled connection
else No connection
HD->>Neon: Establish new connection<br/>(via Neon pooler endpoint)
end
Neon-->>HD: Query result
HD-->>Worker: Result
Worker-->>Client: 200 OK
Connection String Architecture
| Endpoint | Format | Used By |
|---|---|---|
| Neon Pooler (port 5432) | postgresql://user:pass@ep-xxx-pooler.region.aws.neon.tech/neondb?sslmode=require | Hyperdrive binding (production Worker runtime) |
Neon Direct (port 5432, no -pooler) | postgresql://user:pass@ep-xxx.region.aws.neon.tech/neondb?sslmode=require | Prisma migrations only (DIRECT_DATABASE_URL) |
⚠️ Hyperdrive MUST point to the Neon pooler endpoint (hostname contains
-pooler). The direct endpoint bypasses connection pooling and will exhaust Neon’s connection limit under load.
Local Development Hyperdrive
In local development, wrangler dev cannot use the real Hyperdrive binding. Instead, point it
at your personal Neon development branch via .dev.vars:
# .dev.vars — use your personal Neon dev branch (direct connection, not pooled)# Create a branch at https://console.neon.tech → your project → Branches → New BranchCLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE=postgresql://<user>:<password>@<branch-host>.neon.tech/<dbname>?sslmode=requireSee Local Development Setup for full instructions.
4. GitHub Actions Secrets
These secrets are configured in GitHub → Repository Settings → Secrets and variables → Actions.
Required Secrets
| Secret | Source | Used By |
|---|---|---|
CLOUDFLARE_API_TOKEN | Cloudflare Dashboard → API Tokens (use Edit Cloudflare Workers template) | wrangler deploy, migrations, workflow dispatches |
CLOUDFLARE_ACCOUNT_ID | Cloudflare Dashboard → Overview → Account ID | All Cloudflare API calls |
DIRECT_DATABASE_URL | Neon Console → Connection Details → Direct (non-pooler) connection string | db-migrate.yml — Prisma migrations |
NEON_API_KEY | Neon Console → Account Settings → API Keys | neon-branch-*.yml — branch creation/deletion |
NEON_PROJECT_ID | twilight-river-73901472 | Neon branch workflows |
SENTRY_AUTH_TOKEN | Sentry → Settings → Auth Tokens | sentry-sourcemaps.yml — source map uploads |
DOCKERHUB_USERNAME | Docker Hub account username | docker-publish.yml |
DOCKERHUB_TOKEN | Docker Hub → Account Settings → Security → Access Tokens | docker-publish.yml |
Optional / Feature-Specific Secrets
| Secret | Source | Used By |
|---|---|---|
CF_WEB_ANALYTICS_TOKEN | Cloudflare Dashboard → Web Analytics | lighthouse.yml |
CLAUDE_CODE_OAUTH_TOKEN | Anthropic / Claude integration | claude.yml — AI-assisted workflows |
Automatically Provided
| Secret | Source | Notes |
|---|---|---|
GITHUB_TOKEN | GitHub Actions (automatic) | Scoped to the repository; used for PR comments, artifact uploads, etc. |
Setting Secrets via GitHub CLI
# Bulk-set required secretsgh secret set CLOUDFLARE_API_TOKEN --body "<token>"gh secret set CLOUDFLARE_ACCOUNT_ID --body "<account-id>"gh secret set DIRECT_DATABASE_URL --body "postgresql://user:pass@ep-xxx.region.aws.neon.tech/neondb?sslmode=require"gh secret set NEON_API_KEY --body "<neon-api-key>"gh secret set NEON_PROJECT_ID --body "twilight-river-73901472"gh secret set SENTRY_AUTH_TOKEN --body "<sentry-token>"gh secret set DOCKERHUB_USERNAME --body "<username>"gh secret set DOCKERHUB_TOKEN --body "<token>"5. wrangler.toml [vars] (Non-Sensitive)
These values are committed to source control in wrangler.toml under the [vars] block. They contain no secrets — only public keys, feature flags, and configuration.
[vars]COMPILER_VERSION = "0.71.0"ENVIRONMENT = "production"TURNSTILE_SITE_KEY = "0x4AAAAAACMKiPZdJh8I8IEM"Additional Non-Sensitive Variables
These may be set in [vars] or as Worker secrets depending on your security posture:
| Variable | Example Value | Notes |
|---|---|---|
COMPILER_VERSION | "0.71.0" | Bloqr compiler version string |
ENVIRONMENT | "production" | Runtime environment identifier |
TURNSTILE_SITE_KEY | "0x4AAAAAACMKiPZdJh8I8IEM" | Public Turnstile site key (safe to commit) |
BETTER_AUTH_URL | "https://your-domain.com" | Public URL for Better Auth callbacks |
CLERK_PUBLISHABLE_KEY | "pk_live_..." | Clerk public key (safe to commit) |
CLERK_JWKS_URL | "https://your-clerk.clerk.accounts.dev/.well-known/jwks.json" | Public JWKS endpoint |
MAX_REQUEST_BODY_MB | "10" | Request body size limit |
ERROR_REPORTER_TYPE | "composite" | Error reporting backend |
DISABLE_CLERK_FALLBACK | "true" | Disable Clerk JWT fallback |
⚠️ Never put these in
[vars]:BETTER_AUTH_SECRET,CLERK_SECRET_KEY,TURNSTILE_SECRET_KEY,ADMIN_KEY,SENTRY_DSN,CORS_ALLOWED_ORIGINS, database credentials, or any API token.
6. Local Development Environment Files
Local development uses two separate env files to mirror the production split between Worker runtime and shell tooling. Both files use your personal Neon dev branch as the database (no local Docker PostgreSQL needed).
flowchart LR
subgraph Templates["Templates (committed)"]
A[".env.example"]
B[".dev.vars.example"]
end
subgraph Local["Local Files (gitignored)"]
C[".env.local"]
D[".dev.vars"]
end
subgraph Consumers["Consumers"]
E["Prisma CLI\nDeno Tasks\nShell Scripts"]
F["wrangler dev\n(Worker runtime)"]
end
subgraph Neon["Neon (Cloud)"]
NB["dev/yourname branch\n(personal, isolated)"]
end
A -->|"cp"| C
B -->|"cp"| D
C --> E
D --> F
E -->|"DIRECT_DATABASE_URL"| NB
F -->|"CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE"| NB
style C fill:#f59e0b,color:#000
style D fill:#f59e0b,color:#000
Quick Start
# 1. Create a personal dev branch in the Neon Console (one-time)# https://console.neon.tech → bloqr-backend project → Branches → New Branch
# 2. Copy templatescp .env.example .env.localcp .dev.vars.example .dev.vars
# 3. Fill in your Neon branch connection strings# .env.local → DATABASE_URL, DIRECT_DATABASE_URL# .dev.vars → CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE, BETTER_AUTH_SECRET, etc.
# 4. Run Prisma migrations against your Neon branchdeno task db:migrate
# 5. Start the Worker in dev modedeno task wrangler:dev.env.local — Shell Tooling
Used by Prisma CLI, Deno tasks, and any script that runs outside the Worker runtime. Point these at your personal Neon dev branch (direct connection, not pooled).
# Neon dev branch — direct connection (required by Prisma CLI and migrations)DATABASE_URL="postgresql://<user>:<password>@<branch-host>.neon.tech/<dbname>?sslmode=require"DIRECT_DATABASE_URL="postgresql://<user>:<password>@<branch-host>.neon.tech/<dbname>?sslmode=require"
# Neon project API access (optional — for scripts that call the Neon REST API)# NEON_API_KEY="your-neon-api-key"# NEON_PROJECT_ID="twilight-river-73901472"
# Cloudflare API (optional — for scripts that call CF API)# CLOUDFLARE_API_TOKEN="your-api-token"# CLOUDFLARE_ACCOUNT_ID="your-account-id".dev.vars — Worker Runtime
Used by wrangler dev to inject environment bindings into the local Worker.
# Better Auth (primary auth)BETTER_AUTH_SECRET="local-dev-secret-change-in-production"BETTER_AUTH_URL="http://localhost:8787"
# Turnstile (use Cloudflare test keys for local dev)TURNSTILE_SITE_KEY="1x00000000000000000000AA"TURNSTILE_SECRET_KEY="1x0000000000000000000000000000000AA"
# CORSCORS_ALLOWED_ORIGINS="http://localhost:4200,http://localhost:8787"
# Hyperdrive local override — point at your personal Neon dev branch# Create a branch at https://console.neon.tech → your project → Branches → New BranchCLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE="postgresql://<user>:<password>@<branch-host>.neon.tech/<dbname>?sslmode=require"
# Clerk (deprecated — use test keys or disable)CLERK_PUBLISHABLE_KEY="pk_test_..."CLERK_SECRET_KEY="sk_test_..."DISABLE_CLERK_FALLBACK="true"See Local Development Setup for full Neon branching instructions.
7. Secret Rotation Policy
Rotation Schedule
| Secret | Rotation Interval | Impact of Rotation | Procedure |
|---|---|---|---|
BETTER_AUTH_SECRET | Every 90 days | All active sessions invalidated; users must re-authenticate | See Rotating BETTER_AUTH_SECRET |
ADMIN_KEY | Every 90 days | All admin scripts using the old key will fail | See Rotating ADMIN_KEY |
TURNSTILE_SECRET_KEY | On compromise only | Turnstile verification fails until frontend is updated (if site key changes) | See Rotating Turnstile Keys |
CLERK_SECRET_KEY | On compromise only | Clerk webhook verification fails; JWT validation fails | See Rotating Clerk Keys |
CF_ACCESS_AUD | On application recreation only | CF Access tokens rejected until new AUD is deployed | Redeploy with new AUD |
| Neon DB credentials | Managed by Neon | Hyperdrive connection string must be updated | Rotate via Neon Dashboard → Project Settings → Reset Password |
SENTRY_DSN | On compromise only | Error reporting interrupted during transition | Update secret and redeploy |
Rotating BETTER_AUTH_SECRET
# 1. Generate a new secretNEW_SECRET=$(openssl rand -base64 32)
# 2. Set the new secret (Worker will pick it up on next request)echo "$NEW_SECRET" | wrangler secret put BETTER_AUTH_SECRET
# 3. Trigger a deployment to ensure all edge instances reloadwrangler deploy
# 4. Verify — all existing sessions will require re-authenticationcurl -s https://your-domain.com/auth/session \ -H "Cookie: better-auth.session_token=old-token" \ | jq .error# Expected: session invalid or unauthorized⚠️ Session impact: Rotating
BETTER_AUTH_SECRETinvalidates all active sessions. Plan rotations during low-traffic windows and notify users if applicable.
Rotating ADMIN_KEY
# 1. Generate a new keyNEW_KEY=$(openssl rand -base64 48)
# 2. Set the new keyecho "$NEW_KEY" | wrangler secret put ADMIN_KEY
# 3. Redeploywrangler deploy
# 4. Update all admin scripts and CI jobs that use the old key
# 5. Verifycurl -s https://your-domain.com/admin/health \ -H "X-Admin-Key: $NEW_KEY" \ | jq .status# Expected: "ok"Rotating Turnstile Keys
# 1. Go to Cloudflare Dashboard → Turnstile → Site → Settings# 2. Regenerate the secret key (site key may remain unchanged)
# 3. Set the new secretecho "<new-secret>" | wrangler secret put TURNSTILE_SECRET_KEY
# 4. If the SITE KEY also changed, update wrangler.toml [vars]# TURNSTILE_SITE_KEY = "<new-site-key>"
# 5. Redeploywrangler deploy
# 6. If site key changed, also update the Angular frontend and redeploy itRotating Clerk Keys
# 1. Go to Clerk Dashboard → API Keys → Roll Key# 2. Clerk provides a grace period where both old and new keys work
# 3. Set the new secretecho "<new-secret>" | wrangler secret put CLERK_SECRET_KEY
# 4. If webhook secret changed:echo "<new-webhook-secret>" | wrangler secret put CLERK_WEBHOOK_SECRET
# 5. Redeploywrangler deployRotating Neon Database Credentials
# 1. Go to Neon Console → Project → Settings → Reset Password# 2. Copy the new connection string
# 3. Update Hyperdrive configurationwrangler hyperdrive update 800f7e2edc86488ab24e8621982e9ad7 \ --origin-url "postgresql://user:NEW_PASS@ep-xxx-pooler.region.aws.neon.tech/neondb?sslmode=require"
# 4. Update GitHub Actions secret for migrationsgh secret set DIRECT_DATABASE_URL \ --body "postgresql://user:NEW_PASS@ep-xxx.region.aws.neon.tech/neondb?sslmode=require"
# 5. Redeploy Worker to pick up Hyperdrive changeswrangler deploy8. Security Checklist (Pre-Deploy)
Complete this checklist before every production deployment:
Worker Secrets
- All required Worker secrets set via
wrangler secret put(see Section 2) - No secrets present in
wrangler.toml [vars]block -
BETTER_AUTH_SECRETis at least 32 characters of cryptographic randomness -
ADMIN_KEYis at least 48 characters of cryptographic randomness
Database & Hyperdrive
- Hyperdrive connection string points to Neon pooler endpoint (hostname contains
-pooler) -
DIRECT_DATABASE_URL(GitHub Actions) uses Neon direct endpoint (for migrations only) - All D1 queries use parameterized statements (
.prepare().bind()) - No raw SQL string interpolation anywhere in
worker/code
Authentication & Authorization
- Better Auth cookie security:
httpOnly: true,secure: true,sameSite: "lax" - Turnstile verification enabled on all write endpoints (
/compile*,/validate,/ast/parse) - Rate limiting configured per auth tier (anonymous, free, pro, admin)
- Auth chain verified: API Key → Better Auth → Clerk JWT → Anonymous
- Admin routes (
/admin/*) protected by CF Access and/or admin role middleware
Network & CORS
-
CORS_ALLOWED_ORIGINSuses an explicit origin allowlist (not*) - No
Access-Control-Allow-Origin: *on authenticated or write endpoints - SSRF protection active on
/proxy/fetch(blocks RFC 1918, localhost,169.254.169.254)
CI/CD
- All GitHub Actions secrets configured (see Section 4)
- CI pipeline passes: lint, typecheck, unit tests, integration tests
- Sentry source maps uploaded on deploy
Observability
- Security events emitted to Analytics Engine on auth failures via
AnalyticsService.trackSecurityEvent() - Sentry DSN configured for error tracking
- Health monitoring workflow active
9. Troubleshooting
AUTH_SECRET_MISSING / BETTER_AUTH_SECRET is not configured
Cause: The BETTER_AUTH_SECRET Worker secret is not set or the Worker hasn’t been redeployed since it was set.
# Verify the secret exists (shows last 4 characters)wrangler secret list
# Re-set if missingopenssl rand -base64 32 | wrangler secret put BETTER_AUTH_SECRET
# Redeploywrangler deployHYPERDRIVE connection failed / Connection refused
Cause: Hyperdrive cannot reach the Neon PostgreSQL endpoint.
# 1. Verify Hyperdrive configwrangler hyperdrive get 800f7e2edc86488ab24e8621982e9ad7
# 2. Check that the origin URL uses the POOLER endpoint# ✅ ep-xxx-pooler.region.aws.neon.tech# ❌ ep-xxx.region.aws.neon.tech (direct — will exhaust connections)
# 3. Test Neon connectivity directlypsql "postgresql://user:pass@ep-xxx-pooler.region.aws.neon.tech/neondb?sslmode=require" \ -c "SELECT 1"
# 4. If Neon password expired, rotate credentials (see Section 7)Neon password expired / authentication failed
Cause: Neon auto-expires credentials after the project’s configured password lifetime.
# 1. Go to Neon Console → Project → Settings → Reset Password# 2. Update Hyperdrive with the new connection stringwrangler hyperdrive update 800f7e2edc86488ab24e8621982e9ad7 \ --origin-url "postgresql://user:NEW_PASS@ep-xxx-pooler.region.aws.neon.tech/neondb?sslmode=require"
# 3. Update GitHub Actions migration secretgh secret set DIRECT_DATABASE_URL \ --body "postgresql://user:NEW_PASS@ep-xxx.region.aws.neon.tech/neondb?sslmode=require"
# 4. Redeploywrangler deployTurnstile verification failed / invalid-input-response
Cause: The Turnstile secret key doesn’t match the site key, or the token has expired.
# 1. Verify site key in wrangler.toml matches the Cloudflare Dashboardgrep TURNSTILE_SITE_KEY wrangler.toml
# 2. Verify secret key is setwrangler secret list | grep TURNSTILE
# 3. Test with Cloudflare's always-pass test keys (local dev only):# Site key: 1x00000000000000000000AA# Secret key: 1x0000000000000000000000000000000AACORS error: Origin not allowed
Cause: The request origin is not in the CORS_ALLOWED_ORIGINS allowlist.
# 1. Check what origins are currently allowed# (Secret — can't view directly; re-set with the correct list)
# 2. Re-set with updated origin listecho "https://your-domain.com,https://app.your-domain.com" | \ wrangler secret put CORS_ALLOWED_ORIGINS
# 3. Redeploywrangler deployCF Access: 403 Forbidden on /admin/*
Cause: The Cloudflare Access JWT is missing, expired, or the CF_ACCESS_AUD secret doesn’t match.
# 1. Verify CF Access is configured for the route# Cloudflare Zero Trust → Access → Applications
# 2. Verify the AUD tag matcheswrangler secret list | grep CF_ACCESS
# 3. Re-set if neededecho "<aud-tag-from-dashboard>" | wrangler secret put CF_ACCESS_AUD
# 4. Ensure your browser has a valid CF Access cookie# Visit the Access-protected URL directly to trigger the auth flowClerk JWT verification failed (deprecated)
Cause: Clerk JWKS endpoint unreachable or secret key mismatch.
# 1. If Clerk is disabled, verify the flag:grep DISABLE_CLERK_FALLBACK wrangler.toml# Should be: DISABLE_CLERK_FALLBACK = "true"
# 2. If Clerk is still active, verify the JWKS URL is reachable:curl -s "https://your-clerk.clerk.accounts.dev/.well-known/jwks.json" | jq .keys
# 3. Re-set the secret if neededecho "<clerk-secret>" | wrangler secret put CLERK_SECRET_KEYGitHub Actions: Migration failed
Cause: The DIRECT_DATABASE_URL secret is incorrect, expired, or uses the pooler endpoint.
# 1. Verify the secret uses the DIRECT (non-pooler) endpoint:# ✅ ep-xxx.region.aws.neon.tech (direct)# ❌ ep-xxx-pooler.region.aws.neon.tech (pooler — breaks migrations)
# 2. Test the connection locally:psql "$DIRECT_DATABASE_URL" -c "SELECT 1"
# 3. Re-set in GitHub Actions:gh secret set DIRECT_DATABASE_URL \ --body "postgresql://user:pass@ep-xxx.region.aws.neon.tech/neondb?sslmode=require"Local Development: wrangler dev can’t connect to PostgreSQL
Cause: .dev.vars is missing or has the wrong connection string for your Neon dev branch.
# 1. Verify .dev.vars connection stringgrep HYPERDRIVE .dev.vars# Should be: CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE="postgresql://<user>:<password>@<branch-host>.neon.tech/<dbname>?sslmode=require"
# 2. Test the connection directly (replace placeholders with your Neon branch values)psql "postgresql://<user>:<password>@<branch-host>.neon.tech/<dbname>?sslmode=require" -c "SELECT 1"
# 3. Check that the Neon branch is active (not suspended) in the Neon Console# https://console.neon.tech → your project → BranchesSee Local Development Setup for full Neon branching instructions.
Last updated: 2025 · Maintainer: bloqr-backend team
This document is part of the Zero Trust Architecture compliance requirements. Every secret and credential listed here must follow the ZTA principles defined in the project’s security policy.