Skip to main content

Configuration

The InfoConnect Hybrid Search Engine uses environment variables to control how it works. You can create a .env file in the project root. The system reads this file when it starts.

Getting Started with Configuration​

Most users only need to change the API key mapping and the three Redis credentials to get started. The rest can use their default values.

Here is the minimum you need for your first setup:

# API key to client mapping (required for security)
API_KEY_CLIENTS={"my-secret-key-123":"my-client"}

# Distinct URL-safe Redis ACL credentials (required by Docker Compose)
REDIS_APP_PASSWORD=replace-with-app-password
REDIS_CELERY_PASSWORD=replace-with-celery-password
REDIS_PUBLICATION_PASSWORD=replace-with-publication-password

# How fast users can call the API (optional)
RATE_LIMIT=30

# How much detail to show in logs (optional)
LOG_LEVEL=INFO

# Where your services run
REDIS_URL=redis://app:replace-with-app-password@localhost:6379/0
QDRANT_URL=http://localhost:6333
Quick Start

Start from cp .env.example .env, then replace the API key and all three Redis password placeholders with strong, distinct values. Keep the password text synchronized with the corresponding Redis URLs in that file, then run just dev.

The sections below explain each setting in detail. You can skip to the Example .env File for a complete reference.

Settings Most People Change​

These are the settings you will likely want to adjust first.

API Keys and Access Control​

VariableDefaultDescription
API_KEY_CLIENTS{}JSON object mapping valid API keys to client ids for authentication
SEARCH_TRACE_ENABLEDtrueGlobal switch for the durable search debug trace. On, every /search writes one bounded search_trace record to the sink; off, nothing is built or written. See Search debug trace.
RATE_LIMIT30Requests per minute per resolved client id for protected routes. See Rate Limiting guide for details.
AUTH_FAILURE_RATE_LIMIT10Failed authentication attempts per minute per source address before a request can resolve to a client
DEBUGfalseEnable debug mode (exposes /docs and /redoc)
CORS_ORIGINS["http://localhost:3000"]Allowed CORS origins

The API_KEY_CLIENTS variable is required. Every request to the API must include one configured key in the X-API-Key header. This setting is parsed as a JSON object where each key maps to the client id that owns the request data. An empty object rejects every API key.

Set DEBUG=true only in development or trusted environments. It exposes /docs for interactive Swagger UI requests and /redoc for a cleaner read-only API reference. Keep DEBUG=false in production unless you intentionally want those documentation endpoints available.

Why Multiple Keys?

You might give different keys to different teams or applications. This lets you rotate keys later without breaking everything at once.

SEARCH_TRACE_ENABLED defaults to true. While it is on, every /search request, for every API key, writes one bounded search_trace record — the scope and requested limit the search ran with, the raw query, the corrected query, the top scored results with document and page, and per-stage timings, tagged with the request id and the resolved client id (never the API key) — to the durable sink. The record never reaches the ordinary application log, which keeps today's redacted line. Set it to false to roll back: no record is built or written anywhere. See Search debug trace for the record shape, its bounds and the 30-day retention sizing.

Client isolation​

Each API key maps to a client_id that owns the data it ingests. Every request — embed, search, update, delete, list — is automatically scoped to that client. Documents from different clients never mix, even when two clients use the same document_id. You do not pass client_id in your requests; the server resolves it from your API key.

If API_KEY_CLIENTS is empty or missing, the server rejects every request with 401 and logs a loud startup warning. This is intentional: the system fails closed rather than serving unscoped data.

For details on how client scoping affects search filters — including why client_id cannot be used as a filter key — see the Search Filters guide.

Example API Key Clients​

# Development - simple keys are fine
API_KEY_CLIENTS={"test-key-123":"demo-client","another-key":"demo-client","super-secret-key":"demo-client"}

# Production - use strong random keys and stable client ids
API_KEY_CLIENTS={"prod-key-REPLACE_WITH_RANDOM_HEX":"prod-client"}

# Production with the QA-only key (keep it last so the Console and the backfill
# defaults still resolve a customer key)
API_KEY_CLIENTS={"prod-key-REPLACE_WITH_RANDOM_HEX":"prod-client","<generated-qa-key>":"qa-client"}

The optional qa-client entry is a QA-only client used for release live checks and isolation runs; it is owned by the captain and must never serve real users. Its generate, rotate, and revoke procedure is in the QA-Only API Key section of the Security Hardening guide.

Logging Level​

VariableDefaultDescription
LOG_LEVELINFOLogging level (DEBUG, INFO, WARNING, ERROR)

