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

env-var-hygiene skill

/skills/env-var-hygiene

This skill enforces safe environment variable hygiene across platforms by validating formats, trimming whitespace, and ensuring cross-platform parity.

npx playbooks add skill phrazzld/claude-config --skill env-var-hygiene

Review the files below or copy the command above to add this skill to your agents.

Files (3)
SKILL.md
4.6 KB
---
name: env-var-hygiene
description: "Environment variable management across Vercel, Convex, and other platforms. Invoke for: trailing whitespace issues, cross-platform parity, Invalid character errors, webhook secrets, API key management, production deployment, dev vs prod configuration."
effort: low
---

# 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:**
```bash
# ✅ Use printf, not echo
printf '%s' 'sk_live_xxx' | vercel env add STRIPE_SECRET_KEY production

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

# ❌ 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:**
```bash
# Generate token once
TOKEN=$(openssl rand -hex 32)

# Set on ALL platforms
npx convex env set --prod CONVEX_WEBHOOK_TOKEN "$(printf '%s' "$TOKEN")"
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:**
```bash
# Convex
npx convex env list --prod

# 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:**
```bash
# ❌ Unreliable
CONVEX_DEPLOYMENT=prod:xxx npx convex data

# ✅ Reliable
npx convex run --prod module:function
npx convex env list --prod
```

## Quick Reference

### Setting Env Vars Safely

**Convex:**
```bash
# Dev
npx convex env set KEY "value"

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

**Vercel:**
```bash
# Production
printf '%s' 'value' | vercel env add KEY production

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

### Checking Env Vars

**Convex:**
```bash
npx convex env list           # dev
npx convex env list --prod    # prod
```

**Vercel:**
```bash
vercel env ls                              # all
vercel env ls --environment=production     # prod only
```

### Detecting Issues

**Trailing whitespace:**
```bash
# Check Convex prod
npx 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:**
```bash
# Validate Stripe key format
value=$(npx 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.*

Overview

This skill helps teams manage environment variables across Vercel, Convex, and similar platforms to avoid deployment failures and silent authentication issues. It focuses on detecting trailing whitespace, ensuring cross-platform parity, validating formats, and guiding safe CLI usage. Use it to prevent webhook signature mismatches, invalid header errors, and dev/prod drift.

How this skill works

The skill inspects environment-variable values and platform settings to detect invisible characters, format mismatches, and missing tokens across environments. It provides concrete CLI patterns for safe writes (printf, explicit flags), parity checks (set the same token everywhere), and validation regexes for common services. It also surfaces platform-specific gotchas like unreliable env overrides and shows commands to list and verify production values.

When to use it

  • Before a production deployment or release
  • When webhooks or API requests fail with cryptic header errors
  • When migrating or rotating keys across multiple platforms
  • When setting up or auditing prod vs dev environment parity
  • After hands-on edits or copy-paste of secrets

Best practices

  • Always write secrets with printf or explicit trimming to avoid trailing newlines or spaces.
  • Generate a single token and set it on every platform (Vercel, Convex, frontend, backend) to preserve parity.
  • Validate keys locally against known patterns before deploying (e.g., Stripe, Clerk formats).
  • Use platform-specific listing commands to verify production values separately from dev (.env.local does not affect prod).
  • Prefer explicit CLI flags over environment overrides (use npx convex run --prod, vercel env add production).

Example use cases

  • Fixing an ERR_INVALID_CHAR or "Invalid character in header" error by removing trailing \n from STRIPE_SECRET_KEY.
  • Running a parity check after producing a new webhook secret to ensure Vercel and Convex share the identical token.
  • Validating a set of Stripe keys with regex before promoting to production.
  • Auditing environment stores to find values that differ between dev, staging, and prod.
  • Automating a pre-deploy hygiene checklist to catch trailing whitespace, format errors, and missing tokens.

FAQ

How do I remove trailing newlines before setting a secret?

Use printf '%s' or tr -d '\n' to write the value. Example: printf '%s' "$TOKEN" | vercel env add KEY production.

How can I confirm prod values are correct on each platform?

Use each platform's list command explicitly for production: npx convex env list --prod and vercel env ls --environment=production, and compare tokens byte-for-byte.