Skip to content

Environment variable reference

Every novamem-server config knob lives in an environment variable. The schema is enforced at startup by go/internal/config — boot fails fast if anything required is missing or malformed, rather than starting a server that answers every request with a 503.

68 variables. This section is generated from go/internal/config/registry.go by go run ./cmd/gen-env-docs — the defaults below are the ones the loader applies, not a second copy of them. Add or change a variable there, not here.

Required settings

Each of these is required under the condition named, and startup fails fast without it — a server that cannot store, or cannot sign a session, refuses to boot rather than answering every request with a 503. Only NOVAMEM_WARM_URL is unconditional.

VariableRequired whenWhat it does
NOVAMEM_AUTH_TOKENNOVAMEM_AUTH_MODE=bearerThe shared bearer token for bearer mode.
NOVAMEM_COOKIE_SECRETNOVAMEM_AUTH_MODE is not noneSigns session cookies; at least 16 characters.
NOVAMEM_WARM_URLalwaysPostgres connection string for the warm tier, the auth tables and the audit log.
NOVAMEM_EMBEDDINGS_ENDPOINTNOVAMEM_EMBEDDINGS_PROVIDER=openai-compatibleBase URL of the embeddings API, for example https://api.openai.com/v1.
NOVAMEM_EMBEDDINGS_MODELNOVAMEM_EMBEDDINGS_PROVIDER=openai-compatibleModel id, for example text-embedding-3-small or nomic-embed-text.
NOVAMEM_RERANK_ENDPOINTNOVAMEM_RERANK_ENABLED is onFull URL of the rerank endpoint.
NOVAMEM_RERANK_MODELNOVAMEM_RERANK_ENABLED is onRerank model id.
NOVAMEM_EXTRACTION_ENDPOINTNOVAMEM_EXTRACTION_ENABLED is onOpenAI-compatible base URL for extraction.
NOVAMEM_EXTRACTION_MODELNOVAMEM_EXTRACTION_ENABLED is onExtraction model id.
NOVAMEM_QUERY_DECOMP_ENDPOINTNOVAMEM_QUERY_DECOMP_ENABLED is onOpenAI-compatible base URL for decomposition.
NOVAMEM_QUERY_DECOMP_MODELNOVAMEM_QUERY_DECOMP_ENABLED is onDecomposition model id.
NOVAMEM_OBSERVER_ENDPOINTNOVAMEM_OBSERVER_ENABLED is onOpenAI-compatible base URL for the observer.
NOVAMEM_OBSERVER_MODELNOVAMEM_OBSERVER_ENABLED is onObserver model id.

Server transport

VariableTypeDefaultDescription
NOVAMEM_HOSTstring0.0.0.0Bind address. Use 127.0.0.1 to confine the listener to localhost.
NOVAMEM_PORTport (1-65535)7778HTTP port. REST, /mcp, the dashboard and /api-docs are all served here.
NOVAMEM_BASE_URLstringhttp://$NOVAMEM_HOST:$NOVAMEM_PORTThe public origin. Seeds the trusted-origin list for the sign-in CSRF check and the resource identifier in the OAuth protected-resource metadata, so it must be the URL clients actually reach — set it when novamem sits behind a proxy.
NOVAMEM_CORS_ORIGINScomma-separated listhttp://localhost:5173Browser origins allowed to reach /mcp. Empty or self allows same-origin only; * reflects any origin and disables credentialed CORS, because reflect-any plus credentials would let any site read authenticated responses.
NOVAMEM_INSECURE_COOKIESboolean (1/true/yes/on)offDrops the Secure attribute from session cookies, for a load balancer without TLS or local development. Dev only: behind a TLS-terminating proxy it also lets the browser send the session cookie over plain HTTP to the same host.
NOVAMEM_SSE_KEEPALIVE_MSinteger > 025000Comment-frame cadence on the streamable GET /mcp stream. Must stay below the client's HTTP body-read timeout (undici defaults to five minutes) or the connection is torn down as idle.