Set this to DEBUG when troubleshooting. Use INFO for normal operation. Use WARNING or ERROR to reduce log noise in production.

The transport/client loggers urllib3, fastembed, httpcore, and httpx remain at WARNING or above even when the application root logger is configured for DEBUG, keeping third-party request chatter out of normal output.

Optional tracing and retained logs​

VariableDefaultDescription
TRACING_ENABLEDfalseEnable API, search, transport and Celery spans. Requires the optional tracing dependency extra.
TRACING_EXTRAemptyCompose/Docker build argument: set to 1 to include that extra in API and worker images. Empty keeps default images unchanged.
OTEL_SERVICE_NAMEinfoconnectTrace service name; resources also identify the api or worker component.
OTEL_EXPORTER_OTLP_TRACES_ENDPOINThttp://localhost:4318/v1/tracesFull OTLP HTTP/protobuf trace endpoint, reachable from every enabled API/worker process.
TRACING_SAMPLE_RATIO1.0Root trace sample ratio, from 0 to 1; descendants honor their parent's decision.
OTEL_EXPORTER_OTLP_TRACES_HEADERSemptyOptional collector authentication headers, read by the OTLP exporter. Never log their values.
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATEemptyOptional CA certificate path, read by the exporter. Mount the certificate into enabled containers.
DOCKER_LOG_MAX_SIZE100mProduction json-file rotation size per file.
DOCKER_LOG_MAX_FILE20Production retained file count per container, including Console and infrastructure.
SEARCH_TRACE_LOG_PATH/var/log/infoconnect (Compose)Container directory of the durable search trace sink. Traces append to search-trace.jsonl there, rotated daily and pruned after SEARCH_TRACE_RETENTION_DAYS. Empty (the default outside Compose) leaves the sink unconfigured, so no trace is written.
SEARCH_TRACE_RETENTION_DAYS30Daily trace files kept in the durable sink, so the trace horizon is 30 days.
SEARCH_TRACE_LOG_HOST_DIR./.runtime/search-traceCompose host directory mounted at SEARCH_TRACE_LOG_PATH on the API service, so the sink survives container recreation and redeploys.

See request lookup, retention and tracing operations for startup behavior, local setup and collector requirements.

Search and Document Settings​

These settings control how documents are split and how search results are returned.

Document Chunking​

VariableDefaultDescription
CHUNK_SIZE512Approximate tokens per chunk
CHUNK_OVERLAP50Token overlap between chunks

When you upload a document, the InfoConnect Hybrid Search Engine splits it into smaller pieces called chunks. This helps the search work better. Larger chunks mean more context but slower processing. Smaller chunks mean more precise matches but less context.

Choosing Chunk Size

Think of chunks like pages in a book. If your documents are short emails, use small chunks (256). If they are long reports, use larger chunks (1024). The default 512 works well for most documents.

Chunk Size Guidelines​

Use CaseChunk SizeOverlap
Short documents (emails)25625
Standard documents51250
Long documents (reports)1024100

Search Results​

VariableDefaultDescription
RETRIEVAL_TOP_K10Default search results count
HYBRID_ALPHA0.5Default dense weight for hybrid search; supported range is 0.0 to 1.0
SEARCH_DENSE_SCORE_FLOOR0.70Minimum raw cosine score for a dense candidate
SEARCH_SPARSE_SCORE_FLOOR1.0Minimum raw BM25 dot-product score for a sparse candidate
SEARCH_TOTAL_COUNT_CAP1000Maximum score-gated count scan before reporting a lower bound; /search totals are independently bounded to the 200-result retrieval window
RERANK_TOP_N5Number of results to rerank
RERANK_TIMEOUT_SEC2.0Caller deadline for waiting for the inference slot and reranker scores; on timeout, retain retrieval order
MAX_NODES_PER_TASK50Max nodes per search task

Reranker text extraction and score generation run in a blocking worker. The deadline does not cancel native inference already running: the shared SearchService keeps its inference slot occupied until that work finishes, so later requests cannot accumulate native jobs.

RETRIEVAL_TOP_K controls how many results the search returns by default. HYBRID_ALPHA sets the deployment-wide dense/sparse balance; an explicit /search alpha value overrides it for that request. Start with HYBRID_ALPHA=0.5. Lower values favor sparse/lexical matches; higher values favor dense/semantic matches.

