# Environment Setup

> Production environment configuration and secrets management

## 📋 Overview

Proper environment configuration is critical for security and functionality. This guide covers environment variables, secrets management, and configuration strategies for production deployments.

## 🔐 Environment Variables

### Required Variables

#### Database Configuration

```bash
# PostgreSQL connection string
DATABASE_URL="postgresql://username:password@host:5432/database"

# Alternative: separate components
DB_HOST="postgres.example.com"
DB_PORT="5432"
DB_NAME="izri"
DB_USER="app_user"
DB_PASSWORD="secure_password"
```

#### Authentication

```bash
# Better Auth
BETTER_AUTH_SECRET="random_32_char_secret_key_here"
BETTER_AUTH_URL="https://api.yourdomain.com"

# GitHub OAuth
GITHUB_CLIENT_ID="Iv1.1234567890abcdef"
GITHUB_CLIENT_SECRET="1234567890abcdef1234567890abcdef12345678"
GITHUB_REDIRECT_URI="https://yourdomain.com/auth/callback/github"

# JWT for API tokens
JWT_SECRET="another_secure_random_secret_key"
JWT_EXPIRES_IN="7d"
```

#### AI Services

```bash
# OpenAI
OPENAI_API_KEY="sk-proj-..."
OPENAI_MODEL="gpt-4"
OPENAI_MAX_TOKENS="2000"

# Anthropic (optional)
ANTHROPIC_API_KEY="sk-ant-..."
ANTHROPIC_MODEL="claude-3-opus-20240229"
```

#### Redis

```bash
# Redis connection
REDIS_URL="redis://redis.example.com:6379"

# Alternative: separate components
REDIS_HOST="redis.example.com"
REDIS_PORT="6379"
REDIS_PASSWORD="redis_password"
REDIS_DB="0"
```

#### Application Settings

```bash
# Environment
NODE_ENV="production"

# API Server
API_PORT="4000"
API_HOST="0.0.0.0"
LOG_LEVEL="info"

# Web Application
VITE_API_URL="https://api.yourdomain.com"
PORT="3000"
```

### Optional Variables

#### Observability

```bash
# OpenTelemetry
OTEL_EXPORTER_OTLP_ENDPOINT="https://otel-collector:4318"
OTEL_SERVICE_NAME="izri-api"
OTEL_LOG_LEVEL="info"

# Sentry
SENTRY_DSN="https://...@sentry.io/..."
SENTRY_ENVIRONMENT="production"
SENTRY_TRACES_SAMPLE_RATE="0.1"
```

#### External Services

```bash
# Email (if implemented)
SMTP_HOST="smtp.sendgrid.net"
SMTP_PORT="587"
SMTP_USER="apikey"
SMTP_PASSWORD="SG...."
SMTP_FROM="noreply@yourdomain.com"

# S3 (if implemented)
AWS_REGION="us-east-1"
AWS_ACCESS_KEY_ID="AKIA..."
AWS_SECRET_ACCESS_KEY="..."
S3_BUCKET="izri-uploads"
```

#### Rate Limiting

```bash
# API rate limits
RATE_LIMIT_WINDOW_MS="60000"
RATE_LIMIT_MAX_REQUESTS="100"

# AI service rate limits
AI_RATE_LIMIT_MAX_REQUESTS="10"
AI_RATE_LIMIT_WINDOW_MS="60000"
```

## 📝 Environment File Structure

### Development (.env.development)

```bash
NODE_ENV=development
LOG_LEVEL=debug

# Local services
DATABASE_URL=postgresql://postgres:password@localhost:5433/izri_dev
REDIS_URL=redis://localhost:6380

# API
API_PORT=4000
VITE_API_URL=http://localhost:4000

# Auth (GitHub test app)
GITHUB_CLIENT_ID=dev_client_id
GITHUB_CLIENT_SECRET=dev_client_secret
BETTER_AUTH_SECRET=dev_secret_key_min_32_chars_long
BETTER_AUTH_URL=http://localhost:4000

# AI (test key with limits)
OPENAI_API_KEY=sk-...
```

### Production (.env.production)

```bash
NODE_ENV=production
LOG_LEVEL=info

# Production database
DATABASE_URL=postgresql://prod_user:${DB_PASSWORD}@prod-db.region.rds.amazonaws.com:5432/izri

# Production Redis
REDIS_URL=redis://:${REDIS_PASSWORD}@prod-redis.cache.amazonaws.com:6379

# API
API_PORT=4000
API_HOST=0.0.0.0
VITE_API_URL=https://api.yourdomain.com

# Auth (production GitHub app)
GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}
BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
BETTER_AUTH_URL=https://api.yourdomain.com

# AI
OPENAI_API_KEY=${OPENAI_API_KEY}

# Monitoring
SENTRY_DSN=${SENTRY_DSN}
OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_ENDPOINT}
```