Authentication

VariableTypeDefaultDescription
NOVAMEM_AUTH_MODEenumuserOne of none, bearer, user. user is the default: dashboard sessions plus per-user nm_… bearers for MCP. bearer is a single shared token. none disables authentication entirely and makes every request the public tenant — development only.
NOVAMEM_AUTH_TOKENstringThe shared bearer token for bearer mode. Useful for a single-process deployment that wants one static credential. Secret — keep it out of a ConfigMap and out of a committed .env. Required when NOVAMEM_AUTH_MODE=bearer.
NOVAMEM_COOKIE_SECRETstringSigns session cookies; at least 16 characters. Generate with openssl rand -hex 32. Must be stable across restarts — there is deliberately no ephemeral fallback, because one would let a forgotten variable silently invalidate every session on every restart. Secret — keep it out of a ConfigMap and out of a committed .env. Required when NOVAMEM_AUTH_MODE is not none.
NOVAMEM_BOOTSTRAP_ADMIN_EMAILstringEmail for the admin user seeded on first boot, when the deployment has no users yet. Ignored once any user exists.
NOVAMEM_BOOTSTRAP_ADMIN_PASSWORDstringPassword for the bootstrap admin, used only on first boot when the deployment has no users yet. Read once and then removed from the process environment, so a later env dump or a child process cannot see it. Docker Compose requires it to be set even when it will not be used. Secret — keep it out of a ConfigMap and out of a committed .env.
NOVAMEM_ADMIN_DASHBOARDboolean (0/false/no/off disable)onMaster switch for the admin surface. Set 0 to 404 /v1/admin/metrics and /v1/admin/metrics/prom.

Datastores

VariableTypeDefaultDescription
NOVAMEM_WARM_URLstringPostgres connection string for the warm tier, the auth tables and the audit log. Startup fails without it rather than serving 503s from a server that cannot store anything. Secret — keep it out of a ConfigMap and out of a committed .env. Always required.
NOVAMEM_PG_POOL_MAXinteger > 020Upper bound on the warm Postgres pool. Keep it below the server's max_connections divided by the replica count. Values above 2147483647 are refused rather than silently wrapped.
NOVAMEM_COLD_PROVIDERenumqdrantOne of pgvector, qdrant. Which vector tier backs cold storage. pgvector keeps everything in the warm Postgres; qdrant uses a separate Qdrant instance.
NOVAMEM_COLD_URLstring$NOVAMEM_WARM_URL for pgvector, http://localhost:6333 for qdrantEndpoint for the cold tier.
NOVAMEM_COLD_API_KEYstringSent as the api-key header to Qdrant. Unset sends no header. Secret — keep it out of a ConfigMap and out of a committed .env.
NOVAMEM_COLD_VECTOR_SIZEinteger >= 0384Dimension of the cold collection. Must match NOVAMEM_EMBEDDINGS_DIM.
NOVAMEM_COLD_TIMEOUT_MSinteger >= 015000Per-request timeout for the vector tier. Bounds a stalled backend so search degrades instead of hanging.

Embeddings