The score floors gate raw dense and sparse candidates before hybrid fusion. The recommended defaults are SEARCH_DENSE_SCORE_FLOOR=0.70 and SEARCH_SPARSE_SCORE_FLOOR=1.0; a candidate is retained when either active stream clears its floor. Raising a floor can remove low-confidence noise, but setting it too high over-filters genuine matches and can produce no results. Keep gibberish and other queries with no confident match in the genuine no-results state. Recalibrate the floors when the embedding model or corpus distribution changes. See the Embeddings and Retrieval guide for scope selection and the caller-facing Evidence Result contract.

Content is the default search scope; callers should send it explicitly. Every scope requires active publication authority. Metadata and Both additionally require per-Client Discovery Readiness. Metadata semantic eligibility uses the immutable 0.75 dense floor for BAAI/bge-small-en-v1.5; callers cannot override that floor.

Web Search (Chat)​

Optional public web search is controlled by a deployment capability gate and the Chat turn's Answer Mode. With the gate on, Auto permits web only for live, time-varying questions, Docs-only omits the web tool, and Web requires a web-search pass. With the gate off, Auto runs as Docs-only and an explicit Web turn is rejected; a browser preference cannot override deployment policy.

VariableDefaultDescription
WEB_SEARCH_ENABLEDfalseDeployment capability gate. When false, Auto runs Docs-only and explicit Web turns are rejected
WEB_SEARCH_PROVIDERddgsWeb search provider; only ddgs is supported
WEB_SEARCH_DDGS_BACKENDbingDDGS backend; allowed values are bing, google, auto, and duckduckgo
WEB_SEARCH_DDGS_TIMEOUT_SEC5.0Provider request timeout
WEB_SEARCH_TIMEOUT_SEC5.5Hard wall-clock deadline for the whole web branch

Web retrieval is best-effort: a provider error, timeout, or disconnect drops the web results and never breaks the document-backed answer. Only the exact submitted query is sent to the provider, and result URLs are validated against SSRF host rules (including IPv6 reserved ranges) before they reach the model.

bing is the default because keyless DuckDuckGo requests can be bot-blocked. Allowing a small backend set makes availability less predictable than a single fixed egress target; operators that require one predictable destination should set WEB_SEARCH_DDGS_BACKEND explicitly.

Enabling WEB_SEARCH_ENABLED is not the same as selecting Chat Web Answer Mode. The gate is a server capability; Answer Mode is a per-turn Console choice that cannot override it. After you change the variable, recreate API containers (just prod-up) — docker compose restart will not pick it up. See Web search enablement vs Chat Answer Mode.

OCR and Classification Settings​

These settings control how the InfoConnect Hybrid Search Engine reads images and categorizes documents.

Document Classification​

VariableDefaultDescription
CLASSIFICATION_ENABLEDtrueEnable automatic document classification
CLASSIFIER_BACKENDmodernbert-onnxClassification backend selector

When you upload a document, the InfoConnect Hybrid Search Engine can automatically figure out what category it belongs to. This helps organize your documents.

Model Roles

RAPIDOCR_MODEL_NAME is used for OCR during parsing. ModernBERT ONNX is the only supported classification backend.

These model values reflect the repository's current .env.example / Docker mapping.

Upgrading with customized model IDs

The model selectors and artifact path below are a commercial-use allowlist, not deployment tuning choices. Before upgrading an existing deployment, compare its .env with these seven approved values and restore any customized entries:

SPACY_MODEL_NAME=en_core_web_sm
DENSE_MODEL_NAME=BAAI/bge-small-en-v1.5
SPARSE_MODEL_NAME=Qdrant/bm25
RERANK_MODEL_NAME=Xenova/ms-marco-MiniLM-L-6-v2
RAPIDOCR_MODEL_NAME=rapidocr-onnxruntime
MODERNBERT_ONNX_MODEL_REPO=onnx-community/ModernBERT-base-nli-ONNX
MODERNBERT_ONNX_MODEL_FILE=onnx/model_quantized.onnx

An unapproved value makes settings validation fail during API and worker import, so the affected service does not start. This fail-closed behavior is intentional; adding a model requires code and compliance-policy review.

OCR for Images and PDFs​

VariableDefaultDescription
RAPIDOCR_OCR_ENABLEDtrueMaster switch to enable/disable OCR
RAPIDOCR_MODEL_NAMErapidocr-onnxruntimeOCR model for images and low-text PDF pages
OCR_PAGE_TEXT_THRESHOLD80Min characters before OCR fallback

If you upload an image or a scanned PDF, the InfoConnect Hybrid Search Engine uses RapidOCR to read the text. The threshold setting controls when it decides a PDF page needs OCR help.

Custom Categories​

