CortexDB Docs
Operations

Configuration Foundations

How CortexDB resolves configuration — the cortex.toml file, environment variables, CLI flags, precedence rules, and reload semantics.

This page covers the mechanics — where files live, what overrides what, and what happens on reload. For tuning advice, see the per-subsystem pages.

Resolution order

When the cortexdb binary starts, configuration is resolved in this order. Each step overrides the previous.

  1. Compiled defaults — the default_*() functions. What you get with zero config.
  2. <data_dir>/cortex.toml — loaded at startup. Missing fields fall back to compiled defaults; a missing file falls back to all defaults (a warning is logged).
  3. Environment variables — read inline by each subsystem on first use. Most overrides are honored at startup; a handful (recall-tuning knobs) are read per-request.
  4. CLI flags — used only for cluster topology (--node-id, --rpc-addr, --gossip-addr, --seed-nodes, --rf, --port, --data-dir, --peers). No CLI flag duplicates a TOML field or env var.

The data_dir is resolved from the second positional CLI argument (or --data-dir= in cluster mode), defaulting to cortexdb_data single-node. Once data_dir is known, the config file is always read from <data_dir>/cortex.toml.

The cortex.toml file

Location: <data_dir>/cortex.toml. Changing your data directory also moves the config file — point at a fresh data_dir and you get pristine defaults.

Schema

The top-level sections are:

SectionPurpose
[cluster]Node identity, peer set, replication factor, vnodes
[storage]Data path, WAL sync mode, disk watermarks
[engine]RocksDB cache, HNSW shape, vector dims, quantization
[network]API / gossip / gRPC ports, request timeouts
[llm]Default LLM provider, model, fallback chain
[governance]Retention, PII handling, audit logging
[scheduler]Background job intervals (compaction / methylation / drain)
[blob_store]Local / S3 / GCS / Azure blob backend + KMS
[content_processors]Image / audio / video / document / sensor handlers
[security]Encryption, TLS/mTLS, RBAC, rate limits, breach detection, field masking
[compliance]Data residency, consent, classification, DSAR, SIEM, cross-agent
[deployment]Profile preset + per-feature gate overrides

Twelve sections, not fourteen

The schema enumerates 12 top-level sections (above). Every field uses serde(default = "..."), so partial files are valid — you only need to specify what you want to change. Empty sections mean "use all defaults for this section."

A minimal override file (empty sections = defaults; the one non-default field disables the scheduler for repeatable benchmark runs):

[storage]
[engine]
[network]
[llm]
[governance]

[scheduler]
enabled = false

Validation

Config loading returns a validation error (and refuses to start) if a field is out of range — examples:

  • engine.vector_dimensions not in {256, 384, 512, 768, 1024, 1536, 3072}
  • engine.hnsw_m outside [4, 64]
  • storage.max_disk_usage_percent > 100
  • cluster.replication_factor == 0
  • scheduler.compaction_interval_secs < 30

A bad config file fails fast at startup rather than silently using defaults. Watch the logs on first boot.

Reload semantics

cortex.toml is read once at startup. There is no SIGHUP reload — to change a TOML field, restart the binary. Most fields (vector_dimensions, data_path, cluster topology) cannot be safely changed at runtime. For tunables you do want to change without restart, use the env-var overrides in Recall Tuning (read on the request path).

Environment variables

Roughly 100 variables exist, grouped by prefix:

