Skip to main content

GitHub Configuration Guide

This repo uses GitHub Actions for CI/CD with GitHub Environments for environment-specific configuration.

1. GitHub Environments Strategy

We use GitHub Environments (production and sandbox) instead of repository-level prefixed secrets/variables for cleaner, more secure configuration management.

Benefits

  • Scoped Access: Secrets/variables are isolated per environment
  • Protection Rules: Can require approvals for production deployments
  • Cleaner Code: Workflows automatically inherit environment context
  • No Prefixes: Simple variable names like SUPABASE_URL instead of PROD_SUPABASE_URL

Architecture

Repository
├── Environments
│ ├── production (main branch)
│ │ ├── Variables: SUPABASE_URL, NEXT_PUBLIC_SITE_URL, etc.
│ │ └── Secrets: SUPABASE_ANON_KEY, KV_NAMESPACE_ID, etc.
│ └── sandbox (sandbox branch)
│ ├── Variables: SUPABASE_URL, NEXT_PUBLIC_SITE_URL, etc.
│ └── Secrets: SUPABASE_ANON_KEY, KV_NAMESPACE_ID, etc.
├── Repository Variables
│ └── CLOUDFLARE_ACCOUNT_ID (shared across all environments)
└── Repository Secrets
├── CLOUDFLARE_API_TOKEN (shared across all workflows)
├── SUPABASE_ACCESS_TOKEN (shared for Supabase CLI)
└── SSH_PRIVATE_KEY (shared for infrastructure deployments)

2. Required Environment Variables

Go to: Repository → Settings → Environments → [production|sandbox] → Variables

Production Environment

Variable NameValue Example
CLOUDFLARE_ACCOUNT_IDfabfd9b8a8027ca77f54ccc830eceb54
ALLOWED_ORIGINShttps://www.freeaihashtags.com
NEXT_PUBLIC_API_BASE_PATHhttps://api.freeaihashtags.com/v1
NEXT_PUBLIC_DOCS_URLhttps://docs.freeaihashtags.com/api
NEXT_PUBLIC_SITE_URLhttps://www.freeaihashtags.com
NEXT_PUBLIC_SUPABASE_URLhttps://ssvgamtfxzyopspdzgzv.supabase.co
PADDLE_ENVproduction
SUPABASE_PROJECT_REFssvgamtfxzyopspdzgzv
SUPABASE_URLhttps://ssvgamtfxzyopspdzgzv.supabase.co
PADDLE_PRO_MONTHLY_PRICE_IDpri_01xxxxx (Production Paddle ID)
PADDLE_PRO_YEARLY_PRICE_IDpri_01xxxxx (Production Paddle ID)
PADDLE_AGENCY_MONTHLY_PRICE_IDpri_01xxxxx (Production Paddle ID)
PADDLE_AGENCY_YEARLY_PRICE_IDpri_01xxxxx (Production Paddle ID)
PADDLE_PRO_MONTHLY_DISCOUNT_IDdsc_01xxxxx (optional)
PADDLE_PRO_YEARLY_DISCOUNT_IDdsc_01xxxxx (optional)
PADDLE_AGENCY_MONTHLY_DISCOUNT_IDdsc_01xxxxx (optional)
PADDLE_AGENCY_YEARLY_DISCOUNT_IDdsc_01xxxxx (optional)

Sandbox Environment

Variable NameValue Example
CLOUDFLARE_ACCOUNT_IDfabfd9b8a8027ca77f54ccc830eceb54
ALLOWED_ORIGINShttp://localhost:3000,https://faiht-frontend-sandbox.adeel-zain-work.workers.dev
NEXT_PUBLIC_API_BASE_PATHhttps://faiht-api.adeel-zain-work.workers.dev/v1
NEXT_PUBLIC_DOCS_URLhttps://sandbox.faiht2-public-docs.pages.dev/api
NEXT_PUBLIC_SITE_URLhttps://faiht-frontend-sandbox.adeel-zain-work.workers.dev
NEXT_PUBLIC_SUPABASE_URLhttps://wyjzdyfxczuixkukurdg.supabase.co
PADDLE_ENVsandbox
SUPABASE_PROJECT_REFwyjzdyfxczuixkukurdg
SUPABASE_URLhttps://wyjzdyfxczuixkukurdg.supabase.co
PADDLE_PRO_MONTHLY_PRICE_IDpri_01kbxdgfay3hbjvp3yqg6rndje
PADDLE_PRO_YEARLY_PRICE_IDpri_01kbxdghs02htdy7cswy47d9vc
PADDLE_AGENCY_MONTHLY_PRICE_IDpri_01kchmne1mtq81gbatcvh1216v
PADDLE_AGENCY_YEARLY_PRICE_IDpri_01kchmngft2arbpaqfc3sa9tgy
PADDLE_PRO_MONTHLY_DISCOUNT_IDdsc_01kbxe2cneh4qc78v3vs77k5bq
PADDLE_PRO_YEARLY_DISCOUNT_IDFIXTHIS (needs actual value)
PADDLE_AGENCY_MONTHLY_DISCOUNT_IDdsc_01kbxe2cneh4qc78v3vs77k5bq
PADDLE_AGENCY_YEARLY_DISCOUNT_IDFIXTHIS (needs actual value)