VariableTypeDefaultDescription
NOVAMEM_EMBEDDINGS_PROVIDERenumOne of openai-compatible. Unset leaves the embedder unconfigured: writes still store and search degrades to the keyword tier. openai-compatible calls an external endpoint. local-transformers is rejected at startup — the Go server points at an endpoint rather than embedding a model in-process, so run your model behind an OpenAI-compatible server instead.
NOVAMEM_EMBEDDINGS_ENDPOINTstringBase URL of the embeddings API, for example https://api.openai.com/v1. Required when NOVAMEM_EMBEDDINGS_PROVIDER=openai-compatible.
NOVAMEM_EMBEDDINGS_MODELstringModel id, for example text-embedding-3-small or nomic-embed-text. Changing it on an existing deployment invalidates every stored vector; if the dimension is unchanged this fails silently, so re-embed after a swap. Required when NOVAMEM_EMBEDDINGS_PROVIDER=openai-compatible.
NOVAMEM_EMBEDDINGS_API_KEYstringBearer credential for the embeddings endpoint. Secret — keep it out of a ConfigMap and out of a committed .env.
NOVAMEM_EMBEDDINGS_DIMinteger >= 0384Vector dimension the model produces. Must match NOVAMEM_COLD_VECTOR_SIZE.
NOVAMEM_EMBEDDINGS_TIMEOUT_MSinteger >= 030000Per-request timeout. The query is embedded before the per-tier degradation fan-out, so an unbounded hang here stalls every search.
NOVAMEM_EMBEDDINGS_QUERY_PREFIXstring (unset differs from empty)inferred from the model idPrefix applied when embedding a search query. Left unset it is inferred (e5-* gets query: , bge-*-en gets Represent this sentence for searching relevant passages: ); set it to the empty string to suppress the inference. The e5 and bge families lose much of their accuracy when both sides are embedded identically.
NOVAMEM_EMBEDDINGS_DOCUMENT_PREFIXstring (unset differs from empty)inferred from the model idPrefix applied when embedding stored content (e5-* gets passage: ). Set to the empty string to suppress the inference.
NOVAMEM_EMBEDDINGS_RECONCILE_INTERVAL_MSinteger >= 060000How often the reconciler drains entries whose vector is missing. memory_entries.embedded_at IS NULL is the queue, so nothing is lost while the embedder is down and the backlog drains itself when it returns.
NOVAMEM_EMBEDDINGS_RECONCILE_BATCHinteger >= 0400Entries embedded per reconciler tick. A failed batch is simply retried next tick; there is no attempt ceiling, which is what lets it survive a multi-day outage.

Memory engine

VariableTypeDefaultDescription
NOVAMEM_SEARCH_MIN_VECTOR_SCOREnumber in [0,1]0.25Absolute cosine floor for candidates proposed only by the vector tier. Cosine search always returns a nearest neighbour, so without a floor an unrelated store still yields confident-looking hits. Candidates corroborated by a keyword or graph signal are exempt. 0 disables it.
NOVAMEM_GRAPH_LINK_FANOUTinteger >= 03How many co_occurs graph edges each write links to its nearest vector neighbours. Enrichment runs off the write path and is reconciled in the background, so a slow vector tier delays edges rather than writes. 0 disables graph enrichment entirely.
NOVAMEM_DECAY_INTERVAL_MSinteger >= 021600000How often the synaptic-decay sweep runs. 0 disables the sweep.
NOVAMEM_DECAY_DAYSnumber > 07Base half-life in days. Effective lifespan grows with use: effectiveDays = NOVAMEM_DECAY_DAYS x log2(hits + 1).
NOVAMEM_PERSONAL_TERMScomma-separated listDeployment-specific vocabulary — operator name, product names, project slugs — that the worthiness scorer treats as high-relevance.

Quotas and limits

VariableTypeDefaultDescription
NOVAMEM_MAX_CONTENT_CHARSinteger >= 04000Reject writes longer than this. Past the embedding model's context window the tail is silently dropped by the tokenizer, leaving a memory keyword search finds and vector search cannot. 0 disables the limit.
NOVAMEM_QUOTA_MAX_ENTRIESinteger >= 00Per-user cap on stored entries. 0 means unlimited; quotas are opt-in.
NOVAMEM_QUOTA_WRITES_PER_MINUTEinteger >= 00Per-user write rate cap. 0 means unlimited.
NOVAMEM_RATE_LIMIT_PER_MINUTEinteger >= 0600Per-IP request cap. The counter lives in Postgres and is shared across replicas, so the budget is the deployment's rather than each pod's; it falls back to a per-process counter only when the warm store is unreachable. 0 disables the limiter. /health, /live and /ready are never limited.

LLM subsystems