PrefixSubsystemExamples
CORTEX_LOG_*LoggingCORTEX_LOG_FORMAT=json
CORTEX_EMBEDDING_*EmbeddingsCORTEX_EMBEDDING_MODEL, CORTEX_EMBEDDING_DIMS
CORTEX_LLM_*Entity-extraction LLMCORTEX_LLM_MODEL=gpt-4o-mini
CORTEX_ANSWER_*Answer-generation LLMCORTEX_ANSWER_MODEL=...
CORTEX_VERIFIER_*Optional answer verifierCORTEX_VERIFIER_MODEL=gpt-4.1
CORTEX_ENRICHMENT_*Async KG enrichment LLMCORTEX_ENRICHMENT_MODEL= (off by default)
CORTEX_BLOB_*Blob storage backendCORTEX_BLOB_PROVIDER=s3
CORTEX_IMAGE_*, CORTEX_AUDIO_*, …Content modality processorsCORTEX_AUDIO_MODEL=whisper-1
CORTEX_RERANKER_*Cross-encoder rerankerCORTEX_RERANKER_PROVIDER=cohere
CORTEX_*_DISABLEKill switchesCORTEX_SCHEDULER_DISABLE=1
CORTEX_METHYLATION_*, CORTEX_CONSOLIDATION_*Memory-evolution thresholdsCORTEX_METHYLATION_MIN_ACCESS=10
CORTEX_GRAPH_RETRIEVAL_*, CORTEX_MULTIHOP_*, CORTEX_HYDE_*Recall-strategy tuningCORTEX_GRAPH_RETRIEVAL_TOP_K=40
CORTEX_SYNC_*Synchronous-pipeline kill switchesCORTEX_SYNC_FACT_EXTRACT_DISABLE=1
CORTEX_MS_*Multi-session executor flagsCORTEX_MS_COUNT_RELEVANCE_ENABLE=1

CORTEX_ANSWER_MODEL default is a cloud value

claude-opus-4-6 is the managed-cloud default answer model. Self-hosted, set CORTEX_ANSWER_* to your own provider; see Self-hosting defaults.

A few env vars use the ecosystem's own name: OPENAI_API_KEY (embedding key fallback), ANTHROPIC_API_KEY (fallback for CORTEX_ANSWER_API_KEY when provider is anthropic), COHERE_API_KEY (when CORTEX_EMBEDDING_PROVIDER=cohere), LLM_API_KEY (generic fallback), RUST_LOG (tracing filter; the image's default includes cortex_embedding=warn, so the embedding clamp and retry warnings reach the log).

Context, enrichment, erasure and code-plane settings

Defaults marked ✓ are printed in the boot config_lint dump; the others are compiled in. Leave the defaults unless a row says otherwise.

Recall and answer context

VariableDefaultWhat it does
CORTEX_RECALL_BUDGET_EXCERPTSonBudget excerpts and the strict context budget; 0, off or false restores the pre-0.10.1 render and withholds budget_excerpt_v1 from capabilities[] (logged as RECALL_BUDGET_EXCERPTS_DISABLED). See Recall
CORTEX_EVIDENCE_ROLE_CLASSIFIERpackerAnswer-context evidence classifier (full, legacy)
CORTEX_CONTEXT_STRIP_ECHOpairedDrop an assistant turn's echo of a user turn rendered in the same context (all, 0)
CORTEX_CONTEXT_RENDER_SKIP_OVERSIZEDonSkip an event too large for the remaining budget and keep packing
CORTEX_FACET_PACK_POLICYtieredFacet merge policy (legacy; other values are for evaluation)
CORTEX_CONTEXT_ASSISTANT_EXCERPTSoffOpt-in excerpts of assistant turns (1, ms)
CORTEX_CONTEXT_ASSISTANT_EXCERPT_MAX_BYTES600Per-event cap of those excerpts
CORTEX_QUERY_VARIANTS_FUSIONinterleaveHow query_variants are fused (depth_cap, deep_rrf for evaluation)
CORTEX_QUERY_VARIANTS_DEPTH10Depth for depth_cap (evaluation)
CORTEX_QUERY_EMBED_CACHE_CAP512 ✓Query-embedding cache entries (0 disables); unchanged, now documented
CORTEX_EMBEDDING_CACHE_DISABLEunset ✓Measurement only: bypass the provider embedding cache
CORTEX_CHUNK_FRAGMENTSonWrite the chunk fragment rows behind matched_fragments

Enrichment and extraction (enrichment-on deployments)

VariableDefaultWhat it does
CORTEX_ENRICHMENT_SKIP_NON_ENoff ✓Skip LLM enrichment for non-Latin-script text
CORTEX_POLARITY_RECHECKonThe polarity re-check call (0 records the doubt without the call)
CORTEX_POLARITY_QUOTE_TRIGGER_ENonThe English quote trigger of that re-check
CORTEX_ENRICHMENT_MAX_LLM_CALLS_PER_EVENT(not in the dump)Caps the enrichment-model calls one event may use; the polarity re-checks count against it
CORTEX_ENRICHMENT_SCOPE_QUIET_MS0 (off)Hold a busy scope's captures for one batched extraction call (at most 60000)
CORTEX_ENRICHMENT_SCOPE_QUIET_MAX_MS30000Longest hold of that window

