---
title: Environment variables
description: Every variable the binary reads, and what it does when one is unset.
---

## 1. How they are read

The whole environment surface, in one place, so `memhtml manifest` can describe it and a reader
does not have to grep for `process.env`.

Every variable is read through `effect/Config` rather than `process.env` directly: a missing
required value becomes a typed failure with the variable's name in it, and a default is
declared next to the name it defaults for.

## 2. The variables

The middle column is the value the binary falls back to when the variable is unset. A row with no default changes behavior by its absence, and its description says how.

| Variable | When unset | Description |
| --- | --- | --- |
| `MEMHTML_ROOT` | `~/memhtml` | The memory repo's root: a git repository holding the corpus and `.memhtml/`. |
| `MEMHTML_REFUSE_ENV_ROOT` | *no default* | Set to any value but `0`, `false`, `no`, or `off` (absent or blank is off; case-insensitive) makes `memhtml` take its repo from `--repo` alone: `MEMHTML_ROOT` and the `~/memhtml` default stop being doors, and a call that opens a repo without `--repo` is refused with ERR\_REPO\_REQUIRED at exit 2 before `memhtml` opens anything. For CI, for a test suite calling the CLI in-process, and for an agent runtime that exports `MEMHTML_ROOT` to every subprocess it starts. Commands that never open a repo (`manifest`, `help`, `agents-doc`, `eval discriminate`) are unaffected. It governs the roots `memhtml` resolves from its own environment: a caller that hands the in-process `run()` a layer it built states that layer's root itself. Read by `memhtml` only: `memhtml-mcp` takes its root from `MEMHTML_ROOT`, which `memhtml serve mcp --repo` sets for the child explicitly. |
| `MEMHTML_TRACE_ROOT` | `~/.claude` | Where `memhtml trace index` reads Claude Code transcripts from. Read-only; never written. |
| `MEMHTML_AWS_REGION` | `us-east-1` | The Bedrock region for embeddings and the sleep cycle's model-calling phases. |
| `AWS_BEARER_TOKEN_BEDROCK` | *no default* | Bedrock bearer token, read by the AWS SDK itself. Absent means the default credential chain; retrieval then degrades to the lexical floor rather than failing. |
| `MEMHTML_LLM_BASE_URL` | *no default* | An OpenAI- and Anthropic-compatible LLM proxy's origin, e.g. `http://127.0.0.1:4000` for an agentgateway listener. Set, every model call leaves through it instead of going to Bedrock directly: the Anthropic sleep models and the consolidator agent on `/v1/messages`, the OpenAI sleep model and the entity extractor on `/v1/chat/completions`, embeddings on `/v1/embeddings`. Absent means Bedrock directly, under `MEMHTML_AWS_REGION` and the Bedrock credential. A set-but-malformed value fails at startup naming this variable rather than falling back to the direct path. |
| `MEMHTML_LLM_API_KEY` | *no default* | A bearer token for the LLM proxy, sent as `Authorization: Bearer <key>`. Read only when `MEMHTML_LLM_BASE_URL` is set; absent means the proxy takes no credential. |
| `MEMHTML_LLM_MODEL_PREFIX` | `bedrock/` | The prefix in front of every Bedrock model id a proxied request carries, so `global.anthropic.claude-opus-5` is asked for as `bedrock/global.anthropic.claude-opus-5`: the LiteLLM convention, which a LiteLLM proxy routes with one `bedrock/*` entry and which keeps the id after the slash exactly what Bedrock wants. Set it to `none` for a proxy that takes bare Bedrock ids. Read only when `MEMHTML_LLM_BASE_URL` is set. |
| `MEMHTML_LLM_MODEL_MAP` | *no default* | `from=to` pairs, comma-separated, naming single models to the proxy by exact id when the prefix rule does not fit: `cohere.embed-v4:0=cohere-embed-v4`. A mapped id is sent verbatim, without the prefix; every other id follows `MEMHTML_LLM_MODEL_PREFIX`. Read only when `MEMHTML_LLM_BASE_URL` is set. |
| `MEMHTML_OPENAI_PROMPT_CACHE` | `off` | How the OpenAI sleep model's chat-completions requests ask Bedrock to treat prompt caching. `off` sends `prompt_cache_options: {mode: "explicit"}` with no breakpoints, which Bedrock documents as no prompt caching and no cache-write charge; `implicit` sends no caching field and leaves the endpoint's default in place. Off by default because Bedrock's implicit mode for GPT-5.6 writes the whole prompt to the cache at 1.25x the input rate on every call that clears the 1,024-token minimum, and the sleep prompts share no prefix long enough to ever be read back. Set `implicit` for an OpenAI-compatible endpoint that rejects the Bedrock-only field. Read on the direct path and the proxy path alike. Any other value fails at startup naming this variable. |
| `MEMHTML_EMBED` | `on` | `off` disables the embedder entirely. An explicit opt-out, distinct from a missing credential: a missing credential degrades one search at call time, `off` degrades every search, and an operator reading this manifest needs those to be different states. |
| `MEMHTML_VECTOR_COVERAGE_FLOOR` | `0.95` | The share of indexed chunks that must carry a vector in the configured space, `0` to `1`, before the vector arm is trusted. Below it `search` and `recall` drop the vector arm and report `degraded: true` with `vectorCoverage`, `doctor` reports `vectorCoverageLow` and `healthy: false`, and a sleep run warns. A sparse plane ranks the few embedded files above every exact match, so it is treated as absent rather than run. Sleep also refuses below a fixed hard floor of `0.5`, which this variable does not move: a value under `0.5` keeps search and doctor accepting a plane sleep still refuses. Remedy: `memhtml index embed`, or `memhtml index rebuild --embed`. |
| `MEMHTML_LLM` | `on` | `off` makes every model-calling sleep phase report `no model bound` and stay `ok`, so a credential-free run is honest rather than red. `entity-resolution` still runs its deterministic normalization and character-overlap passes; the others do nothing. |
| `MEMHTML_EXTRACT_ENTITIES` | `on` | `off` removes the one `global.openai.gpt-5.6-terra` call per write batch that extracts `memhtml-entity` metas the ops did not declare; `MEMHTML_LLM=off` removes it too. On by default, like MEMHTML\_EMBED. It changes what a write STORES: extracted entities land in the files as if authored, and the write itself never waits on or fails with the model. A failed extraction is a logged warning and an unextracted batch. |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | *no default* | An OTLP collector's base URL, e.g. `http://localhost:4318`. Set, the ~30 `Effect.withSpan` annotations already in the code (retrieval, embeddings, model calls, indexing, the sleep cycle, store writes, `db.*`, `git.*`) export as traces to `<endpoint>/v1/traces`, batched, flushed on exit. Unset, nothing is loaded and behavior is byte-identical. Export can never fail a command: a down collector is one stderr warning and a command that proceeds untraced. |
| `OTEL_SERVICE_NAME` | *no default* | Overrides the `service.name` resource attribute on exported traces. Absent, each process names itself: `memhtml-cli` or `memhtml-mcp`. Read only when `OTEL_EXPORTER_OTLP_ENDPOINT` is set. |
| `MEMHTML_MCP_BIN` | *no default* | An explicit path to the `memhtml-mcp` entry point, read only by the `memhtml serve mcp` supervisor. Absent means the sibling-path default. The two apps ship as one build, so `apps/cli/dist/serve.js` finds `apps/mcp/dist/bin.js` two directories over. An operator sets it for a split deployment that does not keep the apps side by side; it locates the server rather than configuring the store, so it changes no retrieval behavior. |

## 3. Provenance

A loader generates this page from `apps/cli/src/config.ts` while the site builds, so no file in the repository holds it: `CONFIG_VARS` is what `memhtml manifest` publishes, so a variable this page omits is one the binary does not read. Change the registry and this page changes with it.