VariableDefaultDescription
ALLOWED_CATEGORIES["financial reporting","risk management","corporate governance","regulatory compliance","business operations","market analysis","legal","human capital","technology","sustainability","KYC","Credit","Collateral","Ledger","other"]JSON array of category labels scored by ModernBERT
CATEGORY_DESCRIPTIONSKYC/Credit/Collateral/Ledger descriptionsOptional JSON object mapping category labels to richer scoring text. Missing descriptions fall back to the category label.
Default Categories

Current defaults come from app/config.py and use the ModernBERT financial / corporate taxonomy:

["financial reporting","risk management","corporate governance","regulatory compliance","business operations","market analysis","legal","human capital","technology","sustainability","KYC","Credit","Collateral","Ledger","other"]

These settings are also parsed as JSON arrays. If you customize them, keep the JSON syntax:

ALLOWED_CATEGORIES=["financial reporting","technology","other"]

You can customize these to match your business, but the classifier still expects JSON arrays rather than plain delimited strings.

CATEGORY_DESCRIPTIONS is optional. You do not need to provide a description for every item in ALLOWED_CATEGORIES. If a category has no matching description, the InfoConnect Hybrid Search Engine falls back to the label text itself. For example, Ledger without a description is scored as Ledger.

Descriptions are strongly recommended for short, ambiguous, acronym-based, or client-specific labels. Labels such as KYC, Credit, Collateral, and Ledger often need richer text because real documents may say Kad Pengenalan, bankruptcy search, pledged asset, or journal entry instead of the exact label.

ALLOWED_CATEGORIES=["KYC","Credit","Collateral","Ledger"]
CATEGORY_DESCRIPTIONS={"KYC":"know your customer, identity verification, customer due diligence, IC, passport, Kad Pengenalan, Warganegara, onboarding, or beneficial ownership documents","Credit":"credit reports, bankruptcy searches, insolvency checks, lending, repayment history, or credit risk documents"}

You can describe only the categories that need extra context. The API still returns the configured category label, such as KYC; descriptions are used only for scoring and strong lexical evidence matching.

ModernBERT ONNX Settings​

VariableDefaultDescription
MODERNBERT_ONNX_MODEL_REPOonnx-community/ModernBERT-base-nli-ONNXHugging Face repo for the ONNX classifier
MODERNBERT_ONNX_MODEL_FILEonnx/model_quantized.onnxONNX model file loaded from the repo
MODERNBERT_ONNX_CATEGORY_TEMPLATEThe topic of this document is {label}.Hypothesis template for category scoring
MODERNBERT_ONNX_CATEGORY_THRESHOLD0.45Minimum score required to keep a category
MODERNBERT_ONNX_LONG_DOC_WINDOW1800Sliding window size (tokens) for documents exceeding model context
MODERNBERT_ONNX_LONG_DOC_OVERLAP256Token overlap between consecutive windows
MODERNBERT_ONNX_TOP_K_CHUNKS2Max additional chunks to score after the first (long-doc optimization)
MODERNBERT_ONNX_NLI_MAX_TOKENS2048Maximum token length for NLI input sequences (premise + hypothesis). Lower values reduce inference time at the cost of truncating long document text
CLASSIFICATION_SELECT_TOP_K3Number of representative chunks selected via centroid + MMR. Classification scores these chunks instead of the full document text.
MODERNBERT_ONNX_INTRA_OP_THREADS1ONNX Runtime intra-op thread count
MODERNBERT_ONNX_INTER_OP_THREADS1ONNX Runtime inter-op thread count
MODERNBERT_PREFILTER_ENABLEDfalseEnable lexical prefilter to reduce hypothesis space before ONNX inference
MODERNBERT_PREFILTER_TOP_K3Number of top categories to keep after lexical prefilter

Model Repo Compatibility References (MODERNBERT_ONNX_MODEL_REPO)​

The classifier requires a 3-class NLI model outputting entailment (index 0), neutral (index 1), and contradiction (index 2). Only the approved repo below is runnable; the other row is an upstream compatibility reference, not a selectable deployment value.

RepoSizeNLI ClassesStatus
onnx-community/ModernBERT-base-nli-ONNXBase (768d, 22 layers)3 (entailment / neutral / contradiction)Approved runnable value
onnx-community/ModernBERT-large-zeroshot-v2.0-ONNXLarge (1024d, 28 layers)2 (entailment / not_entailment)Rejected by startup allowlist; also incompatible
Non-runnable reference

Do not set MODERNBERT_ONNX_MODEL_REPO to ModernBERT-large-zeroshot-v2.0-ONNX. Settings validation rejects it before service startup. Its 2-class output would also be incompatible with the pipeline's required three NLI logits.

