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.

Role-Based Access Control

Contract Lucidity enforces access control at two levels: user roles and project-level grants.

User Roles

RoleAccess
AdminFull access to all projects, settings, user management, groups, SSO configuration, playbook, and AI providers
UserCan only access projects they have been explicitly granted access to (directly or via a group)

Admin users bypass all project-level access checks. The user role is subject to project access filtering -- they will only see projects where a project_access record exists for their user ID or for a group they belong to.

Project-Level Access Control

Access to individual projects is managed through the project_access table, which supports three access levels:

Access LevelPermissions
viewerRead-only access to the project and its documents
editorUpload documents, make clause decisions, run analyses
adminAll editor permissions plus the ability to manage access for the project

Access can be granted to individual users or to groups. A user's effective access level is the highest level across all their grants (direct + group-based).

For full details, see Groups & Access Control.

SSO Security Benefits

When Single Sign-On is enabled, authentication is delegated to your organization's identity provider. This provides:

  • Centralized MFA enforcement -- multi-factor authentication is configured in the IdP, not managed per-user in CL
  • Conditional access policies -- restrict login by device, location, or risk level (IdP-dependent)
  • Centralized account lifecycle -- disabling a user in the IdP immediately prevents SSO login to CL
  • No password storage -- SSO users have no password hash in the CL database
  • Audit trail -- authentication events are logged in both the IdP and CL

For setup instructions, see Single Sign-On (SSO).

Permission Resolution Hierarchy

Contract Lucidity resolves access permissions in a strict 6-step order. The first matching rule wins:

  1. Ethical wall deny -- If the user is on an ethical wall that covers the project, access is denied unconditionally.
  2. User deny -- If the user has an explicit deny permission on the project, access is denied.
  3. Group deny -- If any of the user's groups has a deny permission on the project, access is denied.
  4. User allow -- If the user has a direct allow grant, access is allowed at the granted level.
  5. Group allow (highest) -- If any of the user's groups has an allow grant, access is allowed at the highest level across all groups.
  6. Default deny -- If no rule matched, access is denied.

This is a hybrid model: deny permissions always override allow permissions, and among allow grants, the most permissive level wins.

Permission Levels

LevelCan ViewCan Upload / EditCan Manage Access
viewerYesNoNo
editorYesYesNo
adminYesYesYes

Deny Overrides Allow

A single deny permission -- whether from an ethical wall, a direct user deny, or a group deny -- blocks all access to a project regardless of any allow grants. If a user has admin-level access through a group but also has a deny through any path, the result is deny.

Admin Role and Ethical Walls

Admin-role users bypass normal project-level access checks (they can see all projects). However, admin-role users do NOT bypass ethical walls. When an ethical wall screens an admin from a project, that admin cannot access the project. Only the seed admin (the break-glass recovery account) is exempt from ethical walls.

For full details on ethical walls, see Ethical Walls (Information Barriers).

Audit Logging

Key access control events are tracked in the audit log:

  • Ethical wall enforcement: When a user's request is blocked by a wall (user ID, project ID, wall ID, timestamp)
  • Permission changes: When access grants are created, modified, or revoked
  • Wall lifecycle: When walls are created, modified, deactivated, reactivated, or deleted
  • User login: Timestamp recorded on the user record (last_login)
  • Document uploads: Tracked with created_at on documents
  • Clause decisions: Tracked with decided_at and decided_by on clause_decisions

Audit logs for ethical wall events are available through the admin API at /api/admin/ethical-walls/audit-log and can be exported for compliance reporting.

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

For comprehensive audit logging beyond the built-in tracking described in the Audit Logging section above, configure your reverse proxy to log all requests with user identity headers.