VariableTypeDefaultDescription
NOVAMEM_RERANK_ENABLEDboolean (1/true/yes/on)offEnables the cross-encoder rerank stage, which callers then opt into per request.
NOVAMEM_RERANK_ENDPOINTstringFull URL of the rerank endpoint. Required when NOVAMEM_RERANK_ENABLED is on.
NOVAMEM_RERANK_MODELstringRerank model id. Required when NOVAMEM_RERANK_ENABLED is on.
NOVAMEM_RERANK_API_KEYstringBearer credential for the rerank endpoint. Secret — keep it out of a ConfigMap and out of a committed .env.
NOVAMEM_RERANK_POOL_MULTIPLIERinteger >= 04How many times the requested result count is fetched before reranking. A larger pool gives the reranker more to work with and costs more latency.
NOVAMEM_RERANK_TIMEOUT_MSinteger >= 05000Per-request rerank timeout. On timeout the fusion ranking stands.
NOVAMEM_EXTRACTION_ENABLEDboolean (any non-empty value is true, including "false")offEnables write-time LLM fact extraction, which runs off the write path. The facts_pending_at marker is the durable debt, so a failed extraction is retried rather than lost.
NOVAMEM_EXTRACTION_ENDPOINTstringOpenAI-compatible base URL for extraction. Required when NOVAMEM_EXTRACTION_ENABLED is on.
NOVAMEM_EXTRACTION_MODELstringExtraction model id. Required when NOVAMEM_EXTRACTION_ENABLED is on.
NOVAMEM_EXTRACTION_API_KEYstringBearer credential for the extraction endpoint. Secret — keep it out of a ConfigMap and out of a committed .env.
NOVAMEM_EXTRACTION_MAX_FACTSinteger > 08Upper bound on facts extracted from one memory.
NOVAMEM_EXTRACTION_TIMEOUT_MSinteger > 0120000Per-request extraction timeout. Deliberately generous: a short timeout aborted generations queued behind a busy vLLM and re-queued them forever, and the durable pending marker makes patience free.
NOVAMEM_EXTRACTION_MAX_CONCURRENTinteger > 012How many extractions may be in flight at once. Bounds the load a burst of writes puts on the model server.
NOVAMEM_QUERY_DECOMP_ENABLEDboolean (any non-empty value is true, including "false")offEnables query decomposition, which callers opt into per request with decompose.
NOVAMEM_QUERY_DECOMP_ENDPOINTstringOpenAI-compatible base URL for decomposition. Required when NOVAMEM_QUERY_DECOMP_ENABLED is on.
NOVAMEM_QUERY_DECOMP_MODELstringDecomposition model id. Required when NOVAMEM_QUERY_DECOMP_ENABLED is on.
NOVAMEM_QUERY_DECOMP_API_KEYstringBearer credential for the decomposition endpoint. Secret — keep it out of a ConfigMap and out of a committed .env.
NOVAMEM_QUERY_DECOMP_MAX_SUBQUERIESinteger > 03How many sub-queries one query may be split into. Must be between 1 and 5.
NOVAMEM_QUERY_DECOMP_COHERENCE_RERANKboolean (any non-empty value is true, including "false")onReranks the merged sub-query results for coherence with the original query. Note the coerced-boolean parsing: setting this to false leaves it on, because any non-empty value is true — unset it to turn it off.
NOVAMEM_QUERY_DECOMP_TIMEOUT_MSinteger > 08000Per-request decomposition timeout. On timeout the original query is searched undecomposed.
NOVAMEM_OBSERVER_ENABLEDboolean (any non-empty value is true, including "false")offEnables the Observer/Reflector loop behind /v1/observe and /v1/context-prefix.
NOVAMEM_OBSERVER_ENDPOINTstringOpenAI-compatible base URL for the observer. Required when NOVAMEM_OBSERVER_ENABLED is on.
NOVAMEM_OBSERVER_MODELstringObserver model id. Required when NOVAMEM_OBSERVER_ENABLED is on.
NOVAMEM_OBSERVER_API_KEYstringBearer credential for the observer endpoint. Secret — keep it out of a ConfigMap and out of a committed .env.
NOVAMEM_OBSERVER_OBSERVE_THRESHOLDinteger > 010How many logged observations trigger an observation pass.
NOVAMEM_OBSERVER_REFLECT_THRESHOLDinteger > 050How many logged observations trigger the heavier reflection pass.
NOVAMEM_OBSERVER_TIMEOUT_MSinteger > 030000Per-request observer timeout.