Upstream Model File References (MODERNBERT_ONNX_MODEL_FILE)​

These files exist in the upstream repository, but only onnx/model_quantized.onnx is a runnable value in the current commercial-use allowlist. Every other row below is a non-runnable reference: selecting it through an environment override fails settings validation during service import. Adding another artifact requires a code and policy change with license evidence and review.

FilePrecisionSizeNotes
onnx/model.onnxfp32~599 MBRejected by startup allowlist; reference only
onnx/model_fp16.onnxfp16~300 MBRejected by startup allowlist; reference only
onnx/model_quantized.onnxint8~151 MBApproved runnable value
onnx/model_int8.onnxint8~151 MBRejected by startup allowlist; reference only
onnx/model_uint8.onnxuint8~151 MBRejected by startup allowlist; reference only
onnx/model_q4.onnx4-bit~225 MBRejected by startup allowlist; reference only
onnx/model_q4f16.onnx4-bit/fp16~140 MBRejected by startup allowlist; reference only
onnx/model_bnb4.onnxBNB 4-bit~218 MBRejected by startup allowlist; reference only
Approved model file

Use onnx/model_quantized.onnx for current deployments. Other upstream artifacts are reference options only until explicitly reviewed and added to the commercial-use allowlist.

Classification Order

The historical publication-disabled path classified asynchronously after embedding and read chunk embeddings from Qdrant; it is no longer bootable because search requires publication. With publication enabled, representative-chunk selection and scoring complete before the immutable generation's embedding chord is dispatched. See Architecture: Post-Embedding Classification for the trigger, retry, and replay contract.

note

Thread counts default to 1 across all environments. Increase MODERNBERT_ONNX_INTRA_OP_THREADS for CPU-bound preprocessing workers if your deployment has spare cores.

Named Entity Recognition​

VariableDefaultDescription
SPACY_NER_ENABLEDtrueEnable spaCy NER extraction
NER_ENTITY_KEYSSee belowEntity types to extract and store

The InfoConnect Hybrid Search Engine can find and extract important information from your documents. This includes names, dates, amounts of money, and more.

Default Entity Keys​

The following entity types are extracted and stored:

  • persons — People names
  • organizations — Companies, agencies
  • dates — Date and time references
  • locations — Places, countries, cities
  • monetary_amounts — Currency values (regex-enhanced)
  • account_numbers — Account identifiers (regex)
  • transaction_refs — Transaction references (regex)
  • account_types — Account types (regex)

Worker and Performance Settings​

These settings control background processing and resource usage.

Celery Worker Settings​

VariableDefaultDescription
CELERY_WORKER_PREFETCH_MULTIPLIER1Tasks prefetched per worker
CELERY_WORKER_REVISIONunknownDeployment marker used by the startup worker-isolation guard
CELERY_TASK_ACKS_LATEtrueAcknowledge after task completion
CELERY_WORKER_DEDUPLICATE_SUCCESSFUL_TASKStrueSuppress redelivery of successful late-ack tasks using the persistent result backend
CELERY_BROKER_VISIBILITY_TIMEOUT_SEC3600Redis broker visibility timeout; publication reconciliation retries are kept below this value
CELERY_WORKER_MAX_MEMORY_PER_CHILD1572864 (1.5GB)Memory limit per worker (KB)
CELERY_WORKER_MAX_TASKS_PER_CHILD100Tasks before worker restart
CELERY_WORKER_MAX_TASKS_PER_CHILD_EMBEDDING250Embedding-worker task recycle target from config.py
CELERY_WORKER_MAX_TASKS_PER_CHILD_PREPROCESSING500Preprocessing-worker task recycle target from config.py
CELERY_TASK_MAX_RETRIES3Max retry attempts for failed tasks
CELERY_RETRY_BACKOFF_BASE60Base seconds for exponential backoff
CELERY_RESULT_EXPIRES86400 (24h)Result retention time (seconds)
CELERY_EMBED_SOFT_TIME_LIMIT_SEC900Soft time limit for embedding tasks
CELERY_EMBED_TIME_LIMIT_SEC1200Hard time limit for embedding tasks
CELERY_TASK_REJECT_ON_WORKER_LOSTtrueRequeue task if a worker exits unexpectedly
CELERY_QUEUE_SPIKE_THRESHOLD500Queue depth that triggers a sampled celery_queue_depth_spike audit event
MEMORY_PRESSURE_WARN_PERCENT85cgroup memory usage percentage that starts a sampled pressure warning
MEMORY_PRESSURE_RECOVER_PERCENT75cgroup memory usage percentage below which pressure clears
OPS_MONITOR_INTERVAL_SECONDS60Sampling interval for queue-depth and memory-pressure monitors
What Are Workers?

