Security and data
Ce contenu n’est pas encore disponible dans votre langue.
Cortex is a self-hosted application. All data stays on your infrastructure unless you explicitly configure external connections (GenAI providers, optional embedding models).
Authentication
Section titled “Authentication”Cortex uses dual authentication on the REST API and a separate mechanism on the MCP server.
REST API authentication
Section titled “REST API authentication”| Method | How it works | Typical consumer |
|---|---|---|
| Bearer JWT (Supabase) | JWT signed by Supabase, verified server-side with SUPABASE_JWT_SECRET. Carries user email and role in app_metadata. | Web UI, human users |
| X-API-Key | Static key set via CORTEX_API_KEY env var. Checked in the X-API-Key header. | CI pipelines, scripts, services |
The middleware checks Bearer JWT first, then falls back to API key. If both are absent, the request is rejected with 401.
An email whitelist (ALLOWED_EMAILS env var) restricts which Supabase users can access the API. An empty whitelist disables JWT login entirely.
MCP server authentication
Section titled “MCP server authentication”| Method | How it works | Typical consumer |
|---|---|---|
| OAuth 2.0 | Full authorization flow with a PostgreSQL-backed provider. Discovery at /.well-known/oauth-authorization-server. | Claude.ai, ChatGPT, Cursor |
| X-API-Key | Same static key as the REST API. | Local development, testing |
The OAuth routes are mounted before the bearer auth middleware, allowing the authorization flow to complete before token verification kicks in.
Role-based access control (RBAC)
Section titled “Role-based access control (RBAC)”Cortex enforces a four-level role hierarchy on both API surfaces:
| Role | Level | Permissions |
|---|---|---|
| reader | 0 | Read catalogue, search, browse, health check |
| contributor | 1 | All reader permissions + draft workspace artefacts, deposit memory, delete documents |
| producteur | 2 | All contributor permissions + ingest blocs, profiles, governance atoms |
| admin | 3 | Full access including configuration, audit trail, reindex |
On the REST API, the role comes from the JWT’s app_metadata.role field (mapped to the Cortex hierarchy) or defaults to the API key’s configured role.
On the MCP server, every tool declares a minimum required role in the TOOL_MIN_ROLES map. Before a tool handler executes, the session’s role is checked. Insufficient role results in an immediate rejection.
Encryption at rest
Section titled “Encryption at rest”Provider credentials
Section titled “Provider credentials”GenAI provider API keys are encrypted with AES-256-GCM before storage:
| Property | Detail |
|---|---|
| Algorithm | AES-256-GCM |
| Key source | ENCRYPTION_KEY environment variable (base64-encoded, 32 bytes) |
| IV | Unique per encryption operation (random 12 bytes) |
| Auth tag | GCM authentication tag stored alongside ciphertext |
| Storage | PostgreSQL provider_credentials table |
| Masked view | Only prefix (4 chars) + mask + suffix (4 chars) returned to API/UI |
If ENCRYPTION_KEY is missing or malformed, the API refuses to start.
Database credentials
Section titled “Database credentials”SurrealDB and PostgreSQL credentials are managed through environment variables. They are never stored in Cortex’s own database.
Data residency
Section titled “Data residency”What stays on your infrastructure
Section titled “What stays on your infrastructure”| Data | Storage |
|---|---|
| Catalogue (atoms, blocs, profiles) | SurrealDB |
| Memory entries (decisions, patterns, errors) | SurrealDB |
| Documents and library | PostgreSQL + MinIO |
| Full-text search index | MeiliSearch |
| Provider credentials (encrypted) | PostgreSQL |
| Audit trail and events | PostgreSQL |
| Embedding model weights | Ollama (local) |
| Embeddings (vectors) | SurrealDB |
What leaves your infrastructure
Section titled “What leaves your infrastructure”| Destination | Data sent | When |
|---|---|---|
| GenAI provider (Google, OpenAI, Anthropic) | The decrypted API key + whatever the consuming application sends | Only when a service calls getDecrypted() and uses the key |
| Ollama (if remote) | Text to embed | Only if Ollama is configured on a remote host |
Cortex itself sends no telemetry, no analytics and no machine identifier to any external service.
CORS policy
Section titled “CORS policy”REST API
Section titled “REST API”The REST API’s CORS origin is controlled by the CORS_ORIGIN environment variable. Default: http://localhost:8080 (the UI).
MCP server
Section titled “MCP server”The MCP server allows origins listed in MCP_CORS_ORIGINS. Default: https://claude.ai,https://chatgpt.com. Override with a comma-separated list for additional LLM clients.
Network exposure
Section titled “Network exposure”By default, all services bind to 127.0.0.1 (localhost only). The Docker Compose example makes this explicit:
ports: - "127.0.0.1:9096:9096" # REST API - "127.0.0.1:3001:3001" # MCP serverThe UI service (cortex_ui) binds to 0.0.0.0:8080 to allow LAN access. For production, put a reverse proxy with TLS in front.
Recommendations for production
Section titled “Recommendations for production”- Set strong, unique passwords for SurrealDB, PostgreSQL, MeiliSearch and MinIO.
- Generate a proper ENCRYPTION_KEY with
openssl rand -base64 32and store it securely. - Use a reverse proxy (nginx, Caddy, Traefik) with TLS termination in front of the API and MCP server.
- Restrict
ALLOWED_EMAILSto known users if you use Supabase JWT authentication. - Set
CORTEX_API_KEYto a strong random value and rotate it periodically. - Limit
CORS_ORIGINandMCP_CORS_ORIGINSto the exact origins that need access. - Back up SurrealDB, PostgreSQL and MinIO regularly (see 08-deployment).
What next?
Section titled “What next?”- Deploy to production: 08-deployment
- Frequently asked questions: 09-faq