Repository-Level Variables

Variable NameValue
CLOUDFLARE_ACCOUNT_IDfabfd9b8a8027ca77f54ccc830eceb54

3. Required Environment Secrets

Go to: Repository → Settings → Environments → [production|sandbox] → Secrets

Production Environment

Secret NameDescription
GOOGLE_CLIENT_IDGoogle OAuth client ID for production
KV_NAMESPACE_IDCloudflare KV namespace for production
PADDLE_CLIENT_TOKENPaddle client token for production
SUPABASE_ANON_KEYSupabase anon key for production
SUPABASE_SERVICE_ROLE_KEYSupabase service role key for production
TIKTOK_CLIENT_KEYTikTok OAuth client key for production
TIKTOK_CLIENT_SECRETTikTok OAuth secret for production

Sandbox Environment

Secret NameDescription
GOOGLE_CLIENT_IDGoogle OAuth client ID for sandbox
KV_NAMESPACE_IDCloudflare KV namespace for sandbox
PADDLE_CLIENT_TOKENPaddle client token for sandbox
SUPABASE_ANON_KEYSupabase anon key for sandbox
SUPABASE_SERVICE_ROLE_KEYSupabase service role key for sandbox
TIKTOK_CLIENT_KEYTikTok OAuth client key for sandbox
TIKTOK_CLIENT_SECRETTikTok OAuth secret for sandbox

Repository-Level Secrets (Shared)

Secret NameDescription
CLOUDFLARE_API_TOKENAPI Token for Cloudflare deployments
SUPABASE_ACCESS_TOKENSupabase CLI access token
SSH_PRIVATE_KEYSSH key for infrastructure deploys

4. Bulk Setup (CLI)

Prerequisites

You need the GitHub CLI with proper permissions:

gh auth login
# Select: GitHub.com → HTTPS → Authenticate with browser
# Token must have 'repo' and 'admin:org' (Environments) permissions

Upload Secrets from File

  1. Create environment-specific secret files:
# Production
cat > .github.production.secrets << EOF
GOOGLE_CLIENT_ID=your-prod-google-client-id
KV_NAMESPACE_ID=your-prod-kv-namespace-id
PADDLE_CLIENT_TOKEN=your-prod-paddle-token
SUPABASE_ANON_KEY=your-prod-supabase-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-prod-supabase-service-role-key
TIKTOK_CLIENT_KEY=your-prod-tiktok-client-key
TIKTOK_CLIENT_SECRET=your-prod-tiktok-client-secret
EOF

# Sandbox
cat > .github.sandbox.secrets << EOF
GOOGLE_CLIENT_ID=your-sandbox-google-client-id
KV_NAMESPACE_ID=your-sandbox-kv-namespace-id
PADDLE_CLIENT_TOKEN=your-sandbox-paddle-token
SUPABASE_ANON_KEY=your-sandbox-supabase-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-sandbox-supabase-service-role-key
TIKTOK_CLIENT_KEY=your-sandbox-tiktok-client-key
TIKTOK_CLIENT_SECRET=your-sandbox-tiktok-client-secret
EOF
  1. Upload via helper script:
# Upload to production
while IFS='=' read -r key value; do
[[ -z "$key" || "$key" =~ ^# ]] && continue
echo "$value" | gh secret set "$key" --env production
done < .github.production.secrets

# Upload to sandbox
while IFS='=' read -r key value; do
[[ -z "$key" || "$key" =~ ^# ]] && continue
echo "$value" | gh secret set "$key" --env sandbox
done < .github.sandbox.secrets

Important: Ensure .github.production.secrets and .github.sandbox.secrets are in .gitignore!


5. KV Namespace Setup

Cloudflare KV namespaces are required for the API Gateway (caching and rate limiting).

cd apps/api

# Create sandbox namespace
wrangler kv:namespace create "FAIHT_CACHE" --env sandbox
# Copy the ID → Add to sandbox environment's KV_NAMESPACE_ID secret

# Create production namespace
wrangler kv:namespace create "FAIHT_CACHE"
# Copy the ID → Add to production environment's KV_NAMESPACE_ID secret

Detailed Guide: Cloudflare KV Setup


6. Verification

Check Variables

# Repository-level
gh variable list

# Environment-level
gh api repos/:owner/:repo/environments/production/variables --jq '.variables[].name'
gh api repos/:owner/:repo/environments/sandbox/variables --jq '.variables[].name'

Check Secrets

# Repository-level
gh secret list

# Environment-level
gh api repos/:owner/:repo/environments/production/secrets --jq '.secrets[].name'
gh api repos/:owner/:repo/environments/sandbox/secrets --jq '.secrets[].name'

Check Environments

gh api repos/:owner/:repo/environments --jq '.environments[].name'

Expected output:

production
sandbox