Configuration guide
This page is the annotated companion to the auto-generated
configuration reference, which lists every
variable with its type and default. Here: what each deployment profile
actually requires, ready-to-copy .env examples, and the variables worth
special care.
Profile requirements matrix
Section titled “Profile requirements matrix”The base schema keeps most variables optional; the profile transform enforces them at boot (fail-fast, before any traffic). See Deployment profiles for the design.
| Variable | lite |
standard |
|---|---|---|
DEPLOYMENT_PROFILE |
lite |
standard (default) |
DATABASE_URL |
required | required |
JWT_SECRET |
— | required (≥32 chars) when AUTH_REQUIRED=true |
AUTH_REQUIRED |
n/a (single-user) | effectively required over streamable-http (boot fail-safe) |
MCP_ADMIN_TOKEN |
recommended (admin tools) | required for any admin tool use |
Boot-time enforcement to expect:
- Missing profile-required URLs fail validation with a clear message.
AUTH_REQUIRED=truewithout a ≥32-charJWT_SECRETrefuses to boot.- Multi-tenant (
standard) +MCP_TRANSPORT=streamable-http+AUTH_REQUIRED=falserefuses to boot in everyNODE_ENVunlessALLOW_UNAUTHENTICATED_HTTP=trueis set — see Enable authentication. MEMORY_CONSOLIDATION_MERGE_THRESHOLDmust stay strictly belowMEMORY_DUPLICATE_THRESHOLD, andMEMORY_CONTRADICTION_THRESHOLDbelowMEMORY_CONTRADICTION_THRESHOLD_MAX.
Example .env per profile
Section titled “Example .env per profile”lite — single-user, Postgres-backed
Section titled “lite — single-user, Postgres-backed”DEPLOYMENT_PROFILE=liteDATABASE_URL=postgresql://engram:engram@localhost:5432/engramMCP_TRANSPORT=stdio
EMBEDDING_PROVIDER=ollama # local, no API key; or openai + OPENAI_API_KEYMCP_ADMIN_TOKEN=<openssl rand -hex 24>standard — multi-tenant, authenticated HTTP
Section titled “standard — multi-tenant, authenticated HTTP”DEPLOYMENT_PROFILE=standardNODE_ENV=productionPORT=3000MCP_TRANSPORT=streamable-http
# Store — Postgres with pgvector is the only backing serviceDATABASE_URL=postgresql://engram:engram@postgres:5432/engram
# EmbeddingsEMBEDDING_PROVIDER=openaiOPENAI_API_KEY=sk-...
# Auth (mandatory posture for multi-tenant HTTP)AUTH_REQUIRED=trueJWT_SECRET=<openssl rand -base64 48>MCP_ADMIN_TOKEN=<openssl rand -hex 24>
# Rate limitingRATE_LIMIT_ENABLED=trueSecurity-critical variables
Section titled “Security-critical variables”These two secrets have different blast radii — treat them accordingly:
JWT_SECRET— HMAC key for session JWTs. A holder can forge a token for any tenant. ≥32 chars enforced; rotate by restarting with a new value (all sessions invalidate). Never logged, never in client configs.MCP_ADMIN_TOKEN— gates every admin tool (reindex_*,consolidate_corpus,import_agent_memory,create_api_key, …) via constant-time comparison. A leak allows minting keys for any tenant — it is the most sensitive secret in the system. Operator-only: it belongs in the server env and the operator’s shell, never in an agent config. Tool inputs require ≥16 chars.
Related, not secrets but security-posture: ALLOW_UNAUTHENTICATED_HTTP
(explicit opt-out of the boot fail-safe), METRICS_TOKEN (bearer for
/health/metrics), CORS_ALLOWED_ORIGINS.
Tuning notes
Section titled “Tuning notes”pgvector HNSW tuning
Section titled “pgvector HNSW tuning”pgvector is the only vector backend; unset values use pgvector’s defaults.
| Variable | Applies at | Effect |
|---|---|---|
PGVECTOR_HNSW_M (2–100) |
index build | Graph connectivity — higher = better recall, bigger index, slower build |
PGVECTOR_HNSW_EF_CONSTRUCTION (4–1000) |
index build | Build-time candidate list — higher = better index quality, slower build |
PGVECTOR_HNSW_EF_SEARCH (1–1000) |
per query | Recall/latency dial — raise it when recall quality matters more than p95 |
Build-time values take effect on index (re)creation. Validate changes with
pnpm bench:backends and the p95 budgets in the
capacity reference.
STM consolidation
Section titled “STM consolidation”| Variable | Default | Meaning |
|---|---|---|
STM_CONSOLIDATION_ACCESS_THRESHOLD |
3 |
Reads an STM memory needs before qualifying for LTM promotion |
STM_CONSOLIDATION_IMPORTANCE_THRESHOLD |
0.5 |
Minimum importance score to promote (not schema-validated; read directly from the environment) |
STM_CONSOLIDATION_INTERVAL_MS |
300000 |
Promotion scan interval; 0 disables the scheduler |
Lower thresholds promote more aggressively (bigger LTM corpus, more embedding cost); raise them if LTM accumulates trivia. Background: Consolidation & decay.
Memory lifecycle (decay, dedup, consolidation, contradictions)
Section titled “Memory lifecycle (decay, dedup, consolidation, contradictions)”Decay — how memories fade:
| Variable | Default | Meaning |
|---|---|---|
MEMORY_DECAY_INTERVAL_MS |
86400000 (24 h) |
Decay pass interval; 0 disables |
MEMORY_DECAY_BATCH_SIZE |
100 |
Rows per cursor-resumable batch |
MEMORY_DECAY_STALE_SCORE_THRESHOLD |
0.3 |
Score at/below which a memory is marked stale |
MEMORY_DECAY_PRUNE_SCORE_THRESHOLD |
0.15 |
Score below which an old, unpinned memory is deleted (audited) |
MEMORY_DECAY_PRUNE_OLDER_THAN_DAYS |
30 |
Minimum age before prune eligibility |
MEMORY_IMPORTANCE_HALF_LIFE_DAYS |
14 |
Recency half-life in importance scoring |
Similarity bands — these four variables partition cosine-similarity space for a new write against an existing memory (defaults shown):
0.80 0.85 0.97 1.00 |── contradiction band ─|── corpus-merge band ──|── duplicate zone ──| CONTRADICTION_THRESHOLD CONSOLIDATION_MERGE DUPLICATE_THRESHOLD| Variable | Default | Meaning |
|---|---|---|
MEMORY_DUPLICATE_THRESHOLD |
0.97 |
At/above: write-time collapse into the existing row |
MEMORY_CONSOLIDATION_MERGE_THRESHOLD |
0.85 |
Lower bound of the consolidate_corpus merge band; must stay below the duplicate threshold (boot-enforced) |
MEMORY_CONSOLIDATION_INTERVAL_MS |
0 (disabled) |
Corpus-consolidation scheduler. Off by default because a scheduled pass merges without review — opt in only after inspecting consolidate_corpus dry runs |
MEMORY_CONTRADICTION_THRESHOLD |
0.8 |
Lower bound of the contradiction band |
MEMORY_CONTRADICTION_THRESHOLD_MAX |
0.97 |
Upper bound (exclusive) of the contradiction band |
MEMORY_CONTRADICTION_POLICY |
flag |
flag: keep both rows visible, mark contradicted for review. supersede: hide the older row (latest-wins) |
Keep flag unless you have a reconciliation habit — supersede silently
hides data on a heuristic match.
Rate limits
Section titled “Rate limits”standard-profile-only, Postgres-backed fixed window; all buckets are
evaluated and the tightest one wins (429 + Retry-After).
| Variable | Default | Bucket |
|---|---|---|
RATE_LIMIT_ENABLED |
false |
Master switch |
RATE_LIMIT_WINDOW_SEC |
60 |
Window length (defaults make the *_RPM values per-minute) |
RATE_LIMIT_USER_RPM |
120 |
Per authenticated user/key |
RATE_LIMIT_ORG_RPM |
6000 |
Aggregated per organization |
RATE_LIMIT_IP_RPM |
60 |
Per unauthenticated client IP |
RATE_LIMIT_TOOL_OVERRIDES |
— | JSON per-tool overrides, e.g. {"reindex_memories":{"limit":2,"windowSeconds":3600}} (validated at boot) |
Size USER_RPM from agent behavior: a coding agent with recall-first habits
makes a handful of calls per task; the default 120/min accommodates bursts
(ingest_conversation is charged proportionally to payload size). Cap
expensive admin tools with per-tool overrides rather than lowering the
global limit.