Workers are background processes that handle document processing. When you upload a file, the API accepts it immediately. Then workers do the heavy lifting: parsing, chunking, embedding, and storing. This happens in the background so the API stays fast.

Workers automatically restart after processing a certain number of tasks or using too much memory. This prevents memory leaks from slowing things down.

just dev and just prod-up set CELERY_WORKER_REVISION to the current 12-character Git revision and include that value in every worker node name. If you start the API or workers manually, use the same valid revision marker for all processes and name workers as preprocessing-<revision>@<host> or embedding-<number>-<revision>@<host>.

A Celery worker revision conflict detected before startup. line during just prod-up means a worker from another revision is still on the shared broker (stderr also includes Expected deployment revision: and Offending worker node(s):). Recover with just prod-down (data preserved), not just prod-down clean or just wipe. See Celery worker revision conflict recovery.

Code defaults vs Docker Compose defaults

app/config.py keeps the general CELERY_WORKER_MAX_TASKS_PER_CHILD default at 100, but docker-compose.yml overrides the live worker values to 250 for embedding workers and 500 for the preprocessing worker.

app/config.py defaults CELERY_WORKER_MAX_MEMORY_PER_CHILD to 1572864 KB (about 1.5 GB). The preprocessing worker in docker-compose.yml can override this via PROD_PREPROCESSING_MAX_MEMORY_PER_CHILD_KB (default 4194304 KB / 4 GB) to match its larger container memory limit.

Docker / Runtime-only Overrides​

VariableDefaultDescription
PROD_PREPROCESSING_MEMORY6GDocker Compose memory limit for worker-preprocessing-1

This variable is only used by docker-compose.yml at runtime. It does not exist in app/config.py, but it controls the container memory limit for the preprocessing worker in production-style Docker runs.

Redis Configuration​

VariableDefaultDescription
REDIS_URLredis://localhost:6379/0Application-state connection using the app ACL user in deployed Compose
CELERY_BROKER_URLredis://localhost:6379/1Celery task queue using the celery ACL user
CELERY_RESULT_BACKENDredis://localhost:6379/2Celery results store using the celery ACL user
PUBLICATION_ENABLEDtrueSearch serves exclusively through the fenced four-lane protocol with Qdrant Manifest read authority; the API fails fast at startup when this is disabled or the publication Redis contract is unusable
PUBLICATION_REDIS_URLredis://publication:<password>@localhost:6379/3Dedicated DB 3 connection using the prefix-limited publication ACL user; the default carries a placeholder credential that must be replaced
PUBLICATION_WORKER_SLOTapi-1Stable deployment-provided search-process slot; enabled startup rejects missing or duplicate slots. Production Compose pins the API service to api-1.
PUBLICATION_WAITAOF_TIMEOUT_MS5000Bounded local AOF fsync barrier timeout
PUBLICATION_LEASE_SECONDS15Redis-server-time worker lease
PUBLICATION_RENEW_SECONDS5Worker lease renewal interval
PUBLICATION_SAFE_DEADLINE_SECONDS10Monotonic local deadline after which a disconnected worker stops serving
PUBLICATION_LIFECYCLE_FENCE_WAIT_SECONDS5Wait bound in seconds for each Soft Delete/Restore snapshot read blocked by a sibling fence or snapshot movement; accepts 0–30. See the Documents API lifecycle contract
PUBLICATION_FINALIZE_RETRY_BUDGET_SECONDS300No-progress budget for jittered finalize retries; each completed publication renews the deadline for a healthy serialized backlog
QA_PUBLICATION_ACTIVATION_DELAY_SECONDS0Test-only publication ACTIVATED hold (seconds) for isolated QA stacks: widens the public contract-503 window so the stranded-worker kill lands inside it. Keep 0 in production, dev, and normal QA runs
PUBLICATION_EXPECTED_REDIS_VERSION7.4.10Qualified Redis runtime version required for publication readiness
PUBLICATION_REDIS_MEMORY_MB512Numeric Redis container-memory budget; must be positive and must match the budget the Redis server derives its maxmemory from. just prod-up generates it from the host plan
REDIS_MEMORY_HEADROOM_PERCENT25Capacity reserved before deriving Redis maxmemory
REDIS_MAXMEMORYemptyRedis memory ceiling; just prod-up emits it from the shared host-wide runtime plan. When unset, development Compose derives it from PUBLICATION_REDIS_MEMORY_MB
PROD_API_WORKERSgenerated (1)Uvicorn worker count emitted by the production runtime planner; publication-enabled API containers must keep this at one
PROD_REDIS_MEMORYgeneratedRedis container limit emitted by the production runtime planner

