skills/phrazzld/claude-config/env-var-hygiene

env-var-hygiene

SKILL.md

Environment Variable Hygiene

Best practices for managing environment variables across deployment platforms.

Triggers

Invoke when user mentions:

  • "env var", "environment variable", "production deploy"
  • "webhook secret", "API key", "token"
  • "Invalid character in header", "ERR_INVALID_CHAR"
  • "silent failure", "webhook not working"
  • "Vercel", "Convex", "deployment", "secrets"

Core Principles

1. Trailing Whitespace Kills

Env vars with \n or trailing spaces cause cryptic errors:

  • "Invalid character in header content" (HTTP headers)
  • Webhook signature mismatch
  • Silent authentication failures

Root Cause: Copy-paste or echo introduces invisible characters.

Prevention:

# ✅ Use printf, not echo
printf '%s' 'sk_live_xxx' | vercel env add STRIPE_SECRET_KEY production

# ✅ Trim when setting
bunx convex env set --prod KEY "$(echo 'value' | tr -d '\n')"  # or: npx convex ...

# ❌ Don't use echo directly
echo "sk_live_xxx" | vercel env add KEY production  # May add \n

2. Cross-Platform Parity

Shared tokens (webhook secrets, auth tokens) must be identical across platforms:

  • Vercel ↔ Convex
  • Frontend ↔ Backend
  • Dev ↔ Staging ↔ Prod (within each platform)

Common Pitfall: Set token on one platform, forget the other.

Prevention:

# Generate token once
TOKEN=$(openssl rand -hex 32)

# Set on ALL platforms
bunx convex env set --prod CONVEX_WEBHOOK_TOKEN "$(printf '%s' "$TOKEN")"  # or: npx convex ...
printf '%s' "$TOKEN" | vercel env add CONVEX_WEBHOOK_TOKEN production

3. Validate Format Before Use

API keys have specific formats. Validate before deployment:

Service Pattern Example
Stripe Secret sk_(test|live)_[A-Za-z0-9]+ sk_live_xxx
Stripe Public pk_(test|live)_[A-Za-z0-9]+ pk_live_xxx
Stripe Webhook whsec_[A-Za-z0-9]+ whsec_xxx
Stripe Price price_[A-Za-z0-9]+ price_xxx
Clerk Secret sk_(test|live)_[A-Za-z0-9]+ sk_live_xxx

4. Dev ≠ Prod

Separate deployments have separate env var stores:

  • Setting .env.local doesn't affect production
  • Convex dev and prod are separate deployments
  • Vercel has per-environment variables

Always verify prod separately:

# Convex
bunx convex env list --prod  # or: npx convex ...

# Vercel
vercel env ls --environment=production

5. CLI Environment Gotcha

CONVEX_DEPLOYMENT=prod:xxx npx convex data may return dev data.

Always use explicit flags:

# ❌ Unreliable
CONVEX_DEPLOYMENT=prod:xxx npx convex data

# ✅ Reliable
bunx convex run --prod module:function  # or: npx convex ...
bunx convex env list --prod

6. Env Load Semantics (Restart Required)

Many runtimes load .env.local once at process start.

If code logs "missing X" but .env.local has X:

  • you edited .env.local after server started. Restart dev server.
  • your shell env overrides dotenv (incl empty string): printenv KEY then unset KEY.

Quick Reference

Setting Env Vars Safely

Convex:

# Dev
bunx convex env set KEY "value"  # or: npx convex ...

# Prod (use --prod flag)
bunx convex env set --prod KEY "$(printf '%s' 'value')"

Vercel:

# Production
printf '%s' 'value' | vercel env add KEY production

# With explicit environment
vercel env add KEY production --force

Checking Env Vars

Convex:

bunx convex env list           # dev
bunx convex env list --prod    # prod

Vercel:

vercel env ls                              # all
vercel env ls --environment=production     # prod only

Detecting Issues

Trailing whitespace:

# Check Convex prod
bunx convex env list --prod | while IFS= read -r line; do
  if [[ "$line" =~ [[:space:]]$ ]]; then
    echo "WARNING: $(echo "$line" | cut -d= -f1) has trailing whitespace"
  fi
done

Format validation:

# Validate Stripe key format
value=$(bunx convex env list --prod | grep "^STRIPE_SECRET_KEY=" | cut -d= -f2-)
[[ "$value" =~ ^sk_(test|live)_[A-Za-z0-9]+$ ]] || echo "Invalid format"

References

See references/ directory:

  • format-patterns.md - Regex patterns for common services
  • platform-specifics.md - Vercel, Convex, Railway platform details
  • hygiene-checklist.md - Pre-deployment validation checklist
  • parity-verification.md - Cross-platform token verification

Related Commands

  • /pre-deploy - Comprehensive pre-deployment checklist
  • /env-parity-check - Cross-platform token verification
  • /stripe-check - Stripe-specific environment audit

Based on 2026-01-17 incident: Trailing \n in STRIPE_SECRET_KEY caused "Invalid character in header" error. Token mismatch between Vercel and Convex caused silent webhook failures.

Weekly Installs
23
GitHub Stars
5
First Seen
Jan 27, 2026
Installed on
codex22
gemini-cli21
opencode21
codebuddy21
github-copilot21
continue21