# Environment Variables Reference

Complete guide to environment variables used in Flyer Crawler.

---

## Quick Reference

### Minimum Required Variables (Development)

| Variable         | Example                  | Purpose              |
| ---------------- | ------------------------ | -------------------- |
| `DB_HOST`        | `localhost`              | PostgreSQL host      |
| `DB_USER`        | `postgres`               | PostgreSQL username  |
| `DB_PASSWORD`    | `postgres`               | PostgreSQL password  |
| `DB_NAME`        | `flyer_crawler_dev`      | Database name        |
| `REDIS_URL`      | `redis://localhost:6379` | Redis connection URL |
| `JWT_SECRET`     | (32+ character string)   | JWT signing key      |
| `GEMINI_API_KEY` | `AIzaSy...`              | Google Gemini API    |

### Source of Truth

The Zod schema at `src/config/env.ts` is the authoritative source for all environment variables. If a variable is not in this file, it is not used by the application.

---

## Configuration by Environment

### Production

| Aspect   | Details                                    |
| -------- | ------------------------------------------ |
| Location | Gitea CI/CD secrets injected at deployment |
| Path     | `/var/www/flyer-crawler.projectium.com/`   |
| File     | No `.env` file - all from CI/CD secrets    |

### Test

| Aspect   | Details                                       |
| -------- | --------------------------------------------- |
| Location | Gitea CI/CD secrets + `.env.test` overrides   |
| Path     | `/var/www/flyer-crawler-test.projectium.com/` |
| File     | `.env.test` for test-specific values          |

### Development Container

| Aspect   | Details                                 |
| -------- | --------------------------------------- |
| Location | `.env.local` file in project root       |
| Priority | Overrides defaults in `compose.dev.yml` |
| File     | `.env.local` (gitignored)               |

---

## Complete Variable Reference

### Database Configuration

| Variable      | Required | Default | Description       |
| ------------- | -------- | ------- | ----------------- |
| `DB_HOST`     | Yes      | -       | PostgreSQL host   |
| `DB_PORT`     | No       | `5432`  | PostgreSQL port   |
| `DB_USER`     | Yes      | -       | Database username |
| `DB_PASSWORD` | Yes      | -       | Database password |
| `DB_NAME`     | Yes      | -       | Database name     |

**Environment-Specific Variables** (Gitea Secrets):

| Variable           | Environment | Description              |
| ------------------ | ----------- | ------------------------ |
| `DB_USER_PROD`     | Production  | Production database user |
| `DB_PASSWORD_PROD` | Production  | Production database pass |
| `DB_DATABASE_PROD` | Production  | Production database name |
| `DB_USER_TEST`     | Test        | Test database user       |
| `DB_PASSWORD_TEST` | Test        | Test database password   |
| `DB_DATABASE_TEST` | Test        | Test database name       |

### Redis Configuration

| Variable         | Required | Default | Description               |
| ---------------- | -------- | ------- | ------------------------- |
| `REDIS_URL`      | Yes      | -       | Redis connection URL      |
| `REDIS_PASSWORD` | No       | -       | Redis password (optional) |

**URL Format**: `redis://[user:password@]host:port`

**Examples**:

```bash
# Development (no auth)
REDIS_URL=redis://localhost:6379

# Production (with auth)
REDIS_URL=redis://:${REDIS_PASSWORD_PROD}@localhost:6379
```

### Authentication

| Variable               | Required | Min Length | Description             |
| ---------------------- | -------- | ---------- | ----------------------- |
| `JWT_SECRET`           | Yes      | 32 chars   | JWT token signing key   |
| `JWT_SECRET_PREVIOUS`  | No       | -          | Previous key (rotation) |
| `GOOGLE_CLIENT_ID`     | No       | -          | Google OAuth client ID  |
| `GOOGLE_CLIENT_SECRET` | No       | -          | Google OAuth secret     |
| `GITHUB_CLIENT_ID`     | No       | -          | GitHub OAuth client ID  |
| `GITHUB_CLIENT_SECRET` | No       | -          | GitHub OAuth secret     |

**Generate Secure Secret**:

```bash
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
```

### AI Services

| Variable                     | Required | Description                      |
| ---------------------------- | -------- | -------------------------------- |
| `GEMINI_API_KEY`             | Yes\*    | Google Gemini API key            |
| `GEMINI_RPM`                 | No       | Rate limit (default: 5)          |
| `AI_PRICE_QUALITY_THRESHOLD` | No       | Quality threshold (default: 0.5) |

\*Required for flyer processing. Application works without it but cannot extract flyer data.