Forget and erasure (see Erasures)

VariableDefaultWhat it does
CORTEX_DESTRUCTIVE_LEASE_WAIT_SECS120 ✓How long a new-scope capture waits for a running forget or erasure (1 to 3600)
CORTEX_SHUTDOWN_DESTRUCTIVE_WAIT_SECS60 ✓How long a graceful stop waits for running forgets and erasures (0 to 3600; 0 skips)
CORTEX_ERASURE_RESIDUALonThe background pass that compacts erased bytes out of the storage files (0 turns it off and logs ERASURE_RESIDUAL_DISABLED)
CORTEX_ERASURE_RESIDUAL_SHUTDOWN_SECS20How long a graceful stop drains that pass
CORTEX_ERASURE_RESIDUAL_MAX_BYTES536870912SST bytes one pass may rewrite per store
CORTEX_ERASURE_RESIDUAL_FULLTEXT_MAX_DOCS1000000Largest full-text segment one pass merges
CORTEX_ERASURE_RESIDUAL_MAX_PENDING_KEYS131072Recorded keys waiting per store
CORTEX_ERASURE_RESIDUAL_MIN_INTERVAL_MS30000Minimum gap between passes
CORTEX_ERASURE_RESIDUAL_DEBOUNCE_MS1000Quiet time before a pass starts
CORTEX_ERASURE_RESIDUAL_PARKED_RECHECK_SECS900Recheck interval of a parked over-budget segment
CORTEX_ERASURE_PERIODIC_COMPACTION_SECSunsetPeriodic compaction backstop (for example 86400)
CORTEX_ERASURE_INDEXonRead the erasure index (0: kept up to date but never read)
CORTEX_ERASURE_INDEX_BACKFILLonBackfill the index at the first start on an older store
CORTEX_ERASURE_INDEX_BACKFILL_ROWS20000Rows per backfill step
CORTEX_ERASURE_INDEX_BACKFILL_PAUSE_MS20Pause between backfill steps

Request log (see Request log and request ids)

VariableDefaultWhat it does
CORTEX_REQUEST_LOGslowOne log line per request: slow, all or off
CORTEX_REQUEST_LOG_SLOW_MS1000The slow threshold in milliseconds (0 logs every request)

Code plane, console and evaluation

VariableDefaultWhat it does
CORTEX_CODE_REPO_ROOTSunset ✓Directories repositories may be registered from; unset allows any readable directory (with a startup warning), set-but-empty refuses every registration. See Code Plane
CORTEX_UPDATE_FEED_URLGitHub ReleasesThe console's update check; off (also 0, false, none, disabled) turns it off. See Admin Console
CORTEX_ABSTAIN_SELF_RESCUEonAnswer self-rescue pass; new opt-in value guarded, 0 turns it off
CORTEX_EVAL_FACET_PLAN_OVERRIDEoffEvaluation only. Never on a production server
CORTEX_EVAL_DUMP_ANSWER_POOLSoffEvaluation only; writes customer content to disk. Never on a production server

Boolean conventions

Boolean env vars accept 1/0, true/false (case-insensitive); empty/unset means default. Most boolean knobs are kill switches — the feature starts on and you set =1 to disable (CORTEX_LLM_DISABLE, CORTEX_SCHEDULER_DISABLE, CORTEX_GRAPH_RETRIEVAL_DISABLE, …). The exceptions start off and you opt in — the _ENABLE suffix is the giveaway (CORTEX_AUTO_ROUTE, CORTEX_ENTITY_VECTOR_SEED_ENABLE, CORTEX_MS_COUNT_RELEVANCE_ENABLE, CORTEX_FACT_EVENT_PROMOTION_ENABLE).

CLI flags

Only 10 flags exist, all about cluster topology. Single-node operators need none — cortexdb with no arguments binds the v1 API on port 3141 and writes to ./cortexdb_data.