### Staging (.env.staging)

```bash
NODE_ENV=staging
LOG_LEVEL=debug

# Staging database
DATABASE_URL=postgresql://staging_user:${DB_PASSWORD}@staging-db.example.com:5432/izri_staging

# Staging Redis
REDIS_URL=redis://staging-redis.example.com:6379

# API
API_PORT=4000
VITE_API_URL=https://staging-api.yourdomain.com

# Auth (staging GitHub app)
GITHUB_CLIENT_ID=${STAGING_GITHUB_CLIENT_ID}
GITHUB_CLIENT_SECRET=${STAGING_GITHUB_CLIENT_SECRET}
BETTER_AUTH_SECRET=${STAGING_AUTH_SECRET}
BETTER_AUTH_URL=https://staging-api.yourdomain.com

# AI (separate key for staging)
OPENAI_API_KEY=${STAGING_OPENAI_API_KEY}
```

## 🔒 Secrets Management

### Option 1: AWS Secrets Manager

**Store secrets**:

```bash
# Create secret
aws secretsmanager create-secret \
  --name izri/production \
  --secret-string '{
    "DATABASE_URL": "postgresql://...",
    "GITHUB_CLIENT_SECRET": "...",
    "OPENAI_API_KEY": "sk-..."
  }'
```

**Retrieve in application**:

```typescript
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager'

const client = new SecretsManagerClient({ region: 'us-east-1' })

async function getSecrets() {
  const response = await client.send(
    new GetSecretValueCommand({ SecretId: 'izri/production' })
  )
  return JSON.parse(response.SecretString!)
}
```

**Docker integration**:

```yaml
# docker-compose.yml
services:
  api:
    environment:
      - AWS_REGION=us-east-1
      - SECRET_NAME=izri/production
    command: sh -c "
      export $(aws secretsmanager get-secret-value --secret-id $SECRET_NAME --query SecretString --output text | jq -r 'to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]') &&
      node apps/api/dist/index.js
    "
```

### Option 2: HashiCorp Vault

**Store secrets**:

```bash
# Enable KV secrets engine
vault secrets enable -path=izri kv-v2

# Store secrets
vault kv put izri/production \
  database_url="postgresql://..." \
  github_client_secret="..." \
  openai_api_key="sk-..."
```

**Retrieve in application**:

```typescript
import vault from 'node-vault'

const client = vault({
  endpoint: 'https://vault.example.com:8200',
  token: process.env.VAULT_TOKEN
})

async function getSecrets() {
  const result = await client.read('izri/production')
  return result.data.data
}
```

### Option 3: Kubernetes Secrets

**Create secret**:

```bash
kubectl create secret generic izri-secrets \
  --from-literal=database-url='postgresql://...' \
  --from-literal=github-client-secret='...' \
  --from-literal=openai-api-key='sk-...'
```

**Use in deployment**:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: izri-api
spec:
  template:
    spec:
      containers:
      - name: api
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: izri-secrets
              key: database-url
        - name: GITHUB_CLIENT_SECRET
          valueFrom:
            secretKeyRef:
              name: izri-secrets
              key: github-client-secret
```

### Option 4: Doppler

**Install CLI**:

```bash
# Install
brew install dopplerhq/cli/doppler

# Login
doppler login

# Setup project
doppler setup
```

**Use in development**:

```bash
# Run with Doppler
doppler run -- pnpm dev

# Docker integration
docker run --env-file <(doppler secrets download --no-file --format docker) izri-api
```

### Option 5: Environment Variables (Platform-specific)

**Railway**:

```bash
# Set via CLI
railway variables set DATABASE_URL="postgresql://..."

# Or via dashboard: project → Variables
```

**Render**:

```yaml
# render.yaml
services:
  - type: web
    name: izri-api
    env: docker
    envVars:
      - key: DATABASE_URL
        fromDatabase:
          name: postgres
          property: connectionString
      - key: GITHUB_CLIENT_SECRET
        sync: false  # Manual secret input
```

**Fly.io**:

```bash
# Set secrets
fly secrets set DATABASE_URL="postgresql://..." \
  GITHUB_CLIENT_SECRET="..." \
  OPENAI_API_KEY="sk-..."

# List secrets
fly secrets list
```

## 🔧 Configuration Validation

### Runtime Validation

**Create schema** (`src/config/env.ts`):

```typescript
import { z } from 'zod'

