Secret Management
Secret Management — Pulumi ESC + GCP Secret Manager
All application secrets — third-party API keys, webhook signatures, database connection strings, auth signing keys — live in GCP Secret Manager (the runtime store, inside the Google BAA), with Pulumi ESC (Environments, Secrets, and Configuration) as the declarative source of truth. There is no database-resident vault.
This replaces the older Supabase Vault design. With Supabase removed, a DB-resident vault is both unavailable (Neon does not support supabase_vault / pgsodium) and undesirable — DB-resident secrets would be copied into every preview branch by branch-per-PR, exactly the wrong direction.
Core Philosophy: Declarative Source of Truth → IAM-Scoped Runtime Store
- Pulumi ESC holds the declarative definition of every environment's config + secrets, alongside the Pulumi IaC we already own. It can also mint short-lived, dynamic credentials rather than storing static ones.
- GCP Secret Manager is the runtime store: covered by the Google BAA, IAM-scoped, and natively integrated with Cloud Run (secrets mounted as env vars or files at deploy).
Pulumi ESC (declarative — source of truth)
→ materialize → GCP Secret Manager (runtime — Google BAA, IAM-scoped)
→ mounted into Cloud Run at deploy time
How materialization works
"Materialize" is a concrete Pulumi step, not hand-copying. The Pulumi program in infra/ imports the ESC environment and, for each declared secret, creates a gcp.secretmanager.Secret + SecretVersion resource whose value comes from ESC:
// infra/ — conceptual: ESC env → Secret Manager resources
const env = esc.getEnvironment({ organization, project, environment: "prod" });
for (const [name, value] of Object.entries(env.secrets)) {
const secret = new gcp.secretmanager.Secret(name, { secretId: name, replication: { auto: {} } });
new gcp.secretmanager.SecretVersion(`${name}-v`, { secret: secret.id, secretData: value });
}So ESC is the declarative source of truth, the Pulumi run materializes it into Secret Manager (the runtime store), and Cloud Run mounts the resulting Secret Manager versions at deploy. Rotating a value in ESC and re-running Pulumi adds a new SecretVersion; the old version is destroyed after cutover.
Mount secrets into Cloud Run at deploy, not fetched per request. Destroy old Secret Manager versions after a rotation cuts over.
What lives here
- Neon per-branch
DATABASE_URLs - Better Auth secrets / JWT signing keys
- Vendor API keys (Telnyx, Sanity, etc.)
- OAuth client secrets
- Per-PR preview secrets (minted in CI, destroyed with the Neon branch)
Local Development
-
Local secrets go in a git-ignored
.envfile at the repo root. -
Hydrate it from Pulumi ESC so local matches the declared shape without hand-copying:
pulumi env open <project>/dev --format dotenv > .env # or: esc run <env> -- <cmd>
The .env file must be listed in .gitignore. Never commit local secrets.
Production & Preview
- Production secrets are declared in Pulumi ESC and materialized into GCP Secret Manager; Cloud Run mounts them at deploy. Rotate by adding a new Secret Manager version, then destroying the old one after cutover.
- Preview (per-PR) secrets — including the ephemeral Neon branch
DATABASE_URLand the branch-scoped Better Auth config — are minted in CI and torn down with the Neon branch on PR close (see PR Environments).
Requirements
- Per-env isolation:
prod/staging/preview-pr-*/dev. - Never log
DATABASE_URLor any secret value. PHI-adjacent keys stay inside the boundary. - Rotation + audit trail (HIPAA): Secret Manager versions + GCP Cloud Audit Logs record every access.
- Keyless CI: GitHub Actions authenticates to GCP via Workload Identity Federation — never store a GCP service-account JSON as a secret.
Quick Reference
| Concern | Local Development | Production / Preview |
|---|---|---|
| Source of truth | Pulumi ESC (pulumi env open) | Pulumi ESC |
| Runtime store | .env (git-ignored) | GCP Secret Manager (mounted into Cloud Run) |
Neon DATABASE_URL | local branch / local PG | Secret Manager (prod); minted per-PR, destroyed with the branch (preview) |
| Rotation | — | New Secret Manager version; destroy old after cutover |
Related Pages
- Environment Variables — Master env var reference including storage locations
- HIPAA Compliance — Vendor BAAs and technical controls
- Infrastructure — Pulumi IaC and GCP resource setup