Skip to main content

Environment Isolation & Verification

This runbook documents how all 6 Factory apps prevent environment mixups using layered configuration and runtime verification.

The Problem: Environment Mixups

Environment mixups happen when:
  • Developer deploys staging code to production
  • Production secrets leak into staging logs
  • Staging database hits production data
  • Wrong rate limiter namespace causes staging to block production users
  • Hyperdrive binding connects to wrong database
Root cause: Environments are not isolated at build/deployment/runtime layers.

The Solution: Layered Isolation

All 6 Factory apps use three layers of isolation to prevent mixups:

Layer 1: Build-Time — wrangler.jsonc Isolation

Each app has a unique wrangler.jsonc with environment-specific overrides:
Protection:
  • WORKER_NAME is hardcoded per environment (no typos, no .env fetch)
  • Hyperdrive ID is per-app (staging can’t accidentally query production DB)
  • Rate limiter namespace is unique per app (staging can’t block production users)

Layer 2: Deploy-Time — GitHub Actions Environment Secrets

Each environment (production, staging) has separate GitHub Actions environment with approval gates:
Protection:
  • Production secrets have approval gate (must explicitly authorize)
  • Staging secrets cannot access production database URL
  • If someone tries to deploy staging code to production, it gets staging secrets (still wrong, but caught at runtime)

Layer 3: Runtime — TypeScript Type Validation + Health Endpoint

Protection:
  • Any missing secret = compile error (caught in CI before deploy)
  • ENVIRONMENT is literal type (only ‘staging’ | ‘production’, no typos)
  • /health endpoint reveals current environment (can verify post-deploy)

Verification Workflow

After Deploy: Check /health Endpoint

Pre-Deploy: Verify wrangler env

CI/CD: Automated Environment Checks

Add this to .github/workflows/deploy.yml:

Anti-Pattern: What NOT to Do

❌ Environment Variable in wrangler.jsonc vars

Problem: Same value in both production and staging → Potential mixup

❌ Optional Environment Fields

Problem: Missing secret = silent failure, not compile error

❌ Environment Variables Read from .env at Runtime

Problem: Code works locally (Node.js) but breaks on Workers (no process object)

❌ Secrets in wrangler.jsonc

Problem: Secrets leak to GitHub, visible to anyone with repo access

Isolation Checklist

Before deploying any app, verify:

Configuration

  • wrangler.jsonc has "name": "{app-name}" or "{app-name}-staging" (not generic)
  • WORKER_NAME in vars matches the worker name (no typos)
  • Rate limiter namespace_id is unique per app (1001–1006)
  • Hyperdrive id is unique per app (verify in wrangler.jsonc)
  • env.staging overrides exist: ENVIRONMENT: "staging", WORKER_NAME: "{app}-staging"

Secrets

  • All required fields in src/env.ts are non-optional
  • No secrets in wrangler.jsonc vars section
  • All secrets set in GitHub Actions environment secrets (not repo secrets)
  • GitHub Actions workflow targets correct environment (environment: production or staging)

Deployment

  • Ran wrangler deploy --dry-run --env {env} and verified worker name
  • Confirmed correct branch (main = production, staging = staging branch)
  • Checked /health endpoint post-deploy to verify environment

Monitoring

  • Sentry is reporting errors to correct project (staging vs. production DSN)
  • PostHog is tracking events to correct workspace (staging vs. production key)
  • Logs show correct worker and environment values

Real-World Scenario: Staging → Production Mixup

Scenario: Developer merges staging code to main by mistake. Layer 1 Protection (Build-Time):
  • CI/CD runs on main branch
  • wrangler deploy reads wrangler.jsonc (which specifies name: wordis-bond, not wordis-bond-staging)
  • Code deploys to wordis-bond.adrper79.workers.dev (production URL)
  • Layer 1 failed → Staged code goes to production ❌
Layer 2 Protection (Deploy-Time):
  • GitHub Actions workflow checks environment: production
  • Secrets are from production environment (correct DB, correct Sentry)
  • Layer 2 passed → Code has production secrets ✓
Layer 3 Protection (Runtime):
  • App starts with c.env.ENVIRONMENT = 'production'
  • c.env.WORKER_NAME = 'wordis-bond'
  • /health endpoint returns worker: wordis-bond, environment: production
  • Layer 3 passed → No obvious sign of mixup ✓
Detection:
  • Monitor Sentry for surge in errors (staging code hitting production data)
  • Check PostHog for unexpected events (staging logic in production)
  • Developers immediately see via /health that production URL has production env
Root cause: Staging code should have never merged to main. Fix: use branch protection rules requiring PR review.

Environment Verification Script

Add this to each app’s scripts section in package.json:
Create scripts/verify-env.js:
Then run:

Summary

Result: Staging code can reach production, but:
  1. ✅ It uses production secrets (not staging)
  2. ✅ It’s visible at runtime (/health shows production env)
  3. ✅ Errors/events flow to correct monitoring (Sentry/PostHog)
This forces developers to notice the mixup immediately and prevents silent data corruption.

See Also