Logging and diagnostics

VariableTypeDefaultDescription
LOG_LEVELstringinfoLog level: debug, info, warn or error.
NOVAMEM_PPROF_ADDRstringWhen set — 127.0.0.1:6060, say — serves Go net/http/pprof on its own listener. A separate socket rather than an API route, so profiling stays reachable in every auth mode and never rides an exposed port by accident.

Deprecated

Still read, so an existing deployment keeps working. Move to the supported spelling — these are removed on the next major.

VariableUse insteadNotes
NOVAMEM_DECAY_DEFAULT_EFFECTIVE_DAYSNOVAMEM_DECAY_DAYSA Go-only spelling of the decay half-life that some deployments picked up. Still read, but NOVAMEM_DECAY_DAYS wins when both are set.

Notes

Changing the embedding model

On start-up the server records the embedding model id that produced the stored vectors. If it changes between runs, it logs a loud error: vectors from two models live in incompatible spaces, so existing memories silently stop being findable. A dimension change at least errors on write; a same-dimension swap (384 → 384, the common case) fails completely silently. Re-embed after a deliberate swap — all content lives in Postgres, so re-embedding is total and safe.

Asymmetric retrieval models

The e5 and bge families are trained with different prefixes on the query and document sides and lose a large chunk of their accuracy when both sides are embedded identically. novamem embeds each side separately and infers the right prefixes from the model id, so these models work correctly out of the box. Set NOVAMEM_EMBEDDINGS_QUERY_PREFIX / NOVAMEM_EMBEDDINGS_DOCUMENT_PREFIX to override the inference, or to the empty string to suppress it.

The coerced booleans

NOVAMEM_EXTRACTION_ENABLED, NOVAMEM_QUERY_DECOMP_ENABLED, NOVAMEM_OBSERVER_ENABLED and NOVAMEM_QUERY_DECOMP_COHERENCE_RERANK are parsed as JS truthiness over the raw string, which means =false turns them on. Any non-empty value is true. To disable one, leave it unset or set it to the empty string.

This is a quirk inherited from the TypeScript server's z.coerce.boolean() and kept deliberately: the config surface is a frozen contract, so a deployment that relied on the old behaviour keeps working. Every other boolean on this page (NOVAMEM_RERANK_ENABLED, NOVAMEM_INSECURE_COOKIES) uses the ordinary 1/true/yes/on spellings.

Variables that are not server config

This page lists what the server reads, so a few names you will meet elsewhere are deliberately absent:

  • POSTGRES_PASSWORD is consumed by the Postgres container and substituted into NOVAMEM_WARM_URL — see the Docker Compose and Kubernetes guides.
  • The client tools read their own: novamem-mcp takes NOVAMEM_TOKEN, and novamem-init also takes NOVAMEM_PASSWORD and NOVAMEM_MCP_BIN.
  • NOVAMEM_URL is the conformance suite's target, not a server setting.

NOVAMEM_BASE_URL is the one name that means something on both sides: to the server it is the origin it advertises, and to the client tools it is the server to connect to. Pointing them at each other is the intent.

No OpenTelemetry

The Go server emits no OTLP traces, and reads no OTEL_* variables — setting OTEL_EXPORTER_OTLP_ENDPOINT does nothing. This page listed them until the reference was generated from the loader, which is exactly the kind of claim a hand-written table can make and a generated one cannot. Prometheus metrics are available at /v1/admin/metrics/prom (see NOVAMEM_ADMIN_DASHBOARD); OTLP export is tracked in #277.

See also