Single-node mode

cortexdb [PORT] [DATA_DIR]
# All defaults: API on 3141, data in ./cortexdb_data
cortexdb

# Custom port, custom data dir
cortexdb 4000 /var/lib/cortexdb

The server exposes a single public port — the v1 API (plus the bundled Admin Console) on 3141 by default.

Cluster mode (experimental — not operational)

Clustering is refused at boot

The cluster flags exist in the binary, but the distribution layer is not operational (no request forwarding, no replication, no membership). A complete set of cluster flags is a boot refusal: the process exits with code 78 and FATAL [CLUSTER_MODE_UNSUPPORTED]: cluster mode is not supported in this release … Remove the cluster flags and run single-node. Single-node is the supported deployment.

Cluster mode is selected by passing --node-id, --rpc-addr, --gossip-addr and --peers together (or their CORTEX_CLUSTER_NODE_ID, CORTEX_CLUSTER_RPC_ADDR, CORTEX_CLUSTER_GOSSIP_ADDR, CORTEX_CLUSTER_PEERS env forms). --peers lists the other members as ID:HOST:PORT:

# documented for recognition only: this exits 78 with CLUSTER_MODE_UNSUPPORTED
cortexdb \
  --node-id=1 \
  --rpc-addr=10.0.0.1:7100 \
  --gossip-addr=10.0.0.1:7000 \
  --peers=2:10.0.0.2:7000,3:10.0.0.3:7000 \
  --rf=3 \
  --port=3141 \
  --data-dir=/data/cortex/node1

A partial set (some cluster flags but not all four) is also fatal, with exit 78 and CLUSTER_FLAGS_INCOMPLETE; a malformed peer entry (for example host:port without the numeric id) is CLUSTER_NODE_ID_INVALID. There is no silent fallback to single-node any more: to run single-node, pass no cluster flags at all.

Inspecting what's actually loaded

On startup, CortexDB logs the resolved scheduler config:

scheduler configuration loaded
  enabled=true
  compaction_interval_secs=300
  methylation_interval_secs=600
  cognitive_persist_interval_secs=60
  feedback_weight_interval_secs=120

For everything else:

# Liveness (always 200 while the process is up) → {"status":"healthy","version":"v0.10.2"}
curl http://localhost:3141/v1/admin/health

# Readiness — data-dir writability + storage handles, the pinned embedding provider,
# and degraded:true on mock corpora. 200 ready / 503 not. Point healthchecks here.
curl http://localhost:3141/v1/admin/ready

# Version and build info
curl http://localhost:3141/v1/admin/version

# Full metrics snapshot (JSON) — storage + vector_indexes blocks show what actually loaded
curl http://localhost:3141/v1/admin/metrics?format=json

Checking which settings loaded

There is no config-read endpoint (GET /v1/admin/config answers 404). To confirm an env var took effect, read the boot-time config_lint log lines (~85 of them — the real "what loaded" surface) and the storage / vector_indexes blocks of GET /v1/admin/metrics?format=json.

Reading the config_lint dump

config_lint is the closest thing to a live config endpoint. It needs no setup: the image ships a default RUST_LOG that includes it, so every boot prints the block.

docker logs cortexdb 2>&1 | grep config_lint

Each line carries the variable, its effective value, and where that value came from:

config CORTEX_VECTOR_SHARDS = 4 (env)
config CORTEX_VECTOR_RESIDENT_MAX = unset (uncapped hot tier) (default; cortex-coordinator/src/production.rs::resident_max_from_env)
  • (env) — your setting was read and is in force. This is the definitive answer to "did my variable take effect?"
  • (default; <file>::<fn>) — the compiled default is in force, and the trailing path names the code that resolved it.

Some entries also emit a runtime sizing warning computed against the machine it booted on, for example flagging that an uncapped hot vector tier can outgrow available memory and suggesting the matching watermark variable. Read those before a production rollout.

Absence from config_lint does not mean a variable is wrong

