Configuration
How to bind a single deployment to stores, model seats, budgets, and optional exporters. This page is operator-facing and agent-scannable. Run-through: Getting started · Deployment.
TL;DR
| Need | Do |
|---|---|
| Local full stack | Copy .env.example → .env, set OpenRouter key, docker compose up |
| Reproducible models | Pin explicit model IDs — never a rotating free router |
| Client only | pip install rememberstack + REMEMBERSTACK_API_URL |
| Ops / budgets / DLQ | rememberstack[server] + REMEMBERSTACK_DATABASE_URL |
| Secrets | Never commit real keys; local examples are disposable |
One deployment id = one trust domain. Changing REMEMBERSTACK_SELFHOST_DEPLOYMENT_ID creates a different deployment identity.
Install surfaces
| Extra / artifact | Role |
|---|---|
rememberstack (base) | Typed SDK, remote CLI/MCP, lineage-aware remote ingest |
rememberstack[server] | API, workers, PostgreSQL 19 adapters, local ops CLI |
rememberstack[connectors-watched-directory] | Names the watched-dir connector surface (stdlib) |
rememberstack[k] | Names the Plane K install surface |
rememberstack[benchmark] | LoCoMo / scorer deps |
rememberstack[observability] | Sentry-protocol + Langfuse SDKs (still opt-in by env) |
| GHCR image / Compose | Fresh single-deployment self-host |
Environment map (self-host)
Authoritative template: repository .env.example.
Identity and API
| Variable | Purpose |
|---|---|
REMEMBERSTACK_SELFHOST_DEPLOYMENT_ID | Stable UUID for this deployment |
REMEMBERSTACK_SELFHOST_DEPLOYMENT_SLUG | Short slug (also default Sentry environment) |
REMEMBERSTACK_SELFHOST_DEPLOYMENT_NAME | Human label |
REMEMBERSTACK_SELFHOST_API_PORT | API listen port (Compose) |
REMEMBERSTACK_SELFHOST_API_BEARER_BIND | Optional {issued_deployment_uuid}:{sha256hex} of the API Bearer secret. When set, /operations and other memory routes require that Bearer. GET /healthz stays unauthenticated |
REMEMBERSTACK_SELFHOST_API_BEARER_TOKEN | Optional plaintext Bearer for local self-host; hashed at startup and bound to SELFHOST_DEPLOYMENT_ID. Must match BIND when both are set |
REMEMBERSTACK_SELFHOST_API_SIGNING_KEYS | Optional JWKS (Ed25519 public keys only) this deployment verifies signed credentials against. Set beside or instead of API_BEARER_BIND: a caller may present a shared secret, a signed credential, or either. Refused at startup if it carries private key material, a non-Ed25519 key, or a key that half loads |
REMEMBERSTACK_SELFHOST_API_REVOKED_CREDENTIAL_IDS | Optional comma-separated credential ids to refuse. A signature is verified by arithmetic, so its issuer cannot withdraw one — this is how a revoked but unexpired credential is refused. Meaningless without API_SIGNING_KEYS, and rejected if set alone |
REMEMBERSTACK_SELFHOST_REQUIRE_API_AUTH | When true, the API process refuses to start unless it has a perimeter — API_BEARER_BIND, API_SIGNING_KEYS, or both. Default false (open quickstart) |
REMEMBERSTACK_SELFHOST_BROWSER_ORIGINS | Optional comma-separated https:// origins allowed to call this deployment from a browser. Empty by default, which advertises no CORS at all. Each entry must be an exact scheme-and-host origin — no wildcard, no path, no http:// |
REMEMBERSTACK_API_URL | Client target; defaults to http://127.0.0.1:8000 — must match if you change the port |
REMEMBERSTACK_TOKEN_HOST | Device-grant host for remember login (required unless --token-host is passed). Never derived from the query API URL |
REMEMBERSTACK_CONFIG_DIR | Optional override for the CLI credential directory. Holds credentials.json and, while a replaced credential is still awaiting revocation, pending-revocation.json — both 0600 inside a 0700 directory |
REMEMBERSTACK_COST_EXPORT_BIND | Optional second listen address for HTTP cost export (127.0.0.1:8001, [::1]:8001, or unix:/path). Unset = no HTTP export |
REMEMBERSTACK_COST_EXPORT_TOKEN | Bearer for the export listener. Required and ≥32 bytes when the bind is set |
Calling a deployment from a browser
A browser refuses a cross-origin request before it is sent unless the server says the origin is allowed. The credential is never examined and the perimeter never runs, so the failure looks like the deployment being down — which is why this is configuration rather than something that can be worked around in a client.
REMEMBERSTACK_SELFHOST_BROWSER_ORIGINS names the origins permitted to do
that. It is empty by default: a deployment nobody told about an app should not
advertise anything, and a permissive default would hand every website on the
internet the ability to make authenticated requests from a visitor's browser.
Each entry must be an origin exactly as a browser sends it:
https://app.example.com, optionally with a port, and nothing else — lowercase
scheme and host, no path, no query, no userinfo, no wildcard.
The reason is worth knowing. The match is a byte comparison against the
request's Origin header, so a value that is not exactly what a browser sends
can never match — it does not widen the perimeter, it silently closes it. An
operator who writes https://*.example.com and watches the deployment start
cleanly believes they have granted a subdomain tree; they have granted
nothing, and will go looking for the fault in their app.
Validation is deliberately small: scheme, structure, case and port, and no finer judgement of hostname syntax than that. A rule strict enough to catch every impossible name also refuses real ones — underscores, trailing dots and IDNA2008 labels are all things browsers send and naive checks reject. DNS fails loudly and specifically for a name that does not exist, which is a better error than a startup refusal for a host that does. The origins actually installed are logged when the API starts, so a typo is something you can see rather than infer.
Credentialed CORS is disabled (allow_credentials is off). Be precise about
what that does: it stops the response being shared with a page that made the
request in credentials: "include" mode. It does not block an ordinary
bearer call — a request carrying Authorization is preflighted, and that
preflight succeeds because the header is on the allowlist. What is refused is
a page trying to have cookies or client certificates count as authentication
across the origin boundary. It does not reach into the browser and stop a page
attaching cookies to a simple cross-origin request in the first place; that
request may still be sent, and only its response is withheld.
So the honest statement is that no credentialed cross-origin call can succeed
here, not that cookies can never leave the browser. The credential this
surface expects travels in Authorization, and same-origin traffic is
unaffected by any of it.
Only GET and POST are advertised, with Authorization and Content-Type.
Sending Authorization makes the request preflighted, and that preflight
succeeds because the header is on the allowlist — it is the browser's
credentials: "include" mode, not the presence of a bearer header, that would
need credentialed CORS.
Preflight responses are cached for ten minutes, which is worth knowing when you remove an origin: a browser that has already been told "yes" may keep that answer for up to that long, so revoking the credential is what ends access immediately, not editing this list.
The CORS layer is installed outermost, so refusals raised by other layers — an oversize body, an exhausted spend ceiling — reach the browser as the errors they are rather than as opaque network failures.
A non-default port is part of an origin and may be included:
https://app.example.com:8443 (1–65535, but not 443 — a browser omits the
default port from Origin, so writing it out matches nothing). https://localhost and
https://localhost:3000 are accepted, so a self-hoster can develop against a
local https listener.
Several origins are comma-separated, and the whole list is validated together — a stray comma is a refusal, not a silently skipped entry, because a list that starts with fewer origins than you wrote hides the missing one until somebody's browser is turned away.
REMEMBERSTACK_SELFHOST_BROWSER_ORIGINS=https://app.remember.devCompose passes this through, so it can be set in .env beside the other
deployment settings.
Stores (Compose local defaults)
| Variable | Purpose |
|---|---|
REMEMBERSTACK_POSTGRES_* | Postgres user/password/db for the stack |
REMEMBERSTACK_MINIO_* | MinIO access keys for object storage |
REMEMBERSTACK_DATABASE_URL | Used by local server-extra CLI tools (budget, ops) when connecting to the spine |
Replace every local-only secret before any non-isolated use.
Ingest admission
| Variable | Purpose |
|---|---|
REMEMBERSTACK_SELFHOST_INGEST_BODY_MAX_BYTES | Optional POST /ingest request-body ceiling in bytes, enforced before the body is buffered (413 body_too_large; requests without a Content-Length get 411). Unset (default) = no engine-imposed limit — a managed host publishes and sets its own bound |
Managed deployments also configure content-free billing receipts. These values
are fleet-authored as one all-or-nothing set; ordinary self-host installations
leave all of them unset and do not start the Compose managed profile.
| Variable | Purpose |
|---|---|
REMEMBERSTACK_SELFHOST_METER_INGEST_URL | HTTPS root of the managed control-plane receipt service |
REMEMBERSTACK_SELFHOST_METER_INGEST_TOKEN | Deployment-scoped umc_mi_ producer credential; never a customer API token |
REMEMBERSTACK_SELFHOST_METER_IDENTITY_KEY | Deployment-local umc_mik_ HMAC key for stable opaque lineage/version IDs. It never leaves the engine and must survive token rotation |
REMEMBERSTACK_SELFHOST_METER_ORG_ID | Fleet-bound organisation UUID carried in every receipt |
REMEMBERSTACK_SELFHOST_METER_PROJECT_ID | Fleet-bound Project UUID carried in every receipt |
REMEMBERSTACK_SELFHOST_REQUIRE_METERING | When true, startup fails unless the entire managed receipt configuration is present |
Conversion routes
| Variable | Purpose |
|---|---|
REMEMBERSTACK_SELFHOST_CONVERSION_ROUTES | JSON object mapping input MIME type to a converter adapter name, e.g. {"text/markdown": "passthrough", "text/plain": "passthrough", "text/html": "markitdown", "application/pdf": "mistral_ocr"}. Shipped adapter names: passthrough, markitdown, mistral_ocr. Setting the variable replaces the whole table (defaults are the stock text table); a MIME type without a route dead-letters on convert, and an unknown adapter name refuses startup |
The mistral_ocr route is BYO-key (provider-backed; off unless routed):
| Variable | Purpose |
|---|---|
REMEMBERSTACK_MISTRAL_OCR_API_KEY | Mistral API key. Required when any route names mistral_ocr; composition refuses to start without it |
REMEMBERSTACK_MISTRAL_OCR_MODEL | OCR model (default mistral-ocr-latest). Pin a dated model for reproducible representations — the manifest records whatever model identifier the provider echoes |
REMEMBERSTACK_MISTRAL_OCR_MAX_DOCUMENT_BYTES | Deterministic input ceiling before any provider call (default 50000000) |
REMEMBERSTACK_MISTRAL_OCR_CONFIDENCE_GRANULARITY | word (default) or page; word-level scores are retained in the provider-response interchange asset |
REMEMBERSTACK_MISTRAL_OCR_KEEP_PROVIDER_RESPONSE | Keep the sanitized raw OCR response as an interchange asset (default true) |
REMEMBERSTACK_MISTRAL_OCR_PRICE_USD_PER_1000_PAGES | Metered price per 1000 processed pages recorded in the cost ledger (default 1) |
Every output-affecting mistral_ocr option is folded into the converter version, so changing the model or an option re-converts affected documents as new representations instead of replaying old ones.
Live-graph resources
| Variable | Purpose |
|---|---|
REMEMBERSTACK_SELFHOST_GRAPH_POOL_SIZE | Dedicated PostgreSQL graph-pool connections (default 4) |
REMEMBERSTACK_SELFHOST_GRAPH_POOL_TIMEOUT_S | Maximum wait for a graph-pool connection (default 1 second) |
REMEMBERSTACK_SELFHOST_GRAPH_MAX_CONCURRENCY | Per-process admitted graph expansions (default 2) |
REMEMBERSTACK_SELFHOST_GRAPH_WORK_MEM_KIB | Transaction-local graph work_mem ceiling per memory node (default 16384 KiB) |
Size these four values together with PostgreSQL shared memory and host reserve. Increasing pool or concurrency independently multiplies worst-case memory; the managed service applies a stricter host-level admission calculation.
Interactive retrieval resources
| Variable | Purpose |
|---|---|
REMEMBERSTACK_SELFHOST_RETRIEVAL_POOL_SIZE | Dedicated PostgreSQL P1/fact read connections (default 4) |
REMEMBERSTACK_SELFHOST_RETRIEVAL_POOL_TIMEOUT_S | Maximum admission wait for an interactive retrieval read (default 1 second, also clamped to the operation deadline) |
REMEMBERSTACK_SELFHOST_RETRIEVAL_MAX_CONCURRENCY | Per-process P1/fact reads admitted across the shared retrieval pool (default 4) |
Keep maximum concurrency at or below pool size. fact_context also clamps
every admitted PostgreSQL statement and transaction to its remaining operation
deadline; pool saturation returns a typed boundary rather than using the
worker/write pool.
Provider (OpenRouter)
| Variable | Purpose |
|---|---|
REMEMBERSTACK_OPENROUTER_API_KEY | Required to start the provider adapter and process a corpus |
REMEMBERSTACK_OPENROUTER_MAX_COMPLETION_TOKENS | Combined reasoning+content budget (default 32000 in example) |
REMEMBERSTACK_OPENROUTER_EMBEDDING_PROVIDER | Optional hard pin of embedding host slug |
REMEMBERSTACK_OPENROUTER_EMBEDDING_PROVIDER_ORDER | Preferred ordered shortlist (wins over hard pin) |
REMEMBERSTACK_OPENROUTER_REASONING_EFFORT | Global effort: none…max (unset = model default) |
REMEMBERSTACK_OPENROUTER_REASONING_EFFORT_MAP | JSON map of model-id → effort overrides |
Embedding routing policy notes: design/operations/openrouter-embedding-routing.md in the repo.
Model seats (optional overrides)
Pin explicit IDs for reproducible runs. Unset values fall back to deployment profile defaults.
| Variable | Stage |
|---|---|
REMEMBERSTACK_E2_EXTRACT_MODEL | Claim extraction |
REMEMBERSTACK_E3_NORMALIZE_MODEL | Normalization / adjudication seats as bound by profile |
REMEMBERSTACK_STRUCTURER_MODEL | Only string-anchor structure fallback proposer |
REMEMBERSTACK_SKELETON_CHECK_MODEL | Bounded skeleton sanity judge (default flash-class) |
REMEMBERSTACK_ROLE_MODEL | Title-only section-role classifier |
REMEMBERSTACK_SUMMARY_MODEL | Bottom-up section summaries + root placement reduction |
Structure honesty: routine skeleton construction is deterministic. Bounded summary/role/check calls still run; STRUCTURER is not “the whole document structure model.”
Smoke conversion route is Markdown unless you register more converters on the deployment.
P1 search indexes (D94)
P1 lives in PostgreSQL. Migrations own the pgvector HNSW and
pg_textsearch BM25 indexes, while ordinary ingestion transactions update the
search rows and embedding attestations. There is no separate search-store path,
maintenance worker, or search-index environment namespace to configure.
Spend ceilings (optional)
export REMEMBERSTACK_WORK_BUDGETS='[
{
"deployment_id":"<deployment-uuid>",
"stage":"extract_claims",
"lane":"steady",
"window_seconds":86400,
"ceiling_usd":"10.00"
}
]'- JSON list; omit a route → unlimited
- Use
nulllane for unlaned K/P routes - Exhaustion parks healthy work until the window ends — does not burn handler attempts or invent a failure
- Inspect:
remember budget inspect --deployment <uuid>(needs[server])
Observability (optional, fail-closed off)
Exporters stay disabled until env is non-empty.
| Variable | Effect |
|---|---|
REMEMBERSTACK_SENTRY_DSN | Metadata-only errors to Sentry/GlitchTip/Bugsink |
REMEMBERSTACK_SENTRY_ENVIRONMENT | Defaults to deployment slug |
REMEMBERSTACK_SENTRY_SAMPLE_RATE | Defaults to 1.0 |
LANGFUSE_PUBLIC_KEY + SECRET_KEY + HOST | LoCoMo answer/judge traces only when all three set |
What is not sent: request bodies, local vars, breadcrumbs, PII, prompts/completions, exception message text as free-form dumps. Worker tags stay stage/lane/processing id. Postgres + JSON telemetry remain authoritative for retries.
Conversion and connectors
| Piece | Config posture |
|---|---|
| Markdown smoke | Default Compose conversion route |
| HTML/PDF/media | Bind converter routes per MIME type via REMEMBERSTACK_SELFHOST_CONVERSION_ROUTES (the smoke profile does not auto-route beyond text) |
| Watched directory | Connector extra + connector management API/CLI; credentials stay deployment-side |
| Lineage identity | source_kind + source_ref together on ingest; `versioning_mode=snapshot |
What configuration is not
- Not multi-tenant control-plane config
- Not a hosted SLA or billing product
- Not free-tier model routing for publication benchmarks
- Not secret storage — use your secret manager outside git
Progressive disclosure
| Depth | Page |
|---|---|
| This page | Env + seats + budgets |
| Deployment | Compose lifecycle, projections |
| Troubleshooting | When config fails in practice |
| CLI reference | budget / ops command contracts |
Repo .env.example | Canonical variable list |