-
Notifications
You must be signed in to change notification settings - Fork 0
Configuration Reference
Every RECALL_* environment variable, grouped by what it protects.
Default values are not listed here. They live in
.env.example, which is generated alongside the code and cannot drift from it. This page covers what each variable is for, when you would change it, and what breaks if you get it wrong — which is the part a config dump cannot tell you.
Precedence: real environment → .env file → built-in default. The .env loader never overrides
a variable that is already set.
The PostgreSQL connection string. Everything else assumes this points somewhere real.
When you'd change it: always, for anything but the local dev container.
If you get it wrong: the repo publishes default credentials in its docker-compose.yml, and
using them against a non-local host makes your memory corpus readable by anyone who can reach
the port. The MCP server therefore refuses to boot in that situation rather than warning — see
RECALL_ALLOW_INSECURE_DSN. The CLI prints a loud stderr warning instead, because a CLI has no
listener to protect.
Accepts the risk above deliberately.
When you'd change it: you genuinely intend to run with published credentials — a throwaway environment, a demo on a trusted network.
If you get it wrong: you have re-enabled exactly the failure the refusal exists to prevent, and nothing will tell you again.
Selects the connection mode, which is a bigger switch than it looks.
Unset means one long-lived connection: correct for a CLI or any single-threaded caller, and the mode the store's reconnect semantics are built around. Set to an integer means a connection pool: what a server needs, so concurrent callers actually proceed concurrently.
When you'd change it: any server process. Requires the pool extra.
If you get it wrong: sharing a single connection across threads serialises them — the MCP server previously served exactly one request at a time for this reason. In the other direction, a pool starts a background maintenance thread that a one-shot CLI invocation should not pay for or have to shut down.
Bounds every statement server-side.
When you'd change it: raise it if legitimate queries are being cancelled; lower it if you need tighter tail latency.
If you get it wrong: without a bound, a single runaway query occupies a connection until the process dies, with nothing able to cancel it. That is the difference between a slow request and an exhausted pool. Schema DDL is the one documented exception — it lifts the bound, because an HNSW build legitimately outlasts any sane query timeout, and restores it afterwards.
How long schema DDL may wait for a lock before giving up.
Read this carefully: it is not a bound on the work. An index build is deliberately unbounded. It bounds only queueing behind another transaction.
When you'd change it: set it to wait forever if you have a deployment that legitimately contends on this and you would rather block than retry.
If you get it wrong: with an unbounded wait, a concurrent transaction turns store startup into a silent stall — which presents as a hang, not an error. The DDL is idempotent and retried on the next store open, so failing fast loses nothing and is diagnosable where a stall is not.
Which embedder to use — the local ONNX model or the fully-offline hashing embedder.
When you'd change it: the hashing embedder for tests, CI and offline work; it is deterministic and needs no model download. Never for retrieval quality — it is weak on purpose.
If you get it wrong: the embedder's name is part of both the embedding cache key and the
calibration file, so switching cannot silently reuse the previous model's vectors or its
threshold. What you will see is a full re-embed on the next index and an uncalibrated fallback
until you re-run calibrate. Both are correct; neither is silent.
Path to the per-embedder calibration file written by recall calibrate.
When you'd change it: you keep calibrations per environment or per corpus.
If you get it wrong: a missing, unreadable, malformed, out-of-range, or
wrong-embedder calibration is ignored, with an uncalibrated fallback and calibrated=False
flagged in every result. That defensiveness is deliberate — a corrupt file must never be able to
silently disable abstention, and a threshold fitted in another model's cosine regime must never
be applied. → The-Trust-Layer
Two different jobs here, and conflating them is the usual mistake: per-request caps bound one call; budgets bound the aggregate per tenant, so a client staying politely under the per-request cap cannot simply issue it in a loop.
Bounds where the MCP recall_index tool may read from.
When you'd change it: narrow it to exactly the corpus directory you intend to expose.
If you get it wrong: a client-callable indexer with no root confinement can read anything the server process can. The confinement survives symlinks.
Per-request caps on candidate file count and candidate bytes. Checked before anything is embedded, so an oversized request costs nothing.
When you'd change it: larger corpora indexed in one call; or tighter, if your embedder is a paid API.
If you get it wrong: a client-callable indexer with no cap is unbounded spend on a cloud embedder.
The aggregate indexing budget, per tenant, per hour. This is the load-bearing one: request count is a poor proxy for spend when one call can carry twenty megabytes and the next two hundred bytes.
When you'd change it: matching it to what you are willing to pay.
RECALL_INDEX_MAX_BYTES. If the hourly budget is smaller than the
per-request cap, requests sized between the two can never succeed — they pass the per-request
check and are then refused by a budget they cannot fit into.
The fraction of a root's indexed sources that a re-index may delete before it refuses and deletes nothing.
When you'd change it: rarely. Lower it if your corpus should never shrink much.
If you get it wrong: set too high, a missing corpus — wrong path, unmounted volume,
half-finished checkout — becomes indistinguishable from a deleted one, and a single bad run
empties your index. The guard also applies above a small floor of indexed sources, so tiny
corpora are not constantly tripping it. Override per-run with --allow-prune once you have
confirmed the files really are gone.
pgvector search parameters applied to the source-filtered query path only.
Why they exist: at pgvector's defaults, the filtered path measured poor recall with most queries truncated — a filtered search can exhaust its candidate list before finding enough matching rows. Widening the search and relaxing iterative scan fixes it, at a latency cost.
When you'd change them: tuning the recall/latency trade-off on filtered queries.
Stated honestly: the unfiltered path still runs at pgvector's defaults, where it measured well — but every query now also carries a tenant predicate, and as of the README's last revision that combination had not been measured on a multi-tenant table. Background: issue #11. Check the README for the current status.
The tenant namespace. A store is bound to one tenant for its lifetime — tenancy is a property of which store you hold, not an argument you pass to a query.
When you'd change it: operating on a different tenant's memory. Note this applies to stdio
only; over HTTP the token carries the tenant, and this variable is ignored.
If you get it wrong: the row-level-security policy means a wrong tenant returns nothing rather than someone else's data — provided you connect as an unprivileged role. → Tenancy-and-Auth
Full treatment: Tenancy-and-Auth and
docs/AUTH.md.
stdio (a private pipe to one client) or an HTTP transport (a network listener).
If you get it wrong: the two have deliberately different security postures. stdio needs no
authentication because there is no remote caller — the pipe is the boundary. An HTTP transport
opens a socket, and starting one without tokens refuses to boot.
The only source of bearer tokens.
Note what is absent: there is no environment variable that accepts a raw token, and the
omission is the point. Environment variables leak through /proc/<pid>/environ, ps e,
container inspection APIs, crash dumps, and every child process the server spawns. A file is what
Kubernetes and Docker mount for secrets anyway, and unlike an env var it can be
permission-checked — which the server does, warning if the file is world-readable.
The file may hold plaintext tokens or their SHA-256 digests, so provisioning access never requires writing a live credential to disk in recoverable form.
Identity metadata advertised to MCP clients for the resource-server auth flow.
Per-tenant call rates, split by the risk each tool class carries — searching and reading stats, indexing, and erasure.
Why per tenant and not per principal: two tokens issued to the same tenant are the same blast radius and the same bill. Letting a tenant multiply its budget by minting another token would make the quota advisory.
Each takes a number or the literal off. A malformed value, a non-finite one, or one too
small to yield a non-zero rate falls back to its default rather than being read as
"unlimited". A typo must not silently remove a cap.
Level, and text or JSON output.
Important: these configure the CLI and the MCP server. The library itself never attaches
logging handlers — that is the host application's job, and a library that calls basicConfig
hijacks its host's logging configuration. If you embed recall in your own service, configure
logging yourself; these variables will not do it for you.
Bind address for the HTTP transports.
If you change one thing before going to production, make it RECALL_DSN — specifically, the
role it connects as. Row-level security is bypassed by a superuser or a BYPASSRLS role, which
means the second isolation layer is decoration unless you connect as an unprivileged one.
store.check_rls_effective() tells you which you have. → Tenancy-and-Auth
If you change two, add RECALL_POOL_SIZE for any server process.
This wiki explains design and intent. Measured figures, deployment defaults and CLI flag values
live in the repository, versioned with the code that produced them:
FINDINGS ·
RESULTS ·
MIGRATIONS ·
.env.example · --help.
If a page here disagrees with the repo, the repo is right.
Concepts
Using it
- Installation-and-Setup
- Embedders-and-Rerankers
- Configuration-Reference
- CLI-Reference
- Python-API-and-MCP
- Tenancy-and-Auth
Evidence
Contributing
In the repo