The dump covers roughly 85 variables, which is a subset of the CORTEX_* surface — plenty of real settings (the embedding, answer and verifier lanes among them) never appear. So a variable missing from the block is inconclusive, while a variable shown as (env) is confirmed. To check whether a name is read by the server at all, see the note on unread variables in Recall Tuning.

Startup warnings

Some settings log an advisory WARN at boot. It never stops the server, and the defaults log none of them. Each line carries a code="…" field you can alert on:

docker logs <container> 2>&1 | grep 'code="'
CodeLogged when
ERASURE_RESIDUAL_DISABLEDCORTEX_ERASURE_RESIDUAL=0: erased bytes stay in the storage files until the engines compact on their own
ENGLISH_ONLY_PLANE_ENABLEDCORTEX_CODE_PLANE (or the artifact and summary planes) is on; those planes use English-only cues
ERASURE_RESIDUAL_LEGACY_WALThe legacy dual WAL (CORTEX_UNIFIED_WAL=0), which the residual pass can't cover
DOCUMENT_PROVIDER_FORGET_RESIDUEA server document processor is configured; a forget by id leaves its whole-document derived text
DOCUMENT_PROVIDER_NATIVE_DEAD_LETTERSCORTEX_DOCUMENT_PROVIDER=native, which yields no usable text for PDF, DOCX and the like
ENTITY_GRAPH_ENRICHMENT_FALSE_MERGEThe entity graph is on with enrichment; the graph resolver can merge near-identical names
ENRICHMENT_RESOLVER_ERASURE_RESIDUEEnrichment on with the graph off; an erased capture's entity names stay in the resolver's files
RECALL_BUDGET_EXCERPTS_DISABLEDCORTEX_RECALL_BUDGET_EXCERPTS is 0, off or false: recall renders as before v0.10.1 and budget_excerpt_v1 is not listed

With the code plane on and CORTEX_CODE_REPO_ROOTS unset, the server also warns that any readable directory may be registered.

The embedding client also warns, without a code, when it clamps the batch size (target cortex_embedding):

WARN cortex_embedding: CORTEX_EMBEDDING_MAX_BATCH_ITEMS=2048 clamped to 340: one response for 2048 x 1536-dim vectors would exceed the embedding client's response byte limit; each provider call carries at most 340 texts configured=2048 effective=340 model="text-embedding-3-small" dims=1536

It tells you the value in effect and is harmless. At 3072 dimensions the image's own 256 is above the limit, so it appears (clamped to 170) even when you set nothing.

Request log and request ids

The server can write one info line per request under the target cortex_api::request, which the image's default RUST_LOG (cortex_api=info) enables. CORTEX_REQUEST_LOG picks which requests:

  • slow (the default): every response outside 2xx — unauthenticated 401 probes included — and every request that took at least CORTEX_REQUEST_LOG_SLOW_MS (default 1000 ms). A 0.6 s recall is not logged; a 1.4 s one is. A health checker or proxy that calls an authenticated route without a key writes one line per call.
  • all: every request. off: none.
INFO cortex_api::request: request request_id=req_01a0e9fc-5496-7622-add0-b3bb97cf7fc0 client_request_id="docs-verify-408-single" method=POST route=/v1/experience status=408 total_ms=30011 admission_ms=6 lease_wait_ms=0 wal_append_ms=3 indexer_wait_ms=30000 queue_depth=1 outcome="timeout"
  • request_id is the id the response carries in X-Request-Id: your own when you send one, else a server-minted req_…. When an error response mints its own id, yours appears as client_request_id.
  • route is the route template (/v1/events/{id}), never the path; the line holds no body or query string.
  • Stage timings appear for the stages the request went through: admission_ms (the scope check and any new-scope registration), lease_wait_ms, wal_append_ms, indexer_wait_ms, queue_depth (the unindexed writes ahead of this one, itself included) and stage_wait_ms (the wait for a later stage after indexing) on a capture; lease_wait_ms, blocking_queue_ms and engine_ms on forgets and erasures.
  • outcome is ok, timeout (a ?wait= ran out) or client_gone (the client left first; status is then 0).

The same id is on the audit rows the request writes (the authentication layer's row, and the gdpr: true row of a forget), so a forget's response, log line and audit trail can be joined on it.

Next steps

On this page