Cortex is configured through environment variables. This page documents every available option.
Required Variables
These must be set for Cortex to function:
Neo4j Database
Code
LLM Provider
At least one LLM provider is required:
Code
Admin Authentication
Code
LLM Configuration
Model Selection
Code
Embedding Configuration
Code
Document Processing
Upload Settings
Code
Chunking Configuration
Code
Batch Processing
Code
BATCH_PROCESSING_CONCURRENCY applies to all processing, not just explicit batches: documents ingested via the API with start_processing=true (and single reprocesses) queue on the same global limit, so a burst of API ingests never runs more pipelines concurrently than this value. Waiting documents show "Queued — waiting for a free processing slot" until a slot frees up.
GraphRAG Configuration
Entity Extraction
Code
Relationship Model
Optional dedicated model for all relationship extraction — both per-chunk extraction during document processing (Step 1) and batch cross-document analysis (Step 2). Runs on a separate rate limit from entity extraction. Falls back to the extraction model, then the primary model.
Code
Relationship Analysis
Cross-document relationship discovery (Phase B / Step 2) using the relationship
model (falls back to extraction model → primary model). The default targeted
mode generates candidate entity pairs without the LLM — entity-embedding kNN
(missing embeddings are backfilled automatically) plus document co-mention —
then verifies them in small batched LLM calls. The legacy llm_scan mode runs
the two-phase full-batch scan (candidate scanning + structured confirmation)
with co-occurrence batching and multi-round discovery:
Code
Reasoning Control for ingestion
Reasoning hurts structured extraction (drift, hidden-token cost, latency, malformed JSON). These knobs let reasoning-capable models (GPT-5/5.1, Claude 4.x, Qwen3, DeepSeek-R1, MiniMax M3) be used for ingestion while suppressing their thinking. Cortex auto-detects the provider from base_url and the model family from the model name — works for OpenAI, OpenRouter, Venice, Anthropic, and self-hosted vLLM.
Accepted values: off | minimal | auto | low | medium | high (none/disabled are aliases for OFF).
Code
Forward compatibility. Same-family minor releases route automatically — gpt-5.8 works like gpt-5.1. For new majors (e.g. gpt-6) or models the heuristic misclassifies, set REASONING_MODEL_OVERRIDES. If the API rejects the param, the wrapper strips it on retry, logs a warning, and caches the model so future calls skip the param upfront.
Caveats. gpt-5-pro is hard-pinned to reasoning_effort=high (OFF is silently ignored, one-time WARN). gpt-5-codex auto-downgrades minimal→low. Anthropic Opus 4.7+ uses adaptive thinking — manual thinking returns 400, so the helper omits the param. OpenRouter exclude:true does NOT save tokens; we use effort:"none" instead.
Budget Fallback Chain
Every LLM call has two budgets that matter: output tokens (how much the model may write) and input context (how much input the call may consume). Sub-tier knobs default to 0 (= inherit from the next tier up) — except EXTRACTION_MAX_OUTPUT_TOKENS, which ships a real default of 12000 (set 0 explicitly to restore inherit). This lets you configure a multi-model stack with just two or three env vars.
Code
Recommended minimal stack — configure two models + two context windows; everything else inherits:
Code
The extraction *_MAX_CONTEXT override matters because batch sizing determines how many output tokens each extraction call must generate — and on hosted providers, generation speed (tok/s), not the model's input window, is what bounds a call. A model with a 256K window served at ~70 tok/s cannot answer a fully-packed prompt inside a request timeout. Cortex clamps the inherited extraction context at 48000 for this reason (an explicitly set GRAPH_EXTRACTION_MAX_CONTEXT is always honored as-is). Pair the input budget with an output cap of ≈ half its size: extraction re-emits every entity, so entity-dense documents overflow smaller caps (measured July 2026: ~10 overflow split-retries per book at 24000/8000; zero at 24000/12000). Overflows self-heal — the batch splits and retries — but each roughly doubles that batch's wall time, and the backend logs a one-shot "output budget looks too small" warning when they repeat. On very slow decode endpoints, lower GRAPH_EXTRACTION_MAX_CONTEXT instead of raising the output cap past what the decode rate delivers inside a request timeout. The embedding model uses the primary OPENAI_API_BASE + OPENAI_API_KEY unless overridden via EMBEDDING_API_BASE/EMBEDDING_API_KEY. EMBEDDING_SEND_DIMENSIONS=true (default) works because Qwen3-Embedding-8B supports MRL — the API honors the dimensions parameter to truncate to any value between 32 and 4096. EMBEDDING_MAX_INPUT_TOKENS defaults to 5400; oversized inputs are token-accurately sub-split client-side (tiktoken, zero content loss) to avoid HTTP 400 "Input text exceeds the maximum token limit" rejections, and chunks a provider still rejects are re-embedded individually with automatic halving.
Concurrency defaults. The shipped defaults (BATCH_PROCESSING_CONCURRENCY=2, CONCURRENT_EXTRACTIONS=3, CONCURRENT_RELATIONS=3, VISION_MAX_CONCURRENT=2) are production-validated. Resist raising BATCH_PROCESSING_CONCURRENCY: on providers with limited serving throughput, lower build concurrency finishes multi-document builds faster — concurrent long extraction calls share the provider's decode rate and can push each other past request timeouts (see "Provider Notes: Venice").
The two CONCURRENT_* knobs are per-document limits, so they compound with BATCH_PROCESSING_CONCURRENCY as documents move through the pipeline. The ingestion pipeline staggers extraction, per-chunk relationships, and vision across each doc's lifecycle, so actual in-flight concurrency stays below the worst-case theoretical product. Because they compound, raise them cautiously — CONCURRENT_EXTRACTIONS is the biggest multiplier (entity extraction is the heaviest call) and the first knob to lower on a slower provider. VISION_MAX_CONCURRENT is a global semaphore (system-wide) and does not multiply with BATCH_PROCESSING_CONCURRENCY.
Override examples:
Code
Migration note. The env var RELATIONSHIP_MAX_OUTPUT_TOKENS previously controlled the Phase 2 batch budget (16000). It now feeds the per-chunk + candidate scan chain. The Phase 2 batch budget moved to RELATIONSHIP_BATCH_MAX_OUTPUT_TOKENS (default still 16000, behavior unchanged for users who never set the legacy var). If you explicitly set RELATIONSHIP_MAX_OUTPUT_TOKENS=16000 in your .env, per-chunk extraction will run with a 16000 cap (harmless overkill); migrate to the new name when convenient.
Migration note. EXTRACTION_MAX_CONTEXT was renamed to GRAPH_EXTRACTION_MAX_CONTEXT to match the GRAPH_EXTRACTION_MODEL/GRAPH_EXTRACTION_API_BASE/GRAPH_EXTRACTION_API_KEY prefix convention. The legacy name is honored as a deprecated alias for one release; the backend logs a one-shot WARN at startup if your .env still uses it. Rename to GRAPH_EXTRACTION_MAX_CONTEXT at your convenience — value semantics are identical.
Provider Notes: Venice
Measured tuning guidance for running the ingestion stack against api.venice.ai (July 2026, qwen3-6-27b extraction tier + text-embedding-3-small):
Code
Size extraction batches to decode speed, not the context window. Extraction calls are output-heavy (structured entity/relationship records), and hosted decode throughput — measured at ~70 tok/s (p50) on qwen3-6-27b, lower under concurrent load — bounds how much output a call can produce before the request times out. A 24000-token input budget yields responses that complete reliably first-try; packing the model's full 256K window produces calls that never finish inside a timeout, and the pipeline's split-retry then re-does the work in halves (self-healing, but slow). The same physics is why lower build concurrency completes multi-document ingestions faster.
Embed input validation runs on Venice's tokenizer, not yours. The nominal 8192-token embed cap is enforced with a tokenizer that counts punctuation- and number-dense text (tables of contents, indexes, bibliographies) ~1.2–1.4× higher than cl100k — a chunk measuring 5,795 cl100k tokens client-side was rejected as over 8192. EMBEDDING_MAX_INPUT_TOKENS=5400 keeps client-side capping safely under the gateway's counter. Cortex also recovers rejected stragglers automatically: a rejected batch is retried per-chunk with automatic halving, so one oversized input can't strip embeddings from its batch neighbours.
Rate limits are generous; concurrency slots are the real ceiling. Measured per key: chat 150 req/min + 10M tokens/min, embeddings 500 req/min + 5M tokens/min, and roughly 20 concurrent request slots. A full ingestion build uses well under 10% of the request budgets — but long-running extraction calls hold concurrency slots for minutes, which is why the vision and build concurrency knobs above matter more than any rate limit.
Errors from Venice can arrive as HTTP 200 responses carrying an error envelope ({"error": ...} with no data payload). Cortex's ingestion pipeline detects and recovers from these; if you see TypeError-style failures on response parsing in custom integrations, check for this pattern before suspecting your client code.
Semantic Entity Resolution
Code
Search & RAG Configuration
Hybrid Search
Code
Re-ranking
Code
The local cross-encoder pulls ~780 MB into the process. It is lazy-loaded by default; the first reranked query's load is hidden behind the preceding LLM/search work. By default the model stays resident (RERANKER_IDLE_TTL_SECONDS=0) so it is never idle-unloaded; set a positive TTL to reclaim its memory after a period of inactivity at the cost of a reload on the next query.
Shared Model Services (cortex-helper)
Offload the cross-encoder and Docling converter to a service hosted once per physical machine (the cortex-helper repo), so many tenant stacks on that host don't each load their own copy. Falls back to the built-in local path automatically when unset or unreachable.
Code
Agentic RAG
Code
Agent-Based Research Pipeline
The agent pipeline replaces the legacy fixed-step agentic RAG with an LLM-driven
researcher/writer architecture. The researcher agent uses function-calling to
iteratively gather information via tools (knowledge_search, community_search,
entity_lookup, reasoning), then the writer synthesizes a streamed answer.
LLM requirement: Your model must support function calling / tool use (the OpenAI
tools parameter). Compatible models include GPT-4o, GPT-4o-mini, Claude, Mistral Large,
and Command R+. Many smaller or local models behind LiteLLM do not support this.
When to disable (ENABLE_AGENT_RESEARCH=false):
- Your LLM does not support function calling (e.g., local models via Ollama/vLLM without tool-use support)
- You want lower token usage — the agent pipeline uses 3-5x more tokens due to multiple researcher iterations
- You want lower latency — the legacy pipeline makes 2 LLM calls vs 4-8 for the agent
- You want deterministic behavior — the legacy pipeline follows a fixed decompose → search → synthesize path, while the agent decides dynamically what to search and when to stop
Code
Reasoning Visibility
Code
Community Detection
Code
Collections
Code
Git Integration
Connect GitHub, GitLab, and Gitea repositories as a knowledge source and (optionally) let the agent open pull requests. See the Git Integration feature guide for the full walkthrough.
Code
The backend image bundles the git binary. Connections are created and managed from Settings → Git Integration (admin only); the personal access token is stored server-side and never exposed to the agent.
Web Import (MDHarvest powered by Crawl4ai)
Harvest web pages into clean markdown and ingest them into the graph. Cortex calls a crawl4ai service over HTTP — run it once and point Cortex at it. See the Web Import feature guide for the full walkthrough.
Code
Run crawl4ai with docker run -d -p 11235:11235 --shm-size=1g -e CRAWL4AI_API_TOKEN=your-shared-token unclecode/crawl4ai:0.9.0 (the token is required for crawl4ai ≥ 0.9.0 — tokenless it binds 127.0.0.1 only; set the same value as CRAWL_SERVICE_TOKEN). It uses a headless browser pool (~4 GB RAM); keep port 11235 on a private network. A single crawl4ai instance can serve many Cortex deployments.
x402 Payments
Monetize the retrieval endpoints with pay-per-query micropayments via the open x402 standard. See the x402 Payments feature guide for the full walkthrough.
Code
Apps (in-instance app hosting)
Host self-contained web apps (built from the Cortex App Template) inside your instance. Off by default — when disabled, every app route returns 404 and the admin UI section is hidden entirely. See the Apps feature guide.
Code
Vision Model Configuration
Configure image analysis capabilities:
Code
Vision Model Options
| Variable | Required | Default | Description |
|---|---|---|---|
VISION_MODEL | No | - | Vision model for image analysis (e.g., gpt-4o, claude-3-5-sonnet) |
VISION_MODEL_API_BASE | No | OPENAI_API_BASE | API endpoint for vision model |
VISION_MODEL_API_KEY | No | OPENAI_API_KEY | API key for vision model |
VISION_MAX_CONCURRENT | No | 2 | Max concurrent vision API calls system-wide. Each in-flight image spawns a multi-call chain; provider concurrent-slot limits (~20/key) bind before RPM |
VISION_REASONING_MODE | No | off | Reasoning mode for the vision-model call. Same value set as the ingestion modes — see Reasoning Control for ingestion. Lets you use reasoning multimodal models (Qwen3-VL, GLM-V) as VISION_MODEL without <think> tokens in image descriptions |
When no vision model is configured, Docling's built-in image description (SmolDocling) is used automatically. See Image Analysis Guide for details.
Observability (Langfuse)
Optional LLM tracing and cost tracking via a self-hosted Langfuse instance. When the three credentials are set, every LLM, embedding, and vision call is traced (cost, tokens, latency, errors) and agentic Q&A flows are grouped into one trace per request — including Venice and OpenRouter traffic. Leave them blank to run untraced; the same image behaves identically with or without tracing (no keys = no tracing).
Code
Get the key pair from Langfuse → Project Settings → API Keys. Accurate USD cost requires per-model price definitions in the Langfuse project (Venice/OpenRouter models aren't in Langfuse's built-in catalog).
Content privacy. By default (LANGFUSE_LOG_EXTENDED=false) Cortex redacts all prompt, completion, tool, embedding, and vision text before it leaves the app — only structure reaches Langfuse (roles, model + params, tool names + argument keys, tokens, cost, latency, tags). This protects user content and keeps trace storage small. Set LANGFUSE_LOG_EXTENDED=true to log full content for local debugging.
Security
Deployment hardening & CORS
Code
With ENVIRONMENT=production, startup refuses to boot if NEO4J_PASSWORD is empty or the default password123, or if SESSION_SECRET is shorter than 32 characters while ADMIN_PASSWORD is set. CORS_ALLOWED_ORIGINS defaults to * (any origin, credentials disabled since auth is header-based); set an explicit comma-separated allowlist for production. Production also auto-disables the interactive API docs (/docs, /redoc, /openapi.json) so a directly-exposed backend doesn't leak its full API schema — override with EXPOSE_API_DOCS=true.
Prompt Security
Code
See the Security guide for how the layers fit together.
API Key Authentication
See Authentication Guide for details.
Frontend Configuration
Code
Resource Limits & Quota
Optional caps for capacity planning and multi-tenant deployments. All default to 0 = unlimited.
Code
MAX_QUERIES_PER_MONTH is unit-denominated: the quota is spent in internal LLM completions — every Q&A loop call plus every document/graph processing (extraction) call — not in raw user queries. Embedding calls are excluded. A single question can therefore consume several units (search + answer + any tool/skill rounds), and a query blocked by the prompt guard still costs 1 unit. The counter resets per UTC calendar month and is instance-wide. Current usage is surfaced on the instance status endpoint (monthly_usage_* fields).
Environment Variable Summary
| Variable | Required | Default | Description |
|---|---|---|---|
NEO4J_URI | Yes | - | Neo4j connection URI |
NEO4J_USER | Yes | - | Neo4j username |
NEO4J_PASSWORD | Yes | - | Neo4j password |
OPENAI_API_KEY | Yes | - | OpenAI API key |
OPENAI_MODEL | No | google-gemma-4-26b-a4b-it | Primary LLM for Q&A, research, chat |
OPENAI_MAX_OUTPUT_TOKENS | No | 8000 | Floor of the output-token budget chain |
OPENAI_MAX_CONTEXT | No | 256000 | Floor of the input-context budget chain (the value extraction inherits is clamped at 48000) |
EMBEDDING_MODEL | No | openai/text-embedding-3-small | Embedding model |
EMBEDDING_DIMENSION | No | 1536 | Embedding dimensions |
EMBEDDING_SEND_DIMENSIONS | No | true | Send dimensions param to embedding API. Set false for fixed-dim models |
EMBEDDING_API_BASE | No | OPENAI_API_BASE | API base URL for embeddings |
EMBEDDING_API_KEY | No | OPENAI_API_KEY | API key for embeddings |
CHUNK_SIZE | No | 500 | Tokens per chunk |
CHUNK_OVERLAP | No | 50 | Overlap tokens |
ENABLE_GRAPH_EXTRACTION | No | true | Enable GraphRAG |
ENABLE_SEMANTIC_ENTITY_RESOLUTION | No | true | Use embedding-based vector similarity for entity dedup (falls back to Levenshtein) |
GRAPH_EXTRACTION_MODEL | No | OPENAI_MODEL | Model for entity extraction and community summarization |
GRAPH_EXTRACTION_API_BASE | No | OPENAI_API_BASE | API base for extraction model |
GRAPH_EXTRACTION_API_KEY | No | OPENAI_API_KEY | API key for extraction model |
RELATIONSHIP_EXTRACTION_MODEL | No | GRAPH_EXTRACTION_MODEL | Model for relationship extraction |
RELATIONSHIP_EXTRACTION_API_BASE | No | GRAPH_EXTRACTION_API_BASE | API base for relationship model |
RELATIONSHIP_EXTRACTION_API_KEY | No | GRAPH_EXTRACTION_API_KEY | API key for relationship model |
CONCURRENT_RELATIONS | No | 3 | Concurrent per-chunk relationship extractions per document |
GRAPH_EXTRACTION_MAX_CONTEXT | No | 0 (=inherit) | Input context for entity extraction batching. Inherits min(OPENAI_MAX_CONTEXT, 48000) — clamped because extraction is decode-bound; recommended explicit 24000. Renamed from EXTRACTION_MAX_CONTEXT (deprecated alias still honored — startup WARN if used) |
EXTRACTION_MAX_OUTPUT_TOKENS | No | 12000 | Output budget for entity extraction — ≈ half of GRAPH_EXTRACTION_MAX_CONTEXT. Set 0 to inherit OPENAI_MAX_OUTPUT_TOKENS |
RELATIONSHIP_MAX_CONTEXT | No | 0 (=inherit) | Input context for Phase 2 batch (legacy llm_scan mode). Inherits GRAPH_EXTRACTION_MAX_CONTEXT → primary. Leave at 0 — wide values time out on self-hosted GPUs (prefill-bound) |
RELATIONSHIP_MAX_OUTPUT_TOKENS | No | 0 (=inherit) | Output budget for per-chunk + candidate scan (was Phase 2 batch in old releases — see migration note). Inherits EXTRACTION_MAX_OUTPUT_TOKENS |
RELATIONSHIP_BATCH_MAX_OUTPUT_TOKENS | No | 16000 | Output budget for Phase 2 batch (standalone, NOT in chain) |
VISION_MAX_OUTPUT_TOKENS | No | 0 (=inherit) | Output budget for image analysis. Inherits RELATIONSHIP_MAX_OUTPUT_TOKENS → extraction → primary |
PARALLEL_RELATIONSHIP_BATCHES | No | 5 | Parallel relationship analysis batches / verification calls |
RELATIONSHIP_DISCOVERY_MODE | No | targeted | Step 2 engine: targeted (kNN + co-mention candidates, LLM pair verification) or llm_scan (legacy full-batch scan) |
RELATIONSHIP_KNN_K | No | 8 | Targeted mode: nearest neighbors per entity in the vector-index scan |
RELATIONSHIP_KNN_MIN_SIMILARITY | No | 0.80 | Targeted mode: min vector-index score for a kNN candidate pair |
RELATIONSHIP_MIN_SHARED_DOCS | No | 2 | Targeted mode: min docs co-mentioning a pair (0 = disable generator) |
RELATIONSHIP_DOC_FREQ_CAP | No | 30 | Targeted mode: skip hub entities mentioned in more docs than this |
RELATIONSHIP_MAX_CANDIDATE_PAIRS | No | 15000 | Targeted mode: total candidate-pair budget per run |
RELATIONSHIP_CANDIDATES_PER_ENTITY | No | 10 | Targeted mode: max candidate pairs per entity (hub guard) |
RELATIONSHIP_PAIRS_PER_CALL | No | 40 | Targeted mode: candidate pairs verified per LLM call |
RELATIONSHIP_PAIR_CONTEXT_TOKENS | No | 3000 | Targeted mode: chunk-context tokens per verification call (0 = descriptions only) |
RELATIONSHIP_TARGET_RATIO | No | 1.0 | Target entity-to-relationship ratio (ERR) — legacy llm_scan mode only |
RELATIONSHIP_MAX_ROUNDS | No | 3 | Max discovery rounds per batch — legacy llm_scan mode only |
RELATIONSHIP_MAX_HOURS | No | 0 | Max hours for analysis (0 = no limit) |
RELATIONSHIP_MAX_PER_ENTITY | No | 50 | Soft cap on relationships per entity (0 = no cap) |
EXTRACTION_REASONING_MODE | No | off | Force reasoning OFF on extraction/summary/community calls. Values: off|minimal|auto|low|medium|high |
RELATIONSHIP_REASONING_MODE | No | off | Force reasoning OFF on candidate scan + relationship extraction. Same values as above |
VISION_REASONING_MODE | No | off | Force reasoning OFF on the vision model's image-description call. Same values as above |
DEFAULT_REASONING_MODE | No | off | Reasoning mode for the Q&A/answer path. Same values as above |
REASONING_MODEL_OVERRIDES | No | empty | Per-model override. Format: model1:mode1,model2:mode2. Example: gpt-5.8:none,custom:minimal |
ENABLE_HYBRID_SEARCH | No | true | Enable hybrid search |
ENABLE_RERANKING | No | true | Enable re-ranking |
RERANKER_PRELOAD | No | false | Eager-load cross-encoder at startup |
RERANKER_IDLE_TTL_SECONDS | No | 0 | Unload idle reranker after N s (0 = never unload, keep resident) |
RERANKER_SERVICE_URL | No | - | Offload reranking to cortex-helper |
DOCLING_SERVICE_URL | No | - | Offload Docling conversion to cortex-helper |
PROMPT_GUARD_SERVICE_URL | No | - | Offload query-time prompt-injection classifier to cortex-helper (/classify); empty = no remote guard (see PROMPT_GUARD_LOCAL) |
PROMPT_GUARD_THRESHOLD | No | 0.5 | Injection-class probability at/above which a question is refused |
PROMPT_GUARD_MODEL | No | leolee99/PIGuard | HuggingFace model id for the in-process (local) classifier |
HELPER_SERVICE_TOKEN | No | - | Shared secret for the helper service (reranker, docling, prompt-guard) |
ENVIRONMENT | No | development | production = fail fast on weak secrets |
CORS_ALLOWED_ORIGINS | No | * | Comma-separated CORS allowlist |
ENABLE_AGENTIC_RAG | No | true | Enable agentic mode |
ENABLE_AGENT_RESEARCH | No | true | Use agent pipeline for deep research (vs legacy) |
ENABLE_AGENT_CHAT | No | true | Use agent pipeline for standard chat (required for skills) |
RESEARCHER_MAX_ITERATIONS_SPEED | No | 3 | Agent loop iterations for chat mode |
RESEARCHER_MAX_ITERATIONS_QUALITY | No | 8 | Agent loop iterations for deep research |
WRITER_MAX_TOKENS_SPEED | No | 1200 | Max output tokens for chat answers |
WRITER_MAX_TOKENS_QUALITY | No | 4000 | Max output tokens for deep research answers |
RESEARCHER_SPEED_EARLY_WRITE | No | true | Chat skips the agent's final confirmation LLM call after a fruitful search |
RESEARCHER_PARALLEL_TOOL_CALLS | No | true | Read-only searches from one agent turn run concurrently |
RESEARCHER_TOOL_ENTITY_HINTS | No | true | Agent passes entities on search calls, skipping the extraction LLM call |
RESEARCHER_SEARCH_DEDUP | No | true | Identical repeat searches served from a per-question cache |
RESEARCHER_FORCE_REFLECTION | No | true | Deep research: force a reasoning step after search rounds the model didn't reflect on, steering the next round |
RESEARCHER_NOVELTY_MIN_NEW_RATIO | No | 0.35 | Deep research: a search round below this share of previously-unseen sources counts as stale (0 disables) |
RESEARCHER_NOVELTY_STALE_ROUNDS | No | 2 | Consecutive stale rounds before research stops and the answer is written (0 disables) |
EMIT_DONE_BEFORE_MEMORY | No | true | SSE done (with pending_memory: true) precedes memory compaction; memory_update follows |
ENABLE_COMMUNITY_DETECTION | No | true | Enable communities |
PROMPT_SECURITY | No | true | Master switch for prompt-injection detection, output filtering, and untrusted-content fencing |
PROMPT_GUARD | No | true | Runtime-toggle default for the query-time Prompt Guard classifier (active when PROMPT_GUARD_SERVICE_URL set or PROMPT_GUARD_LOCAL=true; admin-overridable) |
PROMPT_GUARD_LOCAL | No | false | Load the Prompt Guard classifier in-process when no service URL is set (dev / self-host; needs torch, adds resident RAM). Ignored when the service URL is set |
ENABLE_INGESTION_INJECTION_SCAN | No | false | Experimental. Master flag for the ingestion-time document injection scan. When off (default) the feature is completely absent: no scan runs and the admin toggle is hidden |
INGESTION_INJECTION_SCAN | No | true | Runtime-toggle default for the scan's LLM classifier; only meaningful when ENABLE_INGESTION_INJECTION_SCAN=true |
VISION_MODEL | No | - | Vision model for image analysis |
VISION_MODEL_API_BASE | No | OPENAI_API_BASE | Vision model API endpoint |
VISION_MODEL_API_KEY | No | OPENAI_API_KEY | Vision model API key |
VISION_MAX_CONCURRENT | No | 2 | Max concurrent vision API calls |
ADMIN_EMAIL | Yes | - | Admin login email |
ADMIN_PASSWORD | Yes | - | Admin login password |
ADMIN_API_KEY | Yes | - | Admin API key |
API_KEY_CACHE_TTL_SECONDS | No | 30 | In-process cache for successful API-key validations (0 = disabled). Key changes invalidate it immediately; on multi-worker deployments a revocation propagates within the TTL |
SESSION_SECRET | Yes | - | JWT session secret |
SESSION_COOKIE_SECURE | No | secure in production builds | Secure flag on the admin session cookie (true/false), read at container runtime. Set false only when serving the dashboard over plain HTTP (no TLS in front) — browsers silently drop Secure cookies on HTTP, so login appears to do nothing |
ENCRYPTION_KEY | No | - | At-rest encryption for git PATs + skill secrets (comma-separated Fernet keys; first encrypts, all decrypt) |
LANGFUSE_BASE_URL | No | - | Langfuse instance URL (enables LLM tracing when set with the key pair) |
LANGFUSE_PUBLIC_KEY | No | - | Langfuse project public key |
LANGFUSE_SECRET_KEY | No | - | Langfuse project secret key |
LANGFUSE_TRACING_ENABLED | No | true | Master off-switch for tracing even when keys are set |
LANGFUSE_SAMPLE_RATE | No | 1.0 | Trace sampling rate (0.0–1.0) |
LANGFUSE_LOG_EXTENDED | No | false | Content logging mode. false redacts all prompt/completion/tool/embedding/vision text before export (structure only); true logs full content for debugging |
NEXT_PUBLIC_LOGO_URL | No | - | Custom logo URL |
ACCENT_COLOR | No | - | Custom UI accent color (any CSS color). Read server-side at runtime — not NEXT_PUBLIC_-prefixed, so no rebuild needed |
MAX_FILES | No | 0 | Max total documents (0 = unlimited) |
MAX_COLLECTIONS | No | 0 | Max collections (0 = unlimited) |
MAX_ENTITIES | No | 0 | Max total entities (0 = unlimited) |
MAX_QUERIES_PER_MONTH | No | 0 | Monthly quota in internal LLM completions ("units"); Q&A + processing calls, embeddings excluded; UTC month, instance-wide (0 = unlimited) |
Validation
Verify your configuration is working:
Code
Efficiency Flags (v-next)
None change API shapes or graph semantics; each can be disabled per stack.
| Variable | Default | Description |
|---|---|---|
ENTITY_DEDUP_PREFILTER | true | Levenshtein entity dedup scores only the top-50 fulltext-index candidates instead of scanning every entity. Set false to restore the full scan. |
ENABLE_BATCHED_KG_WRITES | true | Write entities/links/relationships via UNWIND batches (~10 Neo4j round trips per document instead of hundreds), preserving per-item dedup semantics. Set false to restore per-item writes. |
ENABLE_BATCHED_CHUNK_RELATIONSHIPS | true | Pack several chunks into one per-chunk relationship-extraction LLM call (several times fewer Step 1 LLM calls at equivalent extraction quality; especially effective under provider rate limits). Set false to restore one call per chunk. |
RELATIONSHIP_CHUNKS_PER_CALL | 4 | Max chunks per batched relationship-extraction call. |
ENABLE_PHASEB_CHECKPOINTING | false | Persist Phase B batch progress — crash/redeploy resumes; rounds 2+ reuse round 1's candidate scan. |
ENABLE_REPROCESS_DELTA | false | Skip reprocessing when file bytes + extraction config are unchanged since the last successful run. |
RESEARCHER_STABLE_PROMPT | true | Keep the researcher system prompt byte-stable across loop iterations (provider prefix caches hit from iteration 2). |
ENABLE_PROMPT_CACHE_CONTROL | false | Anthropic cache_control breakpoints when routed via OpenRouter to anthropic/* models. |
Observability, Limits & Resilience
| Variable | Default | Description |
|---|---|---|
LOG_FORMAT | plain | plain keeps the legacy log format; json emits structured lines with request_id (from/echoed as X-Request-ID). |
METRICS_ENABLED | true | Prometheus metrics at GET /metrics (admin API key required; not exposed through the prod nginx). |
EXPOSE_API_DOCS | auto | Interactive API docs (/docs, /redoc, /openapi.json). auto = on in development, off in production (avoids unauthenticated API-schema disclosure on a directly-reachable backend). Set true/false to force. |
RATE_LIMIT_QPM | 0 | Per-API-key requests/minute on ask/upload endpoints (0 = off). 429 + Retry-After on excess. |
RATE_LIMIT_BURST | 10 | Token-bucket burst capacity for RATE_LIMIT_QPM. |
MAX_REQUEST_BODY_MB | 32 | Global request-body ceiling (Content-Length precheck + streamed cap → 413). Upload routes get MAX_FILE_SIZE_MB + 8MB multipart slack instead. 0 disables. |
MAX_IMPORT_BODY_MB | 2048 | Body ceiling for /api/admin/import* (ZIPs stream to disk — a disk guard, not a RAM guard). 0 = unlimited. |
MIN_FREE_DISK_MB | 500 | Free-space floor for the uploads filesystem: uploads, reprocessing, and import sessions are refused with 507 when accepting them would leave less free (disk-full corrupts Neo4j checkpoints). Disk gauges + rejection counter exposed in /metrics; disk_free_mb/disk_total_mb on GET /api/stats. 0 disables. |
LLM_REQUEST_TIMEOUT_SECONDS | 360 | Timeout applied to every LLM client the backend builds (replaces the SDK's 600s default). For streaming, bounds the gap between chunks, not the whole stream. 0 restores the SDK default. |
LLM_MAX_RETRIES | 2 | Retry count for LLM transport errors (SDK-level). |
AUTO_RESUME_PENDING_ON_STARTUP | true | When a restart kills a pipeline run, resume the interrupted step on boot (quota-guarded): documents stranded mid-processing, a queued batch that never started, or an interrupted relationship-analysis / community-detection step. The remaining Generate Graph chain is persisted with each task, so a resumed Step 1 still continues into Steps 2 and 3. Bulk uploads parked with start_processing=false are never auto-started. |
ENABLE_AUDIT_LOG | false | Append-only JSONL audit trail: auth failures, key-attributed mutating requests, search/ask activity. Metadata only — never document content or query text. |
AUDIT_LOG_PATH | ./logs/audit.log | Audit trail location; rotates at 50 MB keeping one .1 generation. |
CORTEX_NEO4J_TX_TIMEOUT | 300s | (Compose-level) server-side Neo4j transaction timeout — kills runaway queries so they can't pin connections forever. |
RESEARCHER_WALL_CLOCK_SECONDS | 120 | Wall-clock budget for the researcher loop (0 = unlimited); on expiry the writer synthesizes from gathered results. Guards against provider queue spikes so the answer always starts; raise or disable for slow self-hosted inference. |
RERANK_TOP_K | 15 | Candidates kept/reranked per knowledge search. |
HELPER_STRICT_REMOTE | false | With DOCLING_SERVICE_URL set: conversion failure marks the document failed instead of falling back to local docling. |
DOCLING_CONVERSION_TIMEOUT | 600 | Hard ceiling (seconds) on a single local docling subprocess conversion. On timeout the worker is killed and the document is marked failed instead of hanging in processing on a large/corrupt file. Does not apply to the remote DOCLING_SERVICE_URL path. |
INSTANCE_ID | hostname | Stack identity sent to cortex-helper (X-Tenant-ID) for fair queuing. |
NEO4J_MAX_POOL_SIZE | 100 | Neo4j driver connection pool size. |
NEO4J_CONNECTION_TIMEOUT | 10 | Neo4j TCP connect timeout (seconds). |
NEO4J_CONNECTION_ACQUISITION_TIMEOUT | 60 | Max wait for a pooled connection (seconds). |
Compose-level: NEO4J_MEM_LIMIT (default 4g), NEO4J_HEAP_INITIAL/NEO4J_HEAP_MAX/NEO4J_PAGECACHE, FRONTEND_MEM_LIMIT (1g). Backups: overlay docker-compose.backup.yml with BACKUP_INTERVAL_SECONDS (86400), BACKUP_RETENTION_DAYS (7), BACKUP_INITIAL_DELAY_SECONDS (120), NEO4J_ENTERPRISE_BACKUP (false); the export is written server-side and each run is verified (checksums + row counts) before retention rotates.
Slim image: build with --build-arg INSTALL_LOCAL_ML=false for a torch-free backend (~1.2 GB) when reranking + conversion are offloaded to cortex-helper. Requires OpenAI embeddings; pair with HELPER_STRICT_REMOTE=true.