Skip to main content

Source: ocean/docs/adr/0036-secret-naming-conventions.md | ✏️ Edit on GitHub

ADR-0036: Secret Naming Conventions

Status

Accepted

Context

Our application uses multiple secrets across different environments (production, staging) and services (Supabase, GitHub Actions, Vercel). We discovered that inconsistent naming between GitHub secrets and deployment workflows caused Edge Functions to receive empty secrets, leading to service failures.

Specifically:

  • GitHub secrets were stored with _PRODUCTION suffix (e.g., STRIPE_SECRET_KEY_PRODUCTION)
  • Deployment workflows referenced them without the suffix (e.g., STRIPE_SECRET_KEY)
  • This mismatch resulted in empty secrets being set in Supabase Edge Functions

Decision

We will adopt a consistent naming convention for all secrets across the application:

1. Environment-Specific Suffixes

All environment-specific secrets MUST include an environment suffix:

  • Production: _PRODUCTION
  • Staging: _STAGING
  • Development: _DEVELOPMENT (if needed)

Examples:

  • STRIPE_SECRET_KEY_PRODUCTION
  • STRIPE_SECRET_KEY_STAGING
  • SUPABASE_DB_PASSWORD_PRODUCTION
  • SUPABASE_DB_PASSWORD_STAGING

2. Public/Client-Side Keys

Public keys that are safe for client-side use should use descriptive prefixes:

  • Supabase: VITE_SUPABASE_PUBLISHABLE_KEY_[ENVIRONMENT]
  • Stripe: VITE_STRIPE_PUBLISHABLE_KEY_[ENVIRONMENT]
  • Analytics: VITE_[SERVICE]_KEY_[ENVIRONMENT]

3. Service-Specific Prefixes

Secrets should be prefixed with the service name:

  • Stripe: STRIPE_*
  • Supabase: SUPABASE_*
  • Neon: NEON_*
  • PostHog: POSTHOG_*
  • Sentry: SENTRY_*

4. GitHub Actions Secrets

GitHub Actions secrets must follow the exact naming convention:

# Correct
STRIPE_SECRET_KEY_PRODUCTION
STRIPE_WEBHOOK_SECRET_PRODUCTION

# Incorrect
STRIPE_SECRET_KEY
STRIPE_WEBHOOK_SECRET

5. Deployment Workflows

Deployment workflows must reference secrets with their full names:

# Correct
supabase secrets set STRIPE_SECRET_KEY="${{ secrets.STRIPE_SECRET_KEY_PRODUCTION }}"

# Incorrect
supabase secrets set STRIPE_SECRET_KEY="${{ secrets.STRIPE_SECRET_KEY }}"

6. Optional Secrets

Optional secrets should be conditionally set in workflows:

if [ -n "${{ secrets.STRIPE_FREE_PRICE_ID }}" ]; then
supabase secrets set STRIPE_FREE_PRICE_ID="${{ secrets.STRIPE_FREE_PRICE_ID }}"
fi

Consequences

Positive

  • Clarity: Environment is immediately clear from the secret name
  • Prevention: Reduces risk of using wrong environment secrets
  • Debugging: Easier to identify secret-related issues
  • Consistency: Uniform naming across all services
  • Safety: Less chance of exposing production secrets in wrong environment

Negative

  • Verbosity: Longer secret names
  • Migration: Existing secrets need to be renamed
  • Updates: All workflows and documentation need updating

Implementation Checklist

  1. ✅ Update GitHub Actions workflows to use correct secret names
  2. ✅ Update .github/workflows/deploy-supabase.yml with proper references
  3. ✅ Add conditional checks for optional secrets
  4. Audit all GitHub secrets and rename as needed
  5. Update all environment variable documentation
  6. Update local development setup guides
  7. Create migration script for team members

Examples

GitHub Secrets Structure

Production:
├── STRIPE_SECRET_KEY_PRODUCTION
├── STRIPE_WEBHOOK_SECRET_PRODUCTION
├── STRIPE_FREE_PRICE_ID (optional)
├── SUPABASE_ACCESS_TOKEN
├── SUPABASE_DB_PASSWORD_PRODUCTION
├── SUPABASE_PROJECT_ID_PRODUCTION
├── NEON_API_KEY
├── POSTHOG_API_KEY
├── POSTHOG_PROJECT_API_KEY (optional)
└── SENTRY_DSN

Staging:
├── STRIPE_SECRET_KEY_STAGING
├── STRIPE_WEBHOOK_SECRET_STAGING
├── SUPABASE_DB_PASSWORD_STAGING
└── SUPABASE_PROJECT_ID_STAGING

Workflow Usage

env:
SUPABASE_PROJECT_ID: ${{ secrets.SUPABASE_PROJECT_ID_PRODUCTION }}

steps:
- name: Set Edge Function secrets
run: |
supabase secrets set STRIPE_SECRET_KEY="${{ secrets.STRIPE_SECRET_KEY_PRODUCTION }}"
supabase secrets set STRIPE_WEBHOOK_SECRET="${{ secrets.STRIPE_WEBHOOK_SECRET_PRODUCTION }}"

References