Configuration Reference
Contract Lucidity is configured via environment variables. The backend reads these through Pydantic's BaseSettings class, which supports both environment variables and a .env file at the project root.
All variables are case-insensitive (e.g., POSTGRES_USER and postgres_user are equivalent).
Database
| Variable | Default | Description | Service |
|---|---|---|---|
POSTGRES_USER | cl_user | PostgreSQL username | cl-backend, cl-worker, cl-db |
POSTGRES_PASSWORD | cl_password_change_me | PostgreSQL password | cl-backend, cl-worker, cl-db |
POSTGRES_DB | contract_lucidity | PostgreSQL database name | cl-backend, cl-worker, cl-db |
POSTGRES_HOST | cl-postgres | PostgreSQL hostname | cl-backend, cl-worker |
POSTGRES_PORT | 5432 | PostgreSQL port | cl-backend, cl-worker |
The default PostgreSQL password is cl_password_change_me. Always set a strong, unique password in production via POSTGRES_PASSWORD.
The database connection URLs are derived automatically:
- Async (backend API):
postgresql+asyncpg://{user}:{password}@{host}:{port}/{db} - Sync (Celery worker):
postgresql+psycopg2://{user}:{password}@{host}:{port}/{db}
Redis / Celery
| Variable | Default | Description | Service |
|---|---|---|---|
REDIS_HOST | cl-redis | Redis hostname | cl-backend, cl-worker |
REDIS_PORT | 6379 | Redis port | cl-backend, cl-worker |
REDIS_URL | redis://cl-redis:6379/0 | Full Redis URL | cl-backend, cl-worker |
CELERY_BROKER_URL | redis://cl-redis:6379/0 | Celery broker URL (Redis db 0) | cl-worker |
CELERY_RESULT_BACKEND | redis://cl-redis:6379/1 | Celery result backend (Redis db 1) | cl-worker |
JWT / Authentication
| Variable | Default | Description | Service |
|---|---|---|---|
JWT_SECRET_KEY | change-me-to-a-random-secret-in-production | Secret key for signing JWT tokens | cl-backend |
JWT_ALGORITHM | HS256 | JWT signing algorithm | cl-backend |
JWT_ACCESS_TOKEN_EXPIRE_MINUTES | 60 | Access token lifetime in minutes | cl-backend |
JWT_REFRESH_TOKEN_EXPIRE_DAYS | 7 | Refresh token lifetime in days | cl-backend |
The default JWT secret is a placeholder. In production, generate a random 64+ character secret:
openssl rand -hex 64
Single Sign-On (OIDC) is configured entirely through the admin UI (Settings > SSO), not through environment variables. SSO client secrets are encrypted at rest using a key derived from JWT_SECRET_KEY -- changing the JWT secret will invalidate any stored SSO configuration. See Single Sign-On (SSO) for setup instructions.
Application
| Variable | Default | Description | Service |
|---|---|---|---|
APP_NAME | Contract Lucidity | Application display name | cl-backend |
APP_ENV | development | Environment identifier (development, staging, production) | cl-backend |
LOG_LEVEL | INFO | Logging level (DEBUG, INFO, WARNING, ERROR) | cl-backend, cl-worker |
MAX_UPLOAD_SIZE_MB | 100 | Maximum file upload size in megabytes | cl-backend |
Storage
| Variable | Default | Description | Service |
|---|---|---|---|
STORAGE_PATH | /data/storage | Path for uploaded document files | cl-backend, cl-worker |
CONFIG_PATH | /data/config | Path for application configuration files | cl-backend, cl-worker |
These paths correspond to Docker volumes cl-storage and cl-config. If running without Docker, ensure these directories exist and are writable.
CORS and URLs
| Variable | Default | Description | Service |
|---|---|---|---|
CORS_ORIGINS | http://localhost:3000,https://contractlucidity.com | Comma-separated list of allowed CORS origins | cl-backend |
FRONTEND_URL | http://localhost:3000 | Public URL of the frontend (used for links in emails, etc.) | cl-backend |
BACKEND_INTERNAL_URL | http://cl-backend:8000 | Internal URL for frontend-to-backend communication | cl-frontend |
For production, set CORS_ORIGINS to include your domain:
CORS_ORIGINS=https://contracts.yourcompany.com
The frontend uses BACKEND_INTERNAL_URL to proxy API requests within the Docker network. This should remain as the default unless you have a custom network topology.
Default Admin
| Variable | Default | Description | Service |
|---|---|---|---|
DEFAULT_ADMIN_EMAIL | [email protected] | Email for the auto-created admin account | cl-backend |
DEFAULT_ADMIN_PASSWORD | <your-strong-password> | Initial password for the admin account | cl-backend |
The default admin is created on first boot during the seed process. The account is flagged with must_change_password=True, prompting a password change on first login.
Change DEFAULT_ADMIN_PASSWORD before first boot in production, or change the password immediately after first login. The seed process will not overwrite an existing admin account.
Frontend-Specific
These variables are set on the cl-frontend service in Docker Compose:
| Variable | Default | Description | Service |
|---|---|---|---|
BACKEND_INTERNAL_URL | http://cl-backend:8000 | Backend API URL used by the Next.js server-side proxy | cl-frontend |
NEXT_PUBLIC_FRONTEND_URL | http://localhost:3000 | Public-facing frontend URL (available client-side) | cl-frontend |
NEXT_PUBLIC_ prefixed variables are embedded at build time in Next.js and available in the browser. BACKEND_INTERNAL_URL is server-side only and never exposed to the client.
Worker-Specific
| Variable | Default | Description | Service |
|---|---|---|---|
CELERY_CONCURRENCY | 2 | Number of parallel Celery worker processes | cl-worker |
Each concurrency slot processes one document at a time and makes multiple AI API calls. Higher concurrency means faster throughput but more memory usage and higher AI API costs. Recommended values:
- Development: 1-2
- Small team: 2-4
- Production: 4-8 (depending on available RAM and AI API rate limits)
Allow approximately 512 MB - 1 GB of RAM per concurrency slot.
Service Connectivity & Domain Configuration
Contract Lucidity consists of five services that must be able to communicate. Understanding how they connect is essential for a working deployment.
How Services Talk to Each Other
The frontend is the only public-facing service for normal user traffic. It serves the web application and proxies all /api/* requests to the backend internally. The backend, database, Redis, and worker are never exposed to the internet for user traffic.
SCIM Endpoint Routing
If you use SCIM provisioning, the identity provider needs to reach the backend's SCIM endpoint at /scim/v2. This path is not covered by the default frontend proxy (which only forwards /api/*).
You have two options:
Option A: Add a frontend rewrite rule (recommended)
Add a rewrite for /scim/* in your next.config.ts so the frontend proxies SCIM traffic to the backend, keeping a single public URL:
async rewrites() {
return [
{
source: "/api/:path*",
destination: `${process.env.BACKEND_INTERNAL_URL || "http://cl-backend:8000"}/api/:path*`,
},
{
source: "/scim/:path*",
destination: `${process.env.BACKEND_INTERNAL_URL || "http://cl-backend:8000"}/scim/:path*`,
},
];
},
With this approach, the SCIM endpoint URL you configure in your IdP is https://your-domain.com/scim/v2.
Adding rewrite rules requires rebuilding the frontend container: docker compose up -d --build cl-frontend. The next.config.ts file is not volume-mounted.
Option B: Expose the backend directly for SCIM
Expose the backend on a separate public URL (e.g., https://api.your-domain.com) and configure your IdP to use https://api.your-domain.com/scim/v2. This requires additional TLS configuration and firewall rules to restrict access to the SCIM endpoints.
Option A is simpler and keeps your deployment's single-URL architecture. Option B is useful if you cannot rebuild the frontend image or need to isolate SCIM traffic for security or audit reasons.
Setting Your Domain Name
Only one domain is needed — the domain pointing to your frontend service. Configure it in three places:
| Setting | Where to Set | Example Value |
|---|---|---|
| DNS record | Your DNS provider (Cloudflare, Route 53, etc.) | contracts.yourfirm.com → frontend IP |
FRONTEND_URL | cl-backend environment | https://contracts.yourfirm.com |
CORS_ORIGINS | cl-backend environment | https://contracts.yourfirm.com |
NEXT_PUBLIC_FRONTEND_URL | cl-frontend environment | https://contracts.yourfirm.com |
| TLS certificate | Your reverse proxy or cloud load balancer | Auto via Let's Encrypt, ACM, etc. |
The BACKEND_INTERNAL_URL on cl-frontend should point to the backend's internal address (e.g., http://cl-backend:8000 on Docker, or the private IP/hostname if services run on separate machines).
SSO callback URLs are derived dynamically from the incoming request. When a user initiates SSO login, the backend reads request.base_url to construct the callback URL. This means SSO automatically works with whatever domain traffic arrives on -- no separate SSO URL configuration is needed beyond what your identity provider requires.
The SCIM endpoint URL also uses request.base_url for the meta.location fields in SCIM responses. If you use a frontend rewrite (Option A above), SCIM responses will reference your frontend domain. If you expose the backend directly (Option B), they will reference the backend's domain. Both approaches work correctly with identity providers.
Single-Cloud Deployment (Recommended)
In a standard deployment, all five services run within the same cloud network:
| Deployment Model | Frontend | Backend + Worker | Database | Redis |
|---|---|---|---|---|
| Docker Compose | Same host | Same host | Same host | Same host |
| EasyPanel | Same server | Same server | Managed or same server | Same server |
| AWS | ECS / Fargate | ECS / Fargate | RDS PostgreSQL | ElastiCache |
| Azure | App Service | App Service | Azure Database for PostgreSQL | Azure Cache for Redis |
| GCP | Cloud Run | Cloud Run | Cloud SQL | Memorystore |
In all cases, services communicate over the cloud provider's private network (VPC, VNet, etc.). The only public endpoint is the frontend, which sits behind a load balancer or reverse proxy that terminates TLS.
Key connectivity requirements:
- cl-frontend must reach cl-backend on port 8000 (internal)
- cl-backend and cl-worker must reach cl-postgres on port 5432
- cl-backend and cl-worker must reach cl-redis on port 6379
- cl-worker uses the same database and Redis as cl-backend
- If using SCIM provisioning: the IdP must reach the SCIM endpoint (either via the frontend proxy or directly to the backend -- see "SCIM Endpoint Routing" above)
Cross-Cloud / Distributed Deployments
Cross-cloud deployments are not a supported configuration. This section is provided for information only, for organizations with specific infrastructure requirements.
It is technically possible to run services across cloud providers (e.g., frontend on Vercel, backend on AWS, database on GCP). If you choose this path:
-
The frontend still needs
BACKEND_INTERNAL_URL— but it will be an external HTTPS URL instead of an internal hostname. The backend must be exposed with TLS, andCORS_ORIGINSmust include the frontend domain. -
Database connectivity — set
POSTGRES_HOSTto the external database hostname. Use TLS for the connection (?sslmode=requireis recommended). Ensure firewall rules allow inbound connections from the backend's IP range. -
Redis connectivity — set
REDIS_URLto the external Redis URL with TLS (rediss://). Redis should be co-located with the backend and worker for latency reasons — document processing makes frequent Redis calls. -
Latency considerations — the backend and worker make many small database and Redis queries per document. Placing them in different regions from the database or Redis will significantly impact processing speed. Keep backend, worker, database, and Redis in the same region. The frontend can be in a different region with minimal impact.
-
Network security — all cross-cloud connections should use TLS. Consider VPN tunnels or cloud interconnects (AWS Direct Connect, Azure ExpressRoute, GCP Cloud Interconnect) for production workloads.
Complete .env Example
# Database
POSTGRES_USER=cl_user
POSTGRES_PASSWORD=your-strong-database-password
POSTGRES_DB=contract_lucidity
# Redis
REDIS_URL=redis://cl-redis:6379/0
# Authentication
JWT_SECRET_KEY=your-64-character-random-secret-here
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=60
JWT_REFRESH_TOKEN_EXPIRE_DAYS=7
# Application
APP_ENV=production
LOG_LEVEL=INFO
MAX_UPLOAD_SIZE_MB=100
# CORS / URLs
CORS_ORIGINS=https://contracts.yourcompany.com
FRONTEND_URL=https://contracts.yourcompany.com
BACKEND_INTERNAL_URL=http://cl-backend:8000
# Admin
DEFAULT_ADMIN_EMAIL=[email protected]
DEFAULT_ADMIN_PASSWORD=initial-strong-password
# Worker
CELERY_CONCURRENCY=4
Never commit .env files to version control. Add .env to your .gitignore.