**Get API Key**: [Google AI Studio](https://aistudio.google.com/app/apikey)

**Test Environment Note**: The test/staging environment **deliberately omits** `GEMINI_API_KEY` to preserve free API quota. This is intentional - the API has strict daily limits on the free tier, and we want to reserve tokens for production use. AI features will be non-functional in test, but all other features can be tested normally. Deploy warnings about missing `GEMINI_API_KEY` in test logs are expected and safe to ignore.

### Google Services

| Variable               | Required | Description                      |
| ---------------------- | -------- | -------------------------------- |
| `GOOGLE_MAPS_API_KEY`  | No       | Google Maps Geocoding API        |
| `GOOGLE_CLIENT_ID`     | No       | OAuth (see Authentication above) |
| `GOOGLE_CLIENT_SECRET` | No       | OAuth (see Authentication above) |

### UPC Lookup APIs

| Variable                 | Required | Description            |
| ------------------------ | -------- | ---------------------- |
| `UPC_ITEM_DB_API_KEY`    | No       | UPC Item DB API key    |
| `BARCODE_LOOKUP_API_KEY` | No       | Barcode Lookup API key |

### Application Settings

| Variable       | Required | Default       | Description              |
| -------------- | -------- | ------------- | ------------------------ |
| `NODE_ENV`     | No       | `development` | Environment mode         |
| `PORT`         | No       | `3001`        | Backend server port      |
| `FRONTEND_URL` | No       | -             | Frontend URL (CORS)      |
| `BASE_URL`     | No       | -             | API base URL             |
| `STORAGE_PATH` | No       | (see below)   | Flyer image storage path |

**NODE_ENV Values**: `development`, `test`, `staging`, `production`

**Default STORAGE_PATH**: `/var/www/flyer-crawler.projectium.com/flyer-images`

### Email/SMTP Configuration

| Variable          | Required | Default | Description             |
| ----------------- | -------- | ------- | ----------------------- |
| `SMTP_HOST`       | No       | -       | SMTP server hostname    |
| `SMTP_PORT`       | No       | `587`   | SMTP server port        |
| `SMTP_USER`       | No       | -       | SMTP username           |
| `SMTP_PASS`       | No       | -       | SMTP password           |
| `SMTP_SECURE`     | No       | `false` | Use TLS                 |
| `SMTP_FROM_EMAIL` | No       | -       | From address for emails |

**Note**: Email functionality degrades gracefully if not configured.

### Worker Configuration

| Variable                              | Default | Description                  |
| ------------------------------------- | ------- | ---------------------------- |
| `WORKER_CONCURRENCY`                  | `1`     | Main worker concurrency      |
| `WORKER_LOCK_DURATION`                | `30000` | Lock duration (ms)           |
| `EMAIL_WORKER_CONCURRENCY`            | `10`    | Email worker concurrency     |
| `ANALYTICS_WORKER_CONCURRENCY`        | `1`     | Analytics worker concurrency |
| `CLEANUP_WORKER_CONCURRENCY`          | `10`    | Cleanup worker concurrency   |
| `WEEKLY_ANALYTICS_WORKER_CONCURRENCY` | `1`     | Weekly analytics concurrency |

### Error Tracking (Bugsink/Sentry)

| Variable              | Required | Default  | Description                     |
| --------------------- | -------- | -------- | ------------------------------- |
| `SENTRY_DSN`          | No       | -        | Backend Sentry DSN              |
| `SENTRY_ENABLED`      | No       | `true`   | Enable error tracking           |
| `SENTRY_ENVIRONMENT`  | No       | NODE_ENV | Environment name for errors     |
| `SENTRY_DEBUG`        | No       | `false`  | Enable Sentry SDK debug logging |
| `VITE_SENTRY_DSN`     | No       | -        | Frontend Sentry DSN             |
| `VITE_SENTRY_ENABLED` | No       | `true`   | Enable frontend error tracking  |
| `VITE_SENTRY_DEBUG`   | No       | `false`  | Frontend SDK debug logging      |

**DSN Format**: `http://[key]@[host]:[port]/[project_id]`

**Dev Container DSNs**:

```bash
# Backend (internal)
SENTRY_DSN=http://<key>@localhost:8000/1

# Frontend (via nginx proxy)
VITE_SENTRY_DSN=https://<key>@localhost/bugsink-api/2
```

---

## Configuration Files

| File                                  | Purpose                                     |
| ------------------------------------- | ------------------------------------------- |
| `src/config/env.ts`                   | Zod schema validation - **source of truth** |
| `ecosystem.config.cjs`                | PM2 process manager (production)            |
| `ecosystem.dev.config.cjs`            | PM2 process manager (development)           |
| `.gitea/workflows/deploy-to-prod.yml` | Production deployment workflow              |
| `.gitea/workflows/deploy-to-test.yml` | Test deployment workflow                    |
| `.env.example`                        | Template with all variables                 |
| `.env.local`                          | Dev container overrides (not in git)        |
| `.env.test`                           | Test environment overrides (not in git)     |

---

## Adding New Variables

### Checklist

1. [ ] **Update Zod Schema** - Edit `src/config/env.ts`
2. [ ] **Add to Gitea Secrets** - For prod/test environments
3. [ ] **Update Deployment Workflows** - `.gitea/workflows/*.yml`
4. [ ] **Update PM2 Config** - `ecosystem.config.cjs`
5. [ ] **Update .env.example** - Template for developers
6. [ ] **Update this document** - Add to appropriate section

### Step-by-Step

#### 1. Update Zod Schema

Edit `src/config/env.ts`:

```typescript
const envSchema = z.object({
  // ... existing variables ...
  newSection: z.object({
    newVariable: z.string().min(1, 'NEW_VARIABLE is required'),
  }),
});

// In loadEnvVars():
newSection: {
  newVariable: process.env.NEW_VARIABLE,
},
```

#### 2. Add to Gitea Secrets

1. Go to Gitea repository Settings > Secrets
2. Add `NEW_VARIABLE` with production value
3. Add `NEW_VARIABLE_TEST` if test needs different value

#### 3. Update Deployment Workflows

Edit `.gitea/workflows/deploy-to-prod.yml`:

```yaml
env:
  NEW_VARIABLE: ${{ secrets.NEW_VARIABLE }}
```

Edit `.gitea/workflows/deploy-to-test.yml`:

```yaml
env:
  NEW_VARIABLE: ${{ secrets.NEW_VARIABLE_TEST }}
```

#### 4. Update PM2 Config

Edit `ecosystem.config.cjs`:

```javascript
module.exports = {
  apps: [
    {
      env: {
        NEW_VARIABLE: process.env.NEW_VARIABLE,
      },
    },
  ],
};
```

---

## Security Best Practices

### Do

- Use Gitea Secrets for prod/test
- Use `.env.local` for dev (gitignored)
- Generate secrets with cryptographic randomness
- Rotate secrets regularly
- Use environment-specific database users

### Do Not

- Commit secrets to git
- Use short or predictable secrets
- Share secrets across environments
- Log sensitive values

### Secret Generation

```bash
# Generate secure random secrets (64 hex characters)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

# Example output:
# a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
```

### Database Users by Environment

| Environment | User                 | Database             |
| ----------- | -------------------- | -------------------- |
| Production  | `flyer_crawler_prod` | `flyer-crawler-prod` |
| Test        | `flyer_crawler_test` | `flyer-crawler-test` |
| Development | `postgres`           | `flyer_crawler_dev`  |

---

## Validation

Environment variables are validated at startup via `src/config/env.ts`.

### Startup Validation

If validation fails, you will see:

```text
╔════════════════════════════════════════════════════════════════╗
║           CONFIGURATION ERROR - APPLICATION STARTUP            ║
╚════════════════════════════════════════════════════════════════╝

The following environment variables are missing or invalid:

  - database.host: DB_HOST is required
  - auth.jwtSecret: JWT_SECRET must be at least 32 characters

Please check your .env file or environment configuration.
```

### Debugging Configuration

```bash
# Check what variables are set (dev container)
podman exec flyer-crawler-dev env | grep -E "^(DB_|REDIS_|JWT_|SENTRY_)"

# Test database connection
podman exec flyer-crawler-postgres psql -U postgres -d flyer_crawler_dev -c "SELECT 1;"

# Test Redis connection
podman exec flyer-crawler-redis redis-cli ping
```

---

## Troubleshooting

### Variable Not Found

```text
Error: Missing required environment variable: JWT_SECRET
```

**Solutions**:

1. Check `.env.local` exists and has the variable
2. Verify variable name matches schema exactly
3. Restart the application after changes

### Invalid Value

```text
Error: JWT_SECRET must be at least 32 characters
```

**Solution**: Generate a longer secret value.

### Wrong Environment Selected

Check `NODE_ENV` is set correctly:

| Value         | Purpose                |
| ------------- | ---------------------- |
| `development` | Local dev container    |
| `test`        | CI/CD test server      |
| `staging`     | Pre-production testing |
| `production`  | Production server      |

### Database Connection Issues

```bash
# Development
podman exec flyer-crawler-postgres psql -U postgres -d flyer_crawler_dev -c "SELECT 1;"

# If connection fails, check:
# 1. Container is running: podman ps
# 2. DB_HOST matches container network
# 3. DB_PASSWORD is correct
```

---

## Related Documentation

- [QUICKSTART.md](QUICKSTART.md) - Quick setup guide
- [INSTALL.md](INSTALL.md) - Detailed installation
- [DEV-CONTAINER.md](../development/DEV-CONTAINER.md) - Dev container setup
- [DEPLOYMENT.md](../operations/DEPLOYMENT.md) - Production deployment
- [AUTHENTICATION.md](../architecture/AUTHENTICATION.md) - OAuth setup
- [ADR-007](../adr/0007-configuration-and-secrets-management.md) - Configuration decisions

---

Last updated: January 2026
