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
_PRODUCTIONsuffix (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_PRODUCTIONSTRIPE_SECRET_KEY_STAGINGSUPABASE_DB_PASSWORD_PRODUCTIONSUPABASE_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
- ✅ Update GitHub Actions workflows to use correct secret names
- ✅ Update
.github/workflows/deploy-supabase.ymlwith proper references - ✅ Add conditional checks for optional secrets
- Audit all GitHub secrets and rename as needed
- Update all environment variable documentation
- Update local development setup guides
- 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
- GitHub Actions Encrypted Secrets
- Supabase Edge Functions Secrets
- Issue: Empty secrets in Edge Functions due to naming mismatch