Security
Authentication
Contract Lucidity uses JWT (JSON Web Token) bearer authentication with a refresh token flow.
Token Flow
Token Configuration
| Setting | Default | Recommendation |
|---|---|---|
JWT_ALGORITHM | HS256 | Sufficient for single-service deployments |
JWT_ACCESS_TOKEN_EXPIRE_MINUTES | 60 | Reduce to 15-30 for high-security environments |
JWT_REFRESH_TOKEN_EXPIRE_DAYS | 7 | Reduce to 1-3 for sensitive deployments |
The JWT_SECRET_KEY must be a strong, random value in production. Generate one with:
openssl rand -hex 64
The default value (change-me-to-a-random-secret-in-production) provides no security.
Password Security
- Passwords are hashed with bcrypt (via Passlib)
- The default admin account is created with
must_change_password=True - User roles:
admin(full access) anduser(project-scoped access)
File Upload Validation
When documents are uploaded, the backend validates:
- File size: Enforced by
MAX_UPLOAD_SIZE_MB(default: 100 MB) - File type: Restricted to configured types stored in
system_config(default:pdf, docx, png, jpg, jpeg, tiff) - Storage path: Files are written to the
cl-storagevolume at/data/storage, never to application directories
File type validation is enforced at both the frontend (UI-level) and backend (API-level). The accepted types are stored in the system_config table under the key supported_file_types and can be modified through the admin UI.
CORS Configuration
Cross-Origin Resource Sharing (CORS) is configured on the backend via the CORS_ORIGINS environment variable.
# Default configuration
CORS_ORIGINS=http://localhost:3000,https://contractlucidity.com
The backend applies CORS middleware with these settings:
- Allowed origins: Only origins listed in
CORS_ORIGINS - Credentials: Enabled (
allow_credentials=True) - Methods: All HTTP methods
- Headers: All headers
In production, restrict CORS_ORIGINS to only your deployment domain:
CORS_ORIGINS=https://contracts.yourcompany.com
Do not use wildcards (*) in production.
Network Isolation
Access Requirements by Service
| Service | Public Access | Internal Access | Notes |
|---|---|---|---|
| cl-frontend | Yes (via reverse proxy) | cl-backend | Only service that should face the internet |
| cl-backend | No | cl-frontend, cl-redis, cl-db | API not directly exposed; proxied through frontend |
| cl-worker | No | cl-redis, cl-db, AI APIs | No inbound connections needed |
| cl-redis | No | cl-backend, cl-worker | Internal task queue only |
| cl-db | No | cl-backend, cl-worker | Database traffic only |
The default docker-compose.yml publishes PostgreSQL (5432) and Redis (6379) ports to the host for development convenience. In production, remove or comment out these port mappings:
# Remove these lines in production:
# ports:
# - "5432:5432" # cl-postgres
# - "6379:6379" # cl-redis
AI API Key Security
AI provider API keys are:
- Stored encrypted in the
ai_providerstable (api_key_encryptedcolumn) - Never logged or included in API responses
- Configured via the UI (Settings > AI Capabilities), not in environment variables
- Scoped per capability — different keys can be used for different AI capabilities
Rotate AI provider API keys periodically. When you update a key in the UI, the old key is immediately replaced in the database.
Database Security
- PostgreSQL connections use the internal Docker network (
cl-network) — no traffic traverses public networks - Database credentials are set via environment variables, never hardcoded
- The
pgvectorextension is created automatically on first boot - Alembic migrations run on backend startup, ensuring the schema is always up to date
Recommended Production Settings
- Use a strong, unique
POSTGRES_PASSWORD(32+ characters) - Enable PostgreSQL SSL connections if the database is not co-located with application services
- Configure
pg_hba.confto restrict connections to the application subnet - Enable WAL archiving for point-in-time recovery
SSL/TLS
Contract Lucidity transmits JWT tokens and sensitive legal documents. TLS is mandatory for any non-development deployment.
Place a reverse proxy (Nginx, Caddy, Traefik, or a cloud load balancer) in front of cl-frontend to terminate TLS:
Internet → [TLS termination at :443] → cl-frontend:3000 → cl-backend:8000
All internal communication between services within the Docker network is unencrypted by default, which is acceptable when services share a private network. If services span multiple hosts, use mutual TLS or a service mesh.
Recommended Additional Measures
Firewall Rules
- Allow inbound only on port 443 (HTTPS)
- Allow outbound to AI provider APIs (443) and package registries
- Block all other inbound traffic
Rate Limiting
Apply rate limiting at the reverse proxy level:
- Login endpoint (
/api/auth/login): 5 requests per minute per IP - File upload (
/api/documents): 10 requests per minute per user - General API: 100 requests per minute per user
Web Application Firewall (WAF)
Consider a WAF (Cloudflare, AWS WAF, Azure Front Door) for:
- SQL injection protection (defense in depth — SQLAlchemy uses parameterized queries)
- Request body size limits
- Geographic access restrictions
- Bot detection
Audit Logging
Key actions are tracked via database timestamps:
- User login (
last_loginon users table) - Document uploads (
created_aton documents) - Clause decisions (
decided_at,decided_byon clause_decisions) - Report generation (
created_aton document_reports)
For comprehensive audit logging, configure your reverse proxy to log all requests with user identity headers.