const envSchema = z.object({
  NODE_ENV: z.enum(['development', 'staging', 'production']),
  
  // Database
  DATABASE_URL: z.string().url(),
  
  // Redis
  REDIS_URL: z.string().url(),
  
  // Auth
  GITHUB_CLIENT_ID: z.string().min(1),
  GITHUB_CLIENT_SECRET: z.string().min(1),
  BETTER_AUTH_SECRET: z.string().min(32),
  BETTER_AUTH_URL: z.string().url(),
  
  // AI
  OPENAI_API_KEY: z.string().startsWith('sk-'),
  
  // API
  API_PORT: z.coerce.number().default(4000),
  LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
  
  // Optional
  SENTRY_DSN: z.string().url().optional(),
  OTEL_EXPORTER_OTLP_ENDPOINT: z.string().url().optional(),
})

export const env = envSchema.parse(process.env)

export type Env = z.infer<typeof envSchema>
```

**Use validated config**:

```typescript
import { env } from './config/env'

// Type-safe and validated
const dbUrl = env.DATABASE_URL
const port = env.API_PORT  // number, not string
```

### Startup Validation

```typescript
// src/index.ts
import { env } from './config/env'
import { logger } from './lib/logger'

async function validateEnvironment() {
  logger.info('Validating environment configuration...')
  
  try {
    // Already validated by Zod, but can add runtime checks
    
    // Test database connection
    await db.select().from(users).limit(1)
    logger.info('✓ Database connection successful')
    
    // Test Redis connection
    await redis.ping()
    logger.info('✓ Redis connection successful')
    
    logger.info('✓ Environment validation complete')
  } catch (error) {
    logger.error('✗ Environment validation failed:', error)
    process.exit(1)
  }
}

await validateEnvironment()
```

## 🎯 Best Practices

### 1. Never Commit Secrets

**Add to `.gitignore`**:

```gitignore
# Environment files
.env
.env.*
.env.local
.env.*.local
.env.production
.env.shared  # Important: Never commit with real secrets!

# But keep examples and configs
!.env.shared.example
!env.config.ts
```

### 2. Use Example Files

Izri uses a centralized environment system. Keep `.env.shared.example` updated:

**Maintain `.env.shared.example`**:

```bash
# Database
DATABASE_URL="postgresql://user:password@localhost:5433/izri"

# Auth
GITHUB_CLIENT_ID="your_client_id"
GITHUB_CLIENT_SECRET="your_client_secret"
BETTER_AUTH_SECRET="generate_a_random_32_char_secret"

# AI
OPENAI_API_KEY="sk-your-key-here"

# Optional
SENTRY_DSN=""
```

> **💡 New System**: We use `.env.shared` as single source of truth. Run `pnpm env:generate` to create project-specific files. See [Environment Management](../development/environment-management.md).

### 3. Rotate Secrets Regularly

```bash
# Generate new secrets
openssl rand -base64 32

# Update in .env.shared
# Edit .env.shared with new secrets
nano .env.shared

# Regenerate project-specific files
pnpm env:generate

# Or in CI/CD, update secrets manager
aws secretsmanager update-secret \
  --secret-id izri/production \
  --secret-string '{"BETTER_AUTH_SECRET": "new_secret"}'

# Restart services
docker compose restart api
```

### 4. Use Different Secrets Per Environment

**Development** (`.env.shared` in local):
```bash
BETTER_AUTH_SECRET="dev_secret_32_chars_long_here_"
```

**Staging** (`.env.shared` from secrets manager):
```bash
BETTER_AUTH_SECRET="staging_secret_different_one"
```

**Production** (`.env.shared` from secrets manager):
```bash
BETTER_AUTH_SECRET="prod_secret_completely_unique"
```

> After updating `.env.shared` in any environment, always run `pnpm env:generate`

### 5. Principle of Least Privilege

```bash
# Create dedicated database user with limited permissions
CREATE USER api_user WITH PASSWORD 'secure_password';
GRANT CONNECT ON DATABASE izri TO api_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO api_user;

# Don't use superuser accounts
```

## 🔗 Related Documentation

- **Environment Management**: [Automated environment system](../development/environment-management.md)
- **Environment Variables**: [Complete variable reference](../development/environment-variables.md)
- **Docker**: [Docker configuration](./docker.md)
- **Docker Compose**: [Multi-service setup](./docker-compose.md)
- **Production**: [Production deployment](./production.md)

---

*Ready to deploy? Continue with [Production Deployment](./production.md) →*
