Configuration Guide
CalcBridge uses environment variables for all configuration, following the twelve-factor app methodology. This guide provides a complete reference for all available settings.
Quick Setup
Copy the example environment file and customize:
# Copy the example file
cp .env.example .env
# Generate secure keys
python -c "import secrets; print(f'JWT_SECRET_KEY={secrets.token_urlsafe(32)}')"
python -c "import secrets; print(f'ENCRYPTION_MASTER_KEY={secrets.token_urlsafe(32)}')"
# Edit .env with your settings
code .env # or vim, nano, etc.
Environment Overview
| Environment | ENVIRONMENT Value | Purpose |
| Development | development | Local development, insecure defaults allowed |
| Staging | staging | Pre-production testing, warnings for insecure settings |
| Production | production | Live environment, strict security enforcement |
Production Security
In production, CalcBridge will refuse to start if security-critical settings use insecure defaults. This protects against accidental deployment with test credentials.
Core Settings
Application Settings
| Variable | Type | Default | Description |
ENVIRONMENT | string | development | Environment: development, staging, production |
DEBUG | bool | false | Enable debug mode (never in production) |
LOG_LEVEL | string | INFO | Logging level: DEBUG, INFO, WARNING, ERROR |
API_PREFIX | string | /api/v1 | API route prefix |
API_TITLE | string | CalcBridge API | API title in OpenAPI docs |
API_VERSION | string | 1.0.0 | API version |
.envENVIRONMENT=development
DEBUG=false
LOG_LEVEL=INFO
API_PREFIX=/api/v1
Security Settings
JWT Authentication
| Variable | Type | Default | Description |
JWT_SECRET_KEY | string | (insecure) | REQUIRED: JWT signing key (32+ chars) |
JWT_ALGORITHM | string | HS256 | JWT algorithm |
JWT_ACCESS_TOKEN_EXPIRE_MINUTES | int | 30 | Access token lifetime (5-1440) |
JWT_REFRESH_TOKEN_EXPIRE_DAYS | int | 7 | Refresh token lifetime (1-30) |
Generate Secure Keys
python -c "import secrets; print(secrets.token_urlsafe(32))"
.envJWT_SECRET_KEY=your-secure-random-key-at-least-32-characters
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=30
JWT_REFRESH_TOKEN_EXPIRE_DAYS=7
Encryption (PII Protection)
| Variable | Type | Default | Description |
ENCRYPTION_MASTER_KEY | string | (insecure) | REQUIRED: AES-256 encryption key (32+ chars) |
ENCRYPTION_PREVIOUS_MASTER_KEY | string | (none) | Previous key for rotation |
ENCRYPTION_KEY_ROTATION_ENABLED | bool | false | Enable key rotation mode |
.envENCRYPTION_MASTER_KEY=your-secure-encryption-key-at-least-32-chars
# For key rotation:
# ENCRYPTION_PREVIOUS_MASTER_KEY=old-key
# ENCRYPTION_KEY_ROTATION_ENABLED=true
Database Settings
PostgreSQL Connection
| Variable | Type | Default | Description |
DATABASE_URL | string | (local) | PostgreSQL connection URL |
DATABASE_POOL_SIZE | int | 20 | Connection pool size (1-100) |
DATABASE_MAX_OVERFLOW | int | 10 | Max overflow connections (0-50) |
DATABASE_POOL_PRE_PING | bool | true | Verify connections before use |
DATABASE_POOL_RECYCLE | int | 3600 | Recycle connections after N seconds |
DATABASE_ECHO | bool | false | Log SQL queries (debug only) |
DATABASE_APP_ROLE | string | calcbridge_app | Database role for RLS |
Database SSL
| Variable | Type | Default | Description |
DATABASE_SSL_MODE | string | prefer | SSL mode (see below) |
DATABASE_SSL_CERT_PATH | string | (none) | Client certificate path |
DATABASE_SSL_KEY_PATH | string | (none) | Client key path |
DATABASE_SSL_CA_PATH | string | (none) | CA certificate path |
SSL Mode Options:
| Mode | Security | Description |
disable | None | No SSL |
allow | Low | Try SSL, fall back to unencrypted |
prefer | Low | Try SSL, fall back to unencrypted (default) |
require | Medium | Require SSL, no certificate verification |
verify-ca | High | Verify server certificate against CA |
verify-full | Highest | Verify server certificate and hostname |
Production SSL
Use verify-full for production deployments.
.envDATABASE_URL=postgresql+psycopg://calcbridge:password@localhost:5433/calcbridge
DATABASE_POOL_SIZE=20
DATABASE_SSL_MODE=verify-full
DATABASE_SSL_CA_PATH=/certs/ca-cert.pem
Cache Settings (Valkey/Redis)
| Variable | Type | Default | Description |
VALKEY_HOST | string | localhost | Valkey/Redis host |
VALKEY_PORT | int | 6379 | Valkey/Redis port |
VALKEY_PASSWORD | string | (none) | Valkey/Redis password |
VALKEY_DB | int | 0 | Database number (0-15) |
VALKEY_SSL | bool | false | Enable SSL |
VALKEY_SSL_CERT_REQS | string | required | SSL certificate requirements |
VALKEY_SSL_CA_CERTS | string | (none) | CA certificate path |
VALKEY_MAX_CONNECTIONS | int | 50 | Max connection pool size |
VALKEY_SOCKET_TIMEOUT | float | 5.0 | Socket timeout (seconds) |
VALKEY_RETRY_ON_TIMEOUT | bool | true | Retry on timeout |
.envVALKEY_HOST=localhost
VALKEY_PORT=6380
VALKEY_PASSWORD=your-cache-password
VALKEY_SSL=true
Rate Limiting
| Variable | Type | Default | Description |
RATE_LIMIT_ENABLED | bool | true | Enable rate limiting |
RATE_LIMIT_WINDOW_SECONDS | int | 60 | Sliding window size |
RATE_LIMIT_DEFAULT_PER_IP | int | 100 | Default limit per IP |
RATE_LIMIT_TIER_FREE | int | 100 | Free tier requests/min |
RATE_LIMIT_TIER_STARTER | int | 500 | Starter tier requests/min |
RATE_LIMIT_TIER_PROFESSIONAL | int | 2000 | Professional tier requests/min |
RATE_LIMIT_TIER_ENTERPRISE | int | 10000 | Enterprise tier requests/min |
RATE_LIMIT_BURST_MULTIPLIER | float | 1.1 | Burst allowance (1.0-2.0) |
Subscription Tiers
| Tier | Requests/Minute | Burst Limit | Use Case |
| Free | 100 | 110 | Individual users, trials |
| Starter | 500 | 550 | Small teams |
| Professional | 2,000 | 2,200 | Medium organizations |
| Enterprise | 10,000 | 11,000 | Large organizations, API integrations |
.envRATE_LIMIT_ENABLED=true
RATE_LIMIT_TIER_PROFESSIONAL=2000
File Processing
| Variable | Type | Default | Description |
FILE_PROCESSING_CELERY_THRESHOLD_BYTES | int | 5242880 | File size threshold for Celery (5MB) |
FILE_PROCESSING_EXECUTOR_MAX_WORKERS | int | 4 | Thread pool size for parsing |
FILE_PROCESSING_EXECUTOR_TIMEOUT_SECONDS | int | 1200 | Parser timeout (20 min) |
MAX_UPLOAD_SIZE_MB | int | 100 | Maximum upload size |
File Storage
| Variable | Type | Default | Description |
STORAGE_BACKEND | string | local | Storage backend: local or s3 |
STORAGE_LOCAL_ROOT | string | uploads | Local storage directory |
STORAGE_S3_BUCKET | string | (none) | S3 bucket name (required if backend=s3) |
STORAGE_S3_REGION | string | us-east-1 | AWS region |
STORAGE_S3_ACCESS_KEY | string | (none) | AWS access key |
STORAGE_S3_SECRET_KEY | string | (none) | AWS secret key |
STORAGE_S3_ENDPOINT_URL | string | (none) | Custom endpoint (MinIO) |
STORAGE_PRESIGNED_URL_EXPIRE_SECONDS | int | 3600 | Presigned URL expiration |
.env# Local storage (development)
STORAGE_BACKEND=local
STORAGE_LOCAL_ROOT=uploads
# S3 storage (production)
STORAGE_BACKEND=s3
STORAGE_S3_BUCKET=calcbridge-uploads
STORAGE_S3_REGION=us-east-1
Celery Settings
| Variable | Type | Default | Description |
CELERY_TASK_SOFT_TIME_LIMIT | int | 1200 | Soft time limit (20 min) |
CELERY_TASK_TIME_LIMIT | int | 1320 | Hard time limit (22 min) |
CELERY_WORKER_CONCURRENCY | int | 4 | Worker concurrency |
CELERY_WORKER_PREFETCH_MULTIPLIER | int | 4 | Task prefetch multiplier |
CELERY_WORKER_MAX_TASKS_PER_CHILD | int | 1000 | Tasks before worker restart |
.envCELERY_TASK_SOFT_TIME_LIMIT=1200
CELERY_WORKER_CONCURRENCY=4
CORS Settings
| Variable | Type | Default | Description |
CORS_ORIGINS | list | ["localhost:3002"] | Allowed origins |
CORS_METHODS | list | ["GET","POST",...] | Allowed HTTP methods |
CORS_HEADERS | list | ["Authorization",...] | Allowed headers |
CORS_ALLOW_CREDENTIALS | bool | true | Allow credentials |
CORS_MAX_AGE | int | 600 | Preflight cache (seconds) |
.envCORS_ORIGINS=["https://app.yourdomain.com","https://admin.yourdomain.com"]
Observability
OpenTelemetry
| Variable | Type | Default | Description |
OTEL_ENABLED | bool | false | Enable OpenTelemetry |
OTEL_SERVICE_NAME | string | calcbridge | Service name |
OTEL_SERVICE_VERSION | string | 1.0.0 | Service version |
OTEL_ENDPOINT | string | (none) | OTLP endpoint |
OTEL_EXPORTER_TYPE | string | otlp | Exporter: otlp, console, none |
OTEL_PROTOCOL | string | grpc | Protocol: grpc or http |
OTEL_TRACES_SAMPLE_RATE | float | 1.0 | Sampling rate (0.0-1.0) |
Sentry
| Variable | Type | Default | Description |
SENTRY_DSN | string | (none) | Sentry DSN |
SENTRY_ENVIRONMENT | string | (auto) | Sentry environment |
SENTRY_TRACES_SAMPLE_RATE | float | 0.1 | Transaction sample rate |
SENTRY_PROFILES_SAMPLE_RATE | float | 0.1 | Profiling sample rate |
SENTRY_SEND_DEFAULT_PII | bool | false | Send PII data |
Prometheus
| Variable | Type | Default | Description |
PROMETHEUS_ENABLED | bool | true | Enable metrics endpoint |
PROMETHEUS_ENDPOINT | string | /metrics | Metrics path |
PROMETHEUS_REQUIRE_AUTH | bool | false | Require authentication |
PROMETHEUS_AUTH_TOKEN | string | (none) | Bearer token for auth |
.env# OpenTelemetry
OTEL_ENABLED=true
OTEL_ENDPOINT=http://otel-collector:4317
OTEL_TRACES_SAMPLE_RATE=0.1
# Sentry
SENTRY_DSN=https://xxx@sentry.io/xxx
SENTRY_TRACES_SAMPLE_RATE=0.1
# Prometheus
PROMETHEUS_ENABLED=true
Alerting
| Variable | Type | Default | Description |
ALERTING_ENABLED | bool | false | Enable alerting |
ALERTING_DEFAULT_PROVIDER | string | pagerduty | Default provider |
ALERTING_PAGERDUTY_ROUTING_KEY | string | (none) | PagerDuty routing key |
ALERTING_OPSGENIE_API_KEY | string | (none) | OpsGenie API key |
ALERTING_SLACK_WEBHOOK_URL | string | (none) | Slack webhook URL |
ALERTING_RATE_LIMIT_PER_MINUTE | int | 10 | Alert rate limit |
.envALERTING_ENABLED=true
ALERTING_DEFAULT_PROVIDER=slack
ALERTING_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxx
| Variable | Type | Default | Description |
SECURITY_HEADERS_ENABLED | bool | true | Enable security headers |
SECURITY_HEADERS_HSTS_ENABLED | bool | true | Enable HSTS |
SECURITY_HEADERS_HSTS_MAX_AGE | int | 31536000 | HSTS max-age (1 year) |
SECURITY_HEADERS_CSP_ENABLED | bool | true | Enable CSP |
SECURITY_HEADERS_FRAME_OPTIONS | string | DENY | X-Frame-Options |
Request Limits
Size Limits
| Variable | Type | Default | Description |
REQUEST_SIZE_ENABLED | bool | true | Enable size limits |
REQUEST_SIZE_DEFAULT_LIMIT_BYTES | int | 10485760 | Default limit (10MB) |
REQUEST_SIZE_UPLOAD_LIMIT_BYTES | int | 52428800 | Upload limit (50MB) |
Timeout Settings
| Variable | Type | Default | Description |
REQUEST_TIMEOUT_ENABLED | bool | true | Enable timeouts |
REQUEST_TIMEOUT_DEFAULT_SECONDS | float | 30.0 | Default timeout |
REQUEST_TIMEOUT_HEALTH_CHECK_SECONDS | float | 5.0 | Health check timeout |
REQUEST_TIMEOUT_UPLOAD_ENDPOINT_SECONDS | float | 1200.0 | Upload timeout (20 min) |
REQUEST_TIMEOUT_CALCULATION_ENDPOINT_SECONDS | float | 60.0 | Calculation timeout |
Dead Letter Queue
| Variable | Type | Default | Description |
DLQ_MESSAGE_TTL_DAYS | int | 7 | DLQ entry retention |
DLQ_MAX_ENTRIES | int | 10000 | Max DLQ entries |
DLQ_ALERT_THRESHOLD_TOTAL | int | 100 | Alert threshold |
DLQ_CLEANUP_INTERVAL_HOURS | int | 24 | Cleanup interval |
Complete Example
Here is a complete production .env file:
.env.production# Environment
ENVIRONMENT=production
DEBUG=false
LOG_LEVEL=INFO
# Security (REQUIRED - Replace with real values)
JWT_SECRET_KEY=your-production-jwt-secret-key-minimum-32-chars
ENCRYPTION_MASTER_KEY=your-production-encryption-key-minimum-32-chars
# Database
DATABASE_URL=postgresql+psycopg://calcbridge:strong-password@db.internal:5432/calcbridge
DATABASE_POOL_SIZE=20
DATABASE_SSL_MODE=verify-full
DATABASE_SSL_CA_PATH=/certs/ca.pem
# Cache
VALKEY_HOST=cache.internal
VALKEY_PORT=6379
VALKEY_PASSWORD=strong-cache-password
VALKEY_SSL=true
# Storage
STORAGE_BACKEND=s3
STORAGE_S3_BUCKET=calcbridge-prod
STORAGE_S3_REGION=us-east-1
# CORS
CORS_ORIGINS=["https://app.calcbridge.com"]
# Rate Limiting
RATE_LIMIT_ENABLED=true
# Observability
OTEL_ENABLED=true
OTEL_ENDPOINT=http://otel-collector:4317
SENTRY_DSN=https://xxx@sentry.io/xxx
# Alerting
ALERTING_ENABLED=true
ALERTING_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxx
Validation
CalcBridge validates configuration at startup:
# Test configuration
PYTHONPATH=. python -c "from src.core.config import get_settings; s = get_settings(); print(f'Environment: {s.environment}')"
# View all settings (development only)
PYTHONPATH=. python -c "from src.core.config import get_settings; print(get_settings().model_dump_json(indent=2))"
If production has insecure settings, you will see:
CRITICAL SECURITY ERROR: Insecure settings detected in production!
============================================================
The following issues MUST be resolved before starting:
- JWT_SECRET_KEY contains insecure default value
- ENCRYPTION_MASTER_KEY contains insecure default value