The deployed Redis uses separate app, celery, and publication ACL credentials. Provide REDIS_APP_PASSWORD, REDIS_CELERY_PASSWORD, and REDIS_PUBLICATION_PASSWORD through the deployment secret mechanism. Do not reuse them. Use URL-safe password characters because Compose injects the same value into both the ACL file and connection URI. The publication user can read and write pub:v1:{publication}:*. It also has the narrow, read-only %R~docjobs:* access and SMEMBERS command needed by the one-time legacy publication bootstrap to inspect document-job history in DB 2; it cannot scan or mutate Celery keys. The default user is disabled for network authentication but retains command capability so Redis can replay the multipart AOF during startup; a derived, unpublished password prevents accidental use.

Changing REDIS_APP_PASSWORD, REDIS_CELERY_PASSWORD, or REDIS_PUBLICATION_PASSWORD requires recreating Redis and its dependent API and worker containers — the ACL file is generated at container start, and those services receive interpolated credentials only when they are recreated. Use just prod-down followed by just prod-up to rotate the stack. Force-recreating redis alone is insufficient because API and workers would retain their old passwords.

Redis Database Usage​

DBPurpose
0Application state and bounded caches
1Celery task broker
2Celery results backend
3Durable publication registry, journal, worker roster, and ready state

Logical databases isolate names, not memory, AOF, rewrite load, or process failure. Publication readiness also requires the digest-pinned Redis 7.4.10 build, healthy AOF, appendfsync everysec, no-appendfsync-on-rewrite no, aof-load-truncated no, and noeviction.

While PUBLICATION_ENABLED=true, metadata update and re-embed operations return retryable 503 errors instead of mutating Qdrant outside the generation protocol. Disabling the flag keeps those legacy mutation paths available but makes search unavailable because there is no active publication authority. Soft delete, hard delete, and restore run through the generation protocol; see Document endpoints for their behavior.

Publication mode also requires CELERY_RESULT_BACKEND to use persistent Redis storage, CELERY_TASK_ACKS_LATE=true, and CELERY_WORKER_DEDUPLICATE_SUCCESSFUL_TASKS=true. Startup fails closed when these delivery guarantees are unavailable. Keep the result retention longer than the publication recovery window so a successful parent task remains durable evidence against redelivery.

Qdrant Configuration​

VariableDefaultDescription
QDRANT_URLhttp://localhost:6333Qdrant server URL
QDRANT_TIMEOUT_SECONDS60Qdrant client request deadline in seconds (integer, 1–600); see Hard Delete timeout handling for verification and retry behavior
COLLECTION_NAMEdocumentsVector collection name
VECTOR_SIZE384Embedding dimensions (must match model)

Qdrant is the vector database that stores your document embeddings. This is what makes semantic search possible.

Embedding Models​

VariableDefaultDescription
DENSE_MODEL_NAMEBAAI/bge-small-en-v1.5Dense embedding model
SPARSE_MODEL_NAMEQdrant/bm25Sparse/BM25 model
RERANK_MODEL_NAMEXenova/ms-marco-MiniLM-L-6-v2FastEmbed cross-encoder reranker
EMBEDDING_BATCH_SIZE32Embeddings per batch
FASTEMBED_CACHE_DIR./models/fastembedModel cache location

Embedding models turn text into numbers that capture meaning. The InfoConnect Hybrid Search Engine uses two types: dense (for semantic meaning) and sparse (for keyword matching). Together they give better search results.

The listed model identifiers are the reviewed commercial-use defaults. Arbitrary environment overrides are rejected; changing a model requires updating the runtime and compliance allowlists.

Upstream Dense Model References​

ModelDimensionsSpeedQualityCommercial allowlist
BAAI/bge-small-en-v1.5384FastGoodApproved default
BAAI/bge-base-en-v1.5768MediumRejected by startup allowlist; reference only
BAAI/bge-large-en-v1.51024SlowRejected by startup allowlist; reference only
sentence-transformers/all-MiniLM-L6-v2384FastRejected by startup allowlist; reference only
About Model Size

Larger models may improve quality but use more memory and run slower. They are reference options, not runtime configuration choices, until their licenses and artifacts are reviewed and allowlisted.

File Upload Security​

