Skip to main content

Security

Authentication

Contract Lucidity uses JWT (JSON Web Token) bearer authentication with a refresh token flow.

Token Flow

Token Configuration

SettingDefaultRecommendation
JWT_ALGORITHMHS256Sufficient for single-service deployments
JWT_ACCESS_TOKEN_EXPIRE_MINUTES60Reduce to 15-30 for high-security environments
JWT_REFRESH_TOKEN_EXPIRE_DAYS7Reduce to 1-3 for sensitive deployments
JWT Secret

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) and user (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-storage volume at /data/storage, never to application directories
tip

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
Production CORS

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

ServicePublic AccessInternal AccessNotes
cl-frontendYes (via reverse proxy)cl-backendOnly service that should face the internet
cl-backendNocl-frontend, cl-redis, cl-dbAPI not directly exposed; proxied through frontend
cl-workerNocl-redis, cl-db, AI APIsNo inbound connections needed
cl-redisNocl-backend, cl-workerInternal task queue only
cl-dbNocl-backend, cl-workerDatabase traffic only
danger

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_providers table (api_key_encrypted column)
  • 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
tip

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 pgvector extension is created automatically on first boot
  • Alembic migrations run on backend startup, ensuring the schema is always up to date
  • 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.conf to restrict connections to the application subnet
  • Enable WAL archiving for point-in-time recovery

SSL/TLS

Required for production

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.

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_login on users table)
  • Document uploads (created_at on documents)
  • Clause decisions (decided_at, decided_by on clause_decisions)
  • Report generation (created_at on document_reports)

For comprehensive audit logging, configure your reverse proxy to log all requests with user identity headers.