VariableDefaultDescription
MAX_FILE_SIZE_MB200Maximum upload file size in MB
ALLOWED_EXTENSIONS.pdf,.docx,.txt,.md,.text,.pptx,.ppt,.xlsx,.xls,.png,.jpg,.jpeg,.tif,.tiffPermitted file extensions
ALLOWED_MIME_TYPESSee example belowPermitted upload MIME types mapped to extensions
UPLOAD_DIR./models/uploads (/tmp/ic-uploads in Docker examples)Local staging directory for files before Celery processing
OFFICE_LEGACY_CONVERSION_ENABLEDfalseEnable LibreOffice conversion of legacy .ppt/.xls to OOXML before parsing
LIBREOFFICE_BINARYsofficeLibreOffice executable used for legacy Office conversion
OFFICE_LEGACY_CONVERSION_TIMEOUT_SEC120Timeout for a single legacy Office conversion
OFFICE_MAX_UNCOMPRESSED_BYTES1000000000Max decompressed size of an OOXML (.docx/.pptx/.xlsx) upload (zip-bomb guard)
OFFICE_MAX_COMPRESSION_RATIO200Max uncompressed:compressed ratio for an OOXML upload (zip-bomb guard)

These settings protect your server from oversized uploads and unexpected file types. A file must pass the extension check, declared MIME mapping check when a known MIME type is provided, and sniffed-content check before it is enqueued.

UPLOAD_DIR is especially important in Docker deployments. The API writes the uploaded file there first, and the workers read it from the same path. Publication-enabled processing also stores its replayable classification dispatch outbox beside the staged upload. If you change UPLOAD_DIR, mount the same shared volume at that location in the API and every preprocessing and embedding worker container.

Production Runtime Threading​

VariableDefaultDescription
PROD_EMBEDDING_OMP_THREADS1Auto-tuned OpenMP/BLAS thread count for embedding worker containers
OMP_NUM_THREADSFrom PROD_EMBEDDING_OMP_THREADS in Docker ComposeOpenMP thread cap inside embedding workers
OPENBLAS_NUM_THREADSFrom PROD_EMBEDDING_OMP_THREADS in Docker ComposeOpenBLAS thread cap inside embedding workers
MKL_NUM_THREADSFrom PROD_EMBEDDING_OMP_THREADS in Docker ComposeMKL thread cap inside embedding workers

scripts/generate_prod_runtime.py writes PROD_EMBEDDING_OMP_THREADS to .runtime/prod.env; Docker Compose passes it through to the lower-level numerical libraries. Keep this at 1 unless benchmark evidence shows spare CPU headroom.

Model Cache Paths​

VariableDefaultDescription
FASTEMBED_CACHE_DIR./models/fastembedFastEmbed model cache
HF_HOME./models/huggingface (/models/huggingface in Docker Compose)Hugging Face cache
HF_TOKEN(unset)Optional Hugging Face token for gated or rate-limited model downloads
SPACY_MODEL_NAMEen_core_web_smspaCy NER model

These control where approved models are stored. You usually do not need to change them. Set HF_TOKEN only when downloads of the approved Hugging Face repository are rate-limited; switching repositories or spaCy models requires a reviewed code and compliance-policy change.

Example .env File​

Copy the authoritative, runnable template instead of maintaining another settings list here:

cp .env.example .env

The tables above explain the supported settings; .env.example owns the complete example and tracks deployment-required values such as Redis credentials and Celery visibility timeouts.

Configuration Best Practices​

DEBUG=false
LOG_LEVEL=INFO
RATE_LIMIT=30
API_KEY_CLIENTS={"REPLACE_WITH_A_STRONG_RANDOM_KEY":"prod-client"}
CELERY_WORKER_MAX_TASKS_PER_CHILD=100
CELERY_WORKER_MAX_MEMORY_PER_CHILD=1572864 # 1.5GB

Docker Compose​

Environment variables are automatically loaded from .env when using:

just prod-up

just prod-up also writes .runtime/prod.env with computed service memory ceilings, Redis headroom, and Celery worker scale targets so operators can inspect the chosen runtime plan.

Or set in docker-compose.yml:

services:
api:
environment:
- API_KEY_CLIENTS=${API_KEY_CLIENTS}
- LOG_LEVEL=INFO
Testing Your Config

For local just dev, apply .env changes by restarting the process loop:

just stop
just dev

For production Compose, docker compose restart does not re-read .env. Recreate with just prod-up and just prod-ui-up. See Why docker compose restart does not pick up .env.

What's Next​

Now that you understand the configuration options, you can:

If something is not working as expected, check the logs with LOG_LEVEL=DEBUG and review Troubleshooting and FAQ or